agent_core/tui/themes/
theme.rs

1// Theme system for the TUI
2//
3// Centralizes all colors, modifiers, and styles for consistent theming.
4// Supports runtime theme switching via RwLock.
5
6use std::sync::RwLock;
7
8use ratatui::style::{Color, Modifier, Style};
9
10/// Global theme instance with runtime switching support
11static THEME: RwLock<Option<Theme>> = RwLock::new(None);
12
13/// Current theme name
14static THEME_NAME: RwLock<Option<String>> = RwLock::new(None);
15
16/// Initialize the global theme (call once at startup)
17pub fn init_theme(name: &str, theme: Theme) {
18    if let Ok(mut guard) = THEME.write() {
19        *guard = Some(theme);
20    }
21    if let Ok(mut guard) = THEME_NAME.write() {
22        *guard = Some(name.to_string());
23    }
24}
25
26/// Set a new theme at runtime
27pub fn set_theme(name: &str, theme: Theme) {
28    if let Ok(mut guard) = THEME.write() {
29        *guard = Some(theme);
30    }
31    if let Ok(mut guard) = THEME_NAME.write() {
32        *guard = Some(name.to_string());
33    }
34}
35
36/// Get the current theme name
37pub fn current_theme_name() -> String {
38    THEME_NAME
39        .read()
40        .ok()
41        .and_then(|guard| guard.clone())
42        .unwrap_or_else(|| "default".to_string())
43}
44
45/// Get the current theme (cloned for safe access)
46pub fn theme() -> Theme {
47    THEME
48        .read()
49        .ok()
50        .and_then(|guard| guard.clone())
51        .unwrap_or_default()
52}
53
54/// Complete theme definition for the TUI.
55///
56/// Contains styles for all UI elements including chat messages, markdown formatting,
57/// tool execution, input areas, popups, and interactive panels.
58#[derive(Clone)]
59pub struct Theme {
60    // Base
61    /// Background style for the entire UI.
62    pub background: Style,
63    /// Default text style.
64    pub text: Style,
65
66    // Borders & Chrome
67    pub border: Style,
68    pub border_focused: Style,
69
70    // Chat Title Bar
71    pub title_separator: Style,
72    pub title_indicator_connected: Style,
73    pub title_indicator_disconnected: Style,
74    pub title_text: Style,
75
76    // Message Roles
77    pub user_prefix: Style,
78    pub system_prefix: Style,
79    pub assistant_prefix: Style,
80    pub timestamp: Style,
81
82    // Streaming
83    pub cursor: Style,
84
85    // Markdown - Inline
86    pub bold: Modifier,
87    pub italic: Modifier,
88    pub strikethrough: Modifier,
89    pub inline_code: Style,
90    pub link_text: Style,
91    pub link_url: Style,
92
93    // Markdown - Headings
94    pub heading_1: Style,
95    pub heading_2: Style,
96    pub heading_3: Style,
97    pub heading_4: Style,
98
99    // Markdown - Code Blocks
100    pub code_block: Style,
101
102    // Tables
103    pub table_header: Style,
104    pub table_cell: Style,
105    pub table_border: Style,
106
107    // Tool Execution
108    pub tool_header: Style,
109    pub tool_executing: Style,
110    pub tool_completed: Style,
111    pub tool_failed: Style,
112
113    // Input Area
114    pub input_border: Style,
115    pub input_text: Style,
116    pub prompt: Style,
117
118    // Throbber/Progress
119    pub throbber_label: Style,
120    pub throbber_spinner: Style,
121
122    // Status Bar
123    pub status_help: Style,
124    pub status_model: Style,
125
126    // Slash Command Popup
127    pub popup_border: Style,
128    pub popup_header: Style,
129    pub popup_item: Style,
130    pub popup_item_selected: Style,
131    pub popup_item_desc: Style,
132    pub popup_item_desc_selected: Style,
133    pub popup_selected_bg: Style,
134    pub popup_empty: Style,
135
136    // UI Panel styles (permission panel, question panel)
137    pub help_text: Style,
138    pub muted_text: Style,
139    pub focused_text: Style,
140    pub focus_indicator: Style,
141    pub selected: Style,
142    pub unselected: Style,
143    pub button_confirm: Style,
144    pub button_confirm_focused: Style,
145    pub button_cancel: Style,
146    pub button_cancel_focused: Style,
147    pub warning: Style,
148    pub category: Style,
149    pub resource: Style,
150}
151
152impl Theme {
153    // Accessor methods for compatibility with code that used the old trait
154
155    // Markdown styles
156    pub fn bold(&self) -> Modifier { self.bold }
157    pub fn italic(&self) -> Modifier { self.italic }
158    pub fn strikethrough(&self) -> Modifier { self.strikethrough }
159    pub fn inline_code(&self) -> Style { self.inline_code }
160    pub fn link_text(&self) -> Style { self.link_text }
161    pub fn link_url(&self) -> Style { self.link_url }
162    pub fn heading_1(&self) -> Style { self.heading_1 }
163    pub fn heading_2(&self) -> Style { self.heading_2 }
164    pub fn heading_3(&self) -> Style { self.heading_3 }
165    pub fn heading_4(&self) -> Style { self.heading_4 }
166    pub fn code_block(&self) -> Style { self.code_block }
167    pub fn assistant_prefix(&self) -> Style { self.assistant_prefix }
168
169    // Table styles
170    pub fn table_header(&self) -> Style { self.table_header }
171    pub fn table_cell(&self) -> Style { self.table_cell }
172    pub fn table_border(&self) -> Style { self.table_border }
173
174    // Border styles
175    pub fn border(&self) -> Style { self.border }
176    pub fn border_focused(&self) -> Style { self.border_focused }
177
178    // Popup styles
179    pub fn popup_border(&self) -> Style { self.popup_border }
180    pub fn popup_header(&self) -> Style { self.popup_header }
181    pub fn popup_item(&self) -> Style { self.popup_item }
182    pub fn popup_item_selected(&self) -> Style { self.popup_item_selected }
183    pub fn popup_item_desc(&self) -> Style { self.popup_item_desc }
184    pub fn popup_item_desc_selected(&self) -> Style { self.popup_item_desc_selected }
185    pub fn popup_selected_bg(&self) -> Style { self.popup_selected_bg }
186    pub fn popup_empty(&self) -> Style { self.popup_empty }
187
188    // Status styles
189    pub fn status_help(&self) -> Style { self.status_help }
190    pub fn background(&self) -> Style { self.background }
191    pub fn text(&self) -> Style { self.text }
192    pub fn cursor(&self) -> Style { self.cursor }
193
194    // UI Panel styles
195    pub fn help_text(&self) -> Style { self.help_text }
196    pub fn muted_text(&self) -> Style { self.muted_text }
197    pub fn focused_text(&self) -> Style { self.focused_text }
198    pub fn focus_indicator(&self) -> Style { self.focus_indicator }
199    pub fn selected(&self) -> Style { self.selected }
200    pub fn unselected(&self) -> Style { self.unselected }
201    pub fn button_confirm(&self) -> Style { self.button_confirm }
202    pub fn button_confirm_focused(&self) -> Style { self.button_confirm_focused }
203    pub fn button_cancel(&self) -> Style { self.button_cancel }
204    pub fn button_cancel_focused(&self) -> Style { self.button_cancel_focused }
205    pub fn warning(&self) -> Style { self.warning }
206    pub fn category(&self) -> Style { self.category }
207    pub fn resource(&self) -> Style { self.resource }
208}
209
210impl Default for Theme {
211    fn default() -> Self {
212        Self {
213            // Base
214            background: Style::default(),
215            text: Style::default(),
216
217            // Borders & Chrome
218            border: Style::default().fg(Color::DarkGray),
219            border_focused: Style::default().fg(Color::Cyan),
220
221            // Chat Title Bar
222            title_separator: Style::default().fg(Color::DarkGray),
223            title_indicator_connected: Style::default().fg(Color::Green),
224            title_indicator_disconnected: Style::default().fg(Color::Red),
225            title_text: Style::default(),
226
227            // Message Roles
228            user_prefix: Style::default().fg(Color::Blue),
229            system_prefix: Style::default().fg(Color::Yellow),
230            assistant_prefix: Style::default().fg(Color::Magenta),
231            timestamp: Style::default().fg(Color::DarkGray),
232
233            // Streaming
234            cursor: Style::default().fg(Color::Green),
235
236            // Markdown - Inline (modifiers only, preserve existing fg color)
237            bold: Modifier::BOLD,
238            italic: Modifier::ITALIC,
239            strikethrough: Modifier::CROSSED_OUT,
240            inline_code: Style::default().fg(Color::Yellow),
241            link_text: Style::default().fg(Color::Cyan),
242            link_url: Style::default().fg(Color::DarkGray),
243
244            // Markdown - Headings
245            heading_1: Style::default()
246                .fg(Color::Cyan)
247                .add_modifier(Modifier::BOLD),
248            heading_2: Style::default()
249                .fg(Color::Green)
250                .add_modifier(Modifier::BOLD),
251            heading_3: Style::default()
252                .fg(Color::Yellow)
253                .add_modifier(Modifier::BOLD),
254            heading_4: Style::default()
255                .fg(Color::White)
256                .add_modifier(Modifier::BOLD),
257
258            // Markdown - Code Blocks
259            code_block: Style::default().fg(Color::Rgb(180, 180, 180)),
260
261            // Tables
262            table_header: Style::default()
263                .fg(Color::Cyan)
264                .add_modifier(Modifier::BOLD),
265            table_cell: Style::default().fg(Color::White),
266            table_border: Style::default().fg(Color::DarkGray),
267
268            // Tool Execution
269            tool_header: Style::default().fg(Color::Cyan),
270            tool_executing: Style::default()
271                .fg(Color::Gray)
272                .add_modifier(Modifier::ITALIC),
273            tool_completed: Style::default().fg(Color::Green),
274            tool_failed: Style::default().fg(Color::Red),
275
276            // Input Area
277            input_border: Style::default().fg(Color::DarkGray),
278            input_text: Style::default(),
279            prompt: Style::default(),
280
281            // Throbber/Progress
282            throbber_label: Style::default().fg(Color::DarkGray),
283            throbber_spinner: Style::default().fg(Color::Cyan),
284
285            // Status Bar
286            status_help: Style::default().fg(Color::DarkGray),
287            status_model: Style::default().fg(Color::DarkGray),
288
289            // Slash Command Popup
290            popup_border: Style::default().fg(Color::DarkGray),
291            popup_header: Style::default().fg(Color::DarkGray),
292            popup_item: Style::default().fg(Color::White),
293            popup_item_selected: Style::default()
294                .fg(Color::Cyan)
295                .add_modifier(Modifier::BOLD),
296            popup_item_desc: Style::default().fg(Color::DarkGray),
297            popup_item_desc_selected: Style::default().fg(Color::Gray),
298            popup_selected_bg: Style::default().bg(Color::DarkGray),
299            popup_empty: Style::default()
300                .fg(Color::DarkGray)
301                .add_modifier(Modifier::ITALIC),
302
303            // UI Panel styles
304            help_text: Style::default().fg(Color::DarkGray),
305            muted_text: Style::default().fg(Color::Gray),
306            focused_text: Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
307            focus_indicator: Style::default().fg(Color::Yellow),
308            selected: Style::default().fg(Color::Green),
309            unselected: Style::default().fg(Color::Gray),
310            button_confirm: Style::default().fg(Color::Rgb(60, 120, 60)),
311            button_confirm_focused: Style::default().fg(Color::LightGreen).add_modifier(Modifier::BOLD),
312            button_cancel: Style::default().fg(Color::Rgb(140, 70, 70)),
313            button_cancel_focused: Style::default().fg(Color::LightRed).add_modifier(Modifier::BOLD),
314            warning: Style::default().fg(Color::Yellow),
315            category: Style::default().fg(Color::Magenta),
316            resource: Style::default().fg(Color::Cyan),
317        }
318    }
319}
320