1use rich_rust::prelude::*;
4
5#[derive(Debug)]
7pub struct FastMcpTheme {
8 pub primary: Color, pub secondary: Color, pub accent: Color, pub success: Color, pub warning: Color, pub error: Color, pub info: Color, pub text: Color, pub text_muted: Color, pub text_dim: Color, pub border: Color, pub background: Color, pub header_style: Style,
28 pub subheader_style: Style,
29 pub label_style: Style,
30 pub value_style: Style,
31 pub key_style: Style,
32 pub muted_style: Style,
33 pub success_style: Style,
34 pub warning_style: Style,
35 pub error_style: Style,
36 pub info_style: Style,
37 pub border_style: Style,
38}
39
40impl Default for FastMcpTheme {
41 fn default() -> Self {
42 Self {
43 primary: Color::from_rgb(0, 212, 255),
45 secondary: Color::from_rgb(168, 85, 247),
46 accent: Color::from_rgb(34, 197, 94),
47 success: Color::from_rgb(34, 197, 94),
48 warning: Color::from_rgb(245, 158, 11),
49 error: Color::from_rgb(239, 68, 68),
50 info: Color::from_rgb(59, 130, 246),
51 text: Color::from_rgb(229, 231, 235),
52 text_muted: Color::from_rgb(156, 163, 175),
53 text_dim: Color::from_rgb(107, 114, 128),
54 border: Color::from_rgb(55, 65, 81),
55 background: Color::from_rgb(31, 41, 55),
56
57 header_style: Style::new().bold().color(Color::from_rgb(0, 212, 255)),
59 subheader_style: Style::new().color(Color::from_rgb(156, 163, 175)),
60 label_style: Style::new().color(Color::from_rgb(107, 114, 128)),
61 value_style: Style::new().color(Color::from_rgb(229, 231, 235)),
62 key_style: Style::new().bold().color(Color::from_rgb(168, 85, 247)),
63 muted_style: Style::new().dim().color(Color::from_rgb(107, 114, 128)),
64 success_style: Style::new().bold().color(Color::from_rgb(34, 197, 94)),
65 warning_style: Style::new().bold().color(Color::from_rgb(245, 158, 11)),
66 error_style: Style::new().bold().color(Color::from_rgb(239, 68, 68)),
67 info_style: Style::new().color(Color::from_rgb(59, 130, 246)),
68 border_style: Style::new().color(Color::from_rgb(55, 65, 81)),
69 }
70 }
71}
72
73pub fn theme() -> &'static FastMcpTheme {
75 static THEME: std::sync::OnceLock<FastMcpTheme> = std::sync::OnceLock::new();
76 THEME.get_or_init(FastMcpTheme::default)
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_theme_default_colors() {
85 let t = FastMcpTheme::default();
86 assert_eq!(t.primary.triplet.map(|tr| tr.red), Some(0));
88 assert_eq!(t.primary.triplet.map(|tr| tr.green), Some(212));
89 assert_eq!(t.primary.triplet.map(|tr| tr.blue), Some(255));
90 }
91
92 #[test]
93 fn test_theme_semantic_colors() {
94 let t = FastMcpTheme::default();
95 assert_eq!(t.success.triplet.map(|tr| tr.green), Some(197));
97 assert_eq!(t.error.triplet.map(|tr| tr.red), Some(239));
99 assert_eq!(t.warning.triplet.map(|tr| tr.red), Some(245));
101 }
102
103 #[test]
104 fn test_theme_global_singleton() {
105 let t1 = theme();
106 let t2 = theme();
107 assert!(std::ptr::eq(t1, t2));
109 }
110
111 #[test]
112 fn test_theme_styles_exist() {
113 let t = FastMcpTheme::default();
114 let _ = t.header_style.clone();
116 let _ = t.error_style.clone();
117 let _ = t.success_style.clone();
118 let _ = t.warning_style.clone();
119 }
120
121 #[test]
126 fn theme_neutral_palette() {
127 let t = FastMcpTheme::default();
128 assert_eq!(t.text.triplet.map(|tr| tr.red), Some(229));
130 assert_eq!(t.text_muted.triplet.map(|tr| tr.red), Some(156));
132 assert_eq!(t.text_dim.triplet.map(|tr| tr.red), Some(107));
134 assert_eq!(t.border.triplet.map(|tr| tr.red), Some(55));
136 assert_eq!(t.background.triplet.map(|tr| tr.red), Some(31));
138 }
139
140 #[test]
141 fn theme_info_and_secondary_colors() {
142 let t = FastMcpTheme::default();
143 assert_eq!(t.info.triplet.map(|tr| tr.blue), Some(246));
145 assert_eq!(t.secondary.triplet.map(|tr| tr.red), Some(168));
147 assert_eq!(t.accent.triplet.map(|tr| tr.green), Some(197));
149 }
150
151 #[test]
152 fn theme_all_styles_accessible() {
153 let t = FastMcpTheme::default();
154 let _ = t.subheader_style.clone();
156 let _ = t.label_style.clone();
157 let _ = t.value_style.clone();
158 let _ = t.key_style.clone();
159 let _ = t.muted_style.clone();
160 let _ = t.info_style.clone();
161 let _ = t.border_style.clone();
162 }
163
164 #[test]
165 fn theme_debug_impl() {
166 let t = FastMcpTheme::default();
167 let debug = format!("{t:?}");
168 assert!(debug.contains("FastMcpTheme"));
169 }
170}