arct_tui/
theme.rs

1//! Theme and color scheme management
2
3use ratatui::style::{Color, Style, Modifier};
4
5/// Theme for the TUI
6#[derive(Debug, Clone)]
7pub struct Theme {
8    pub name: String,
9
10    // Background colors
11    pub bg_primary: Color,
12    pub bg_secondary: Color,
13    pub bg_tertiary: Color,
14
15    // Foreground colors
16    pub fg_primary: Color,
17    pub fg_secondary: Color,
18    pub fg_dim: Color,
19
20    // Accent colors
21    pub accent: Color,
22    pub success: Color,
23    pub warning: Color,
24    pub error: Color,
25    pub info: Color,
26
27    // UI elements
28    pub border: Color,
29    pub border_focused: Color,
30    pub selection: Color,
31}
32
33impl Theme {
34    /// Arc Academy Orange theme (default)
35    pub fn arc_academy_orange() -> Self {
36        Self {
37            name: "Arc Academy Orange".to_string(),
38
39            // Dark background with orange accents
40            bg_primary: Color::Rgb(18, 18, 20),      // #121214 - Very dark
41            bg_secondary: Color::Rgb(26, 26, 28),    // #1a1a1c - Dark
42            bg_tertiary: Color::Rgb(32, 32, 36),     // #202024 - Lighter dark
43
44            fg_primary: Color::Rgb(230, 230, 235),   // #e6e6eb - Bright text
45            fg_secondary: Color::Rgb(180, 180, 190), // #b4b4be - Secondary text
46            fg_dim: Color::Rgb(120, 120, 130),       // #787882 - Dim text
47
48            accent: Color::Rgb(255, 107, 53),        // #FF6B35 - Arc Orange
49            success: Color::Rgb(80, 200, 120),       // #50C878 - Emerald green
50            warning: Color::Rgb(255, 179, 71),       // #FFB347 - Warm orange
51            error: Color::Rgb(255, 82, 82),          // #FF5252 - Red
52            info: Color::Rgb(100, 181, 246),         // #64B5F6 - Blue
53
54            border: Color::Rgb(60, 60, 65),          // #3c3c41
55            border_focused: Color::Rgb(255, 107, 53), // #FF6B35 - Arc Orange
56            selection: Color::Rgb(80, 50, 35),       // #503223 - Dark orange
57        }
58    }
59
60    /// Arc Academy Green theme
61    pub fn arc_academy_green() -> Self {
62        Self {
63            name: "Arc Academy Green".to_string(),
64
65            // Dark background with green accents
66            bg_primary: Color::Rgb(16, 20, 18),      // #101412 - Very dark green tint
67            bg_secondary: Color::Rgb(22, 27, 24),    // #161b18 - Dark green tint
68            bg_tertiary: Color::Rgb(28, 34, 30),     // #1c221e - Lighter dark green
69
70            fg_primary: Color::Rgb(230, 240, 235),   // #e6f0eb - Bright text with green tint
71            fg_secondary: Color::Rgb(180, 200, 190), // #b4c8be - Secondary text
72            fg_dim: Color::Rgb(120, 140, 130),       // #788c82 - Dim text
73
74            accent: Color::Rgb(76, 175, 80),         // #4CAF50 - Arc Green
75            success: Color::Rgb(102, 187, 106),      // #66BB6A - Success green
76            warning: Color::Rgb(255, 193, 7),        // #FFC107 - Amber
77            error: Color::Rgb(244, 67, 54),          // #F44336 - Red
78            info: Color::Rgb(41, 182, 246),          // #29B6F6 - Cyan
79
80            border: Color::Rgb(50, 65, 55),          // #324137
81            border_focused: Color::Rgb(76, 175, 80), // #4CAF50 - Arc Green
82            selection: Color::Rgb(30, 50, 35),       // #1e3223 - Dark green
83        }
84    }
85
86    /// Arc Dark theme (Catppuccin inspired - fallback)
87    pub fn arc_dark() -> Self {
88        Self {
89            name: "Arc Dark".to_string(),
90
91            // Catppuccin Mocha inspired
92            bg_primary: Color::Rgb(30, 30, 46),      // #1e1e2e
93            bg_secondary: Color::Rgb(24, 24, 37),    // #181825
94            bg_tertiary: Color::Rgb(17, 17, 27),     // #11111b
95
96            fg_primary: Color::Rgb(205, 214, 244),   // #cdd6f4
97            fg_secondary: Color::Rgb(186, 194, 222), // #bac2de
98            fg_dim: Color::Rgb(108, 112, 134),       // #6c7086
99
100            accent: Color::Rgb(137, 180, 250),       // #89b4fa (blue)
101            success: Color::Rgb(166, 227, 161),      // #a6e3a1 (green)
102            warning: Color::Rgb(249, 226, 175),      // #f9e2af (yellow)
103            error: Color::Rgb(243, 139, 168),        // #f38ba8 (red)
104            info: Color::Rgb(148, 226, 213),         // #94e2d5 (teal)
105
106            border: Color::Rgb(108, 112, 134),       // #6c7086
107            border_focused: Color::Rgb(137, 180, 250), // #89b4fa
108            selection: Color::Rgb(88, 91, 112),      // #585b70
109        }
110    }
111
112    /// Arc Light theme
113    pub fn arc_light() -> Self {
114        Self {
115            name: "Arc Light".to_string(),
116
117            bg_primary: Color::Rgb(239, 241, 245),   // #eff1f5
118            bg_secondary: Color::Rgb(230, 233, 239), // #e6e9ef
119            bg_tertiary: Color::Rgb(220, 224, 232),  // #dce0e8
120
121            fg_primary: Color::Rgb(76, 79, 105),     // #4c4f69
122            fg_secondary: Color::Rgb(92, 95, 119),   // #5c5f77
123            fg_dim: Color::Rgb(156, 160, 176),       // #9ca0b0
124
125            accent: Color::Rgb(30, 102, 245),        // #1e66f5
126            success: Color::Rgb(64, 160, 43),        // #40a02b
127            warning: Color::Rgb(223, 142, 29),       // #df8e1d
128            error: Color::Rgb(210, 15, 57),          // #d20f39
129            info: Color::Rgb(4, 165, 229),           // #04a5e5
130
131            border: Color::Rgb(156, 160, 176),       // #9ca0b0
132            border_focused: Color::Rgb(30, 102, 245), // #1e66f5
133            selection: Color::Rgb(204, 208, 218),    // #ccd0da
134        }
135    }
136
137    /// Get a style for normal text
138    pub fn style_normal(&self) -> Style {
139        Style::default().fg(self.fg_primary).bg(self.bg_primary)
140    }
141
142    /// Get a style for secondary text
143    pub fn style_secondary(&self) -> Style {
144        Style::default().fg(self.fg_secondary).bg(self.bg_primary)
145    }
146
147    /// Get a style for dim text
148    pub fn style_dim(&self) -> Style {
149        Style::default().fg(self.fg_dim).bg(self.bg_primary)
150    }
151
152    /// Get a style for headers
153    pub fn style_header(&self) -> Style {
154        Style::default()
155            .fg(self.fg_primary)
156            .bg(self.bg_secondary)
157            .add_modifier(Modifier::BOLD)
158    }
159
160    /// Get a style for focused borders
161    pub fn style_border_focused(&self) -> Style {
162        Style::default().fg(self.border_focused)
163    }
164
165    /// Get a style for unfocused borders
166    pub fn style_border(&self) -> Style {
167        Style::default().fg(self.border)
168    }
169
170    /// Get a style for accent text
171    pub fn style_accent(&self) -> Style {
172        Style::default().fg(self.accent).add_modifier(Modifier::BOLD)
173    }
174
175    /// Get a style for success messages
176    pub fn style_success(&self) -> Style {
177        Style::default().fg(self.success)
178    }
179
180    /// Get a style for warnings
181    pub fn style_warning(&self) -> Style {
182        Style::default().fg(self.warning).add_modifier(Modifier::BOLD)
183    }
184
185    /// Get a style for errors
186    pub fn style_error(&self) -> Style {
187        Style::default().fg(self.error).add_modifier(Modifier::BOLD)
188    }
189
190    /// Get a style for info messages
191    pub fn style_info(&self) -> Style {
192        Style::default().fg(self.info)
193    }
194
195    /// Get a style for selections
196    pub fn style_selection(&self) -> Style {
197        Style::default()
198            .bg(self.selection)
199            .fg(self.fg_primary)
200    }
201
202    /// Get a style for panel/block backgrounds
203    /// This ensures panels have the correct background color (important for light themes)
204    pub fn style_block(&self) -> Style {
205        Style::default().bg(self.bg_primary).fg(self.fg_primary)
206    }
207}
208
209impl Default for Theme {
210    fn default() -> Self {
211        Self::arc_academy_orange()
212    }
213}
214
215impl Theme {
216    /// Cycle to the next theme
217    pub fn cycle_next(&self) -> Self {
218        match self.name.as_str() {
219            "Arc Academy Orange" => Self::arc_academy_green(),
220            "Arc Academy Green" => Self::arc_dark(),
221            "Arc Dark" => Self::arc_light(),
222            _ => Self::arc_academy_orange(),
223        }
224    }
225
226    /// Get theme by name
227    pub fn from_name(name: &str) -> Self {
228        match name {
229            "Arc Academy Orange" => Self::arc_academy_orange(),
230            "Arc Academy Green" => Self::arc_academy_green(),
231            "Arc Dark" => Self::arc_dark(),
232            "Arc Light" => Self::arc_light(),
233            _ => {
234                tracing::warn!("Unknown theme '{}', using default", name);
235                Self::default()
236            }
237        }
238    }
239}