code_mesh_tui/
theme.rs

1use ratatui::style::{Color, Modifier, Style};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Theme trait for defining color schemes
6pub trait Theme {
7    fn name(&self) -> &str;
8    
9    // Background colors
10    fn background(&self) -> Color;
11    fn background_panel(&self) -> Color;
12    fn background_element(&self) -> Color;
13    
14    // Text colors
15    fn text(&self) -> Color;
16    fn text_muted(&self) -> Color;
17    fn text_secondary(&self) -> Color;
18    
19    // Brand colors
20    fn primary(&self) -> Color;
21    fn secondary(&self) -> Color;
22    fn accent(&self) -> Color;
23    
24    // Status colors
25    fn success(&self) -> Color;
26    fn warning(&self) -> Color;
27    fn error(&self) -> Color;
28    fn info(&self) -> Color;
29    
30    // Border colors
31    fn border(&self) -> Color;
32    fn border_active(&self) -> Color;
33    fn border_subtle(&self) -> Color;
34    
35    // Diff colors
36    fn diff_added(&self) -> Color;
37    fn diff_removed(&self) -> Color;
38    fn diff_modified(&self) -> Color;
39    fn diff_context(&self) -> Color;
40    
41    // Syntax highlighting colors
42    fn syntax_keyword(&self) -> Color;
43    fn syntax_string(&self) -> Color;
44    fn syntax_comment(&self) -> Color;
45    fn syntax_number(&self) -> Color;
46    fn syntax_type(&self) -> Color;
47    fn syntax_function(&self) -> Color;
48    fn syntax_variable(&self) -> Color;
49    fn syntax_operator(&self) -> Color;
50}
51
52/// Default theme with dark colors
53#[derive(Debug, Clone)]
54pub struct DefaultTheme;
55
56impl Theme for DefaultTheme {
57    fn name(&self) -> &str { "default" }
58    
59    fn background(&self) -> Color { Color::Rgb(16, 16, 16) }
60    fn background_panel(&self) -> Color { Color::Rgb(24, 24, 24) }
61    fn background_element(&self) -> Color { Color::Rgb(32, 32, 32) }
62    
63    fn text(&self) -> Color { Color::Rgb(240, 240, 240) }
64    fn text_muted(&self) -> Color { Color::Rgb(160, 160, 160) }
65    fn text_secondary(&self) -> Color { Color::Rgb(200, 200, 200) }
66    
67    fn primary(&self) -> Color { Color::Rgb(99, 179, 237) }
68    fn secondary(&self) -> Color { Color::Rgb(158, 134, 200) }
69    fn accent(&self) -> Color { Color::Rgb(255, 184, 108) }
70    
71    fn success(&self) -> Color { Color::Rgb(152, 195, 121) }
72    fn warning(&self) -> Color { Color::Rgb(229, 192, 123) }
73    fn error(&self) -> Color { Color::Rgb(224, 108, 117) }
74    fn info(&self) -> Color { Color::Rgb(97, 175, 239) }
75    
76    fn border(&self) -> Color { Color::Rgb(64, 64, 64) }
77    fn border_active(&self) -> Color { Color::Rgb(99, 179, 237) }
78    fn border_subtle(&self) -> Color { Color::Rgb(48, 48, 48) }
79    
80    fn diff_added(&self) -> Color { Color::Rgb(152, 195, 121) }
81    fn diff_removed(&self) -> Color { Color::Rgb(224, 108, 117) }
82    fn diff_modified(&self) -> Color { Color::Rgb(229, 192, 123) }
83    fn diff_context(&self) -> Color { Color::Rgb(160, 160, 160) }
84    
85    fn syntax_keyword(&self) -> Color { Color::Rgb(198, 120, 221) }
86    fn syntax_string(&self) -> Color { Color::Rgb(152, 195, 121) }
87    fn syntax_comment(&self) -> Color { Color::Rgb(92, 99, 112) }
88    fn syntax_number(&self) -> Color { Color::Rgb(209, 154, 102) }
89    fn syntax_type(&self) -> Color { Color::Rgb(86, 182, 194) }
90    fn syntax_function(&self) -> Color { Color::Rgb(97, 175, 239) }
91    fn syntax_variable(&self) -> Color { Color::Rgb(224, 108, 117) }
92    fn syntax_operator(&self) -> Color { Color::Rgb(86, 182, 194) }
93}
94
95/// Gruvbox dark theme
96#[derive(Debug, Clone)]
97pub struct GruvboxTheme;
98
99impl Theme for GruvboxTheme {
100    fn name(&self) -> &str { "gruvbox" }
101    
102    fn background(&self) -> Color { Color::Rgb(40, 40, 40) }
103    fn background_panel(&self) -> Color { Color::Rgb(50, 48, 47) }
104    fn background_element(&self) -> Color { Color::Rgb(60, 56, 54) }
105    
106    fn text(&self) -> Color { Color::Rgb(235, 219, 178) }
107    fn text_muted(&self) -> Color { Color::Rgb(168, 153, 132) }
108    fn text_secondary(&self) -> Color { Color::Rgb(213, 196, 161) }
109    
110    fn primary(&self) -> Color { Color::Rgb(131, 165, 152) }
111    fn secondary(&self) -> Color { Color::Rgb(211, 134, 155) }
112    fn accent(&self) -> Color { Color::Rgb(250, 189, 47) }
113    
114    fn success(&self) -> Color { Color::Rgb(184, 187, 38) }
115    fn warning(&self) -> Color { Color::Rgb(250, 189, 47) }
116    fn error(&self) -> Color { Color::Rgb(204, 36, 29) }
117    fn info(&self) -> Color { Color::Rgb(69, 133, 136) }
118    
119    fn border(&self) -> Color { Color::Rgb(80, 73, 69) }
120    fn border_active(&self) -> Color { Color::Rgb(131, 165, 152) }
121    fn border_subtle(&self) -> Color { Color::Rgb(60, 56, 54) }
122    
123    fn diff_added(&self) -> Color { Color::Rgb(184, 187, 38) }
124    fn diff_removed(&self) -> Color { Color::Rgb(204, 36, 29) }
125    fn diff_modified(&self) -> Color { Color::Rgb(250, 189, 47) }
126    fn diff_context(&self) -> Color { Color::Rgb(168, 153, 132) }
127    
128    fn syntax_keyword(&self) -> Color { Color::Rgb(251, 73, 52) }
129    fn syntax_string(&self) -> Color { Color::Rgb(184, 187, 38) }
130    fn syntax_comment(&self) -> Color { Color::Rgb(146, 131, 116) }
131    fn syntax_number(&self) -> Color { Color::Rgb(211, 134, 155) }
132    fn syntax_type(&self) -> Color { Color::Rgb(250, 189, 47) }
133    fn syntax_function(&self) -> Color { Color::Rgb(131, 165, 152) }
134    fn syntax_variable(&self) -> Color { Color::Rgb(69, 133, 136) }
135    fn syntax_operator(&self) -> Color { Color::Rgb(254, 128, 25) }
136}
137
138/// Dracula theme
139#[derive(Debug, Clone)]
140pub struct DraculaTheme;
141
142impl Theme for DraculaTheme {
143    fn name(&self) -> &str { "dracula" }
144    
145    fn background(&self) -> Color { Color::Rgb(40, 42, 54) }
146    fn background_panel(&self) -> Color { Color::Rgb(68, 71, 90) }
147    fn background_element(&self) -> Color { Color::Rgb(98, 114, 164) }
148    
149    fn text(&self) -> Color { Color::Rgb(248, 248, 242) }
150    fn text_muted(&self) -> Color { Color::Rgb(98, 114, 164) }
151    fn text_secondary(&self) -> Color { Color::Rgb(189, 147, 249) }
152    
153    fn primary(&self) -> Color { Color::Rgb(139, 233, 253) }
154    fn secondary(&self) -> Color { Color::Rgb(189, 147, 249) }
155    fn accent(&self) -> Color { Color::Rgb(241, 250, 140) }
156    
157    fn success(&self) -> Color { Color::Rgb(80, 250, 123) }
158    fn warning(&self) -> Color { Color::Rgb(241, 250, 140) }
159    fn error(&self) -> Color { Color::Rgb(255, 85, 85) }
160    fn info(&self) -> Color { Color::Rgb(139, 233, 253) }
161    
162    fn border(&self) -> Color { Color::Rgb(68, 71, 90) }
163    fn border_active(&self) -> Color { Color::Rgb(139, 233, 253) }
164    fn border_subtle(&self) -> Color { Color::Rgb(98, 114, 164) }
165    
166    fn diff_added(&self) -> Color { Color::Rgb(80, 250, 123) }
167    fn diff_removed(&self) -> Color { Color::Rgb(255, 85, 85) }
168    fn diff_modified(&self) -> Color { Color::Rgb(241, 250, 140) }
169    fn diff_context(&self) -> Color { Color::Rgb(98, 114, 164) }
170    
171    fn syntax_keyword(&self) -> Color { Color::Rgb(255, 121, 198) }
172    fn syntax_string(&self) -> Color { Color::Rgb(241, 250, 140) }
173    fn syntax_comment(&self) -> Color { Color::Rgb(98, 114, 164) }
174    fn syntax_number(&self) -> Color { Color::Rgb(189, 147, 249) }
175    fn syntax_type(&self) -> Color { Color::Rgb(139, 233, 253) }
176    fn syntax_function(&self) -> Color { Color::Rgb(80, 250, 123) }
177    fn syntax_variable(&self) -> Color { Color::Rgb(255, 184, 108) }
178    fn syntax_operator(&self) -> Color { Color::Rgb(255, 121, 198) }
179}
180
181/// Theme manager for handling multiple themes
182#[derive(Debug, Clone)]
183pub struct ThemeManager {
184    current_theme: Box<dyn Theme + Send + Sync>,
185    available_themes: HashMap<String, Box<dyn Theme + Send + Sync>>,
186}
187
188impl Default for ThemeManager {
189    fn default() -> Self {
190        let mut manager = Self {
191            current_theme: Box::new(DefaultTheme),
192            available_themes: HashMap::new(),
193        };
194        
195        // Register built-in themes
196        manager.register_theme(Box::new(DefaultTheme));
197        manager.register_theme(Box::new(GruvboxTheme));
198        manager.register_theme(Box::new(DraculaTheme));
199        
200        manager
201    }
202}
203
204impl ThemeManager {
205    /// Register a new theme
206    pub fn register_theme(&mut self, theme: Box<dyn Theme + Send + Sync>) {
207        let name = theme.name().to_string();
208        self.available_themes.insert(name, theme);
209    }
210    
211    /// Set the current theme by name
212    pub fn set_theme(&mut self, name: &str) -> anyhow::Result<()> {
213        if let Some(theme) = self.available_themes.get(name) {
214            // Clone the theme (this requires implementing Clone for the theme)
215            // For now, we'll recreate the theme based on name
216            match name {
217                "default" => self.current_theme = Box::new(DefaultTheme),
218                "gruvbox" => self.current_theme = Box::new(GruvboxTheme),
219                "dracula" => self.current_theme = Box::new(DraculaTheme),
220                _ => return Err(anyhow::anyhow!("Unknown theme: {}", name)),
221            }
222            Ok(())
223        } else {
224            Err(anyhow::anyhow!("Theme not found: {}", name))
225        }
226    }
227    
228    /// Get the current theme
229    pub fn current_theme(&self) -> &dyn Theme {
230        self.current_theme.as_ref()
231    }
232    
233    /// Get list of available theme names
234    pub fn available_themes(&self) -> Vec<String> {
235        self.available_themes.keys().cloned().collect()
236    }
237}
238
239/// Helper functions for creating styled components
240pub struct Styles;
241
242impl Styles {
243    /// Create a style with the current theme's primary color
244    pub fn primary(theme: &dyn Theme) -> Style {
245        Style::default().fg(theme.primary())
246    }
247    
248    /// Create a style with the current theme's secondary color
249    pub fn secondary(theme: &dyn Theme) -> Style {
250        Style::default().fg(theme.secondary())
251    }
252    
253    /// Create a style with the current theme's accent color
254    pub fn accent(theme: &dyn Theme) -> Style {
255        Style::default().fg(theme.accent())
256    }
257    
258    /// Create a style for success messages
259    pub fn success(theme: &dyn Theme) -> Style {
260        Style::default().fg(theme.success())
261    }
262    
263    /// Create a style for warning messages
264    pub fn warning(theme: &dyn Theme) -> Style {
265        Style::default().fg(theme.warning())
266    }
267    
268    /// Create a style for error messages
269    pub fn error(theme: &dyn Theme) -> Style {
270        Style::default().fg(theme.error())
271    }
272    
273    /// Create a style for info messages
274    pub fn info(theme: &dyn Theme) -> Style {
275        Style::default().fg(theme.info())
276    }
277    
278    /// Create a style for muted text
279    pub fn muted(theme: &dyn Theme) -> Style {
280        Style::default().fg(theme.text_muted())
281    }
282    
283    /// Create a style for borders
284    pub fn border(theme: &dyn Theme) -> Style {
285        Style::default().fg(theme.border())
286    }
287    
288    /// Create a style for active borders
289    pub fn border_active(theme: &dyn Theme) -> Style {
290        Style::default().fg(theme.border_active())
291    }
292    
293    /// Create a bold style
294    pub fn bold(theme: &dyn Theme) -> Style {
295        Style::default().fg(theme.text()).add_modifier(Modifier::BOLD)
296    }
297    
298    /// Create an italic style
299    pub fn italic(theme: &dyn Theme) -> Style {
300        Style::default().fg(theme.text()).add_modifier(Modifier::ITALIC)
301    }
302    
303    /// Create an underlined style
304    pub fn underline(theme: &dyn Theme) -> Style {
305        Style::default().fg(theme.text()).add_modifier(Modifier::UNDERLINED)
306    }
307}
308
309/// Theme-aware color definitions for serialization
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct ThemeColors {
312    pub background: String,
313    pub background_panel: String,
314    pub background_element: String,
315    pub text: String,
316    pub text_muted: String,
317    pub text_secondary: String,
318    pub primary: String,
319    pub secondary: String,
320    pub accent: String,
321    pub success: String,
322    pub warning: String,
323    pub error: String,
324    pub info: String,
325    pub border: String,
326    pub border_active: String,
327    pub border_subtle: String,
328    pub diff_added: String,
329    pub diff_removed: String,
330    pub diff_modified: String,
331    pub diff_context: String,
332}
333
334impl ThemeColors {
335    /// Convert hex color string to ratatui Color
336    pub fn parse_color(hex: &str) -> anyhow::Result<Color> {
337        let hex = hex.trim_start_matches('#');
338        if hex.len() != 6 {
339            return Err(anyhow::anyhow!("Invalid hex color: {}", hex));
340        }
341        
342        let r = u8::from_str_radix(&hex[0..2], 16)?;
343        let g = u8::from_str_radix(&hex[2..4], 16)?;
344        let b = u8::from_str_radix(&hex[4..6], 16)?;
345        
346        Ok(Color::Rgb(r, g, b))
347    }
348    
349    /// Convert ratatui Color to hex string
350    pub fn color_to_hex(color: Color) -> String {
351        match color {
352            Color::Rgb(r, g, b) => format!("#{:02x}{:02x}{:02x}", r, g, b),
353            _ => "#000000".to_string(), // Fallback for non-RGB colors
354        }
355    }
356}