1use std::sync::RwLock;
7
8use ratatui::style::{Color, Modifier, Style};
9
10static THEME: RwLock<Option<Theme>> = RwLock::new(None);
12
13static THEME_NAME: RwLock<Option<String>> = RwLock::new(None);
15
16pub 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
26pub 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
36pub 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
45pub fn theme() -> Theme {
47 THEME
48 .read()
49 .ok()
50 .and_then(|guard| guard.clone())
51 .unwrap_or_default()
52}
53
54#[derive(Clone)]
59pub struct Theme {
60 pub background: Style,
63 pub text: Style,
65
66 pub border: Style,
68 pub border_focused: Style,
69
70 pub title_separator: Style,
72 pub title_indicator_connected: Style,
73 pub title_indicator_disconnected: Style,
74 pub title_text: Style,
75
76 pub user_prefix: Style,
78 pub system_prefix: Style,
79 pub assistant_prefix: Style,
80 pub timestamp: Style,
81
82 pub cursor: Style,
84
85 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 pub heading_1: Style,
95 pub heading_2: Style,
96 pub heading_3: Style,
97 pub heading_4: Style,
98
99 pub code_block: Style,
101
102 pub table_header: Style,
104 pub table_cell: Style,
105 pub table_border: Style,
106
107 pub tool_header: Style,
109 pub tool_executing: Style,
110 pub tool_completed: Style,
111 pub tool_failed: Style,
112
113 pub input_border: Style,
115 pub input_text: Style,
116 pub prompt: Style,
117
118 pub throbber_label: Style,
120 pub throbber_spinner: Style,
121
122 pub status_help: Style,
124 pub status_model: Style,
125
126 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 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 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 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 pub fn border(&self) -> Style { self.border }
176 pub fn border_focused(&self) -> Style { self.border_focused }
177
178 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 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 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 background: Style::default(),
215 text: Style::default(),
216
217 border: Style::default().fg(Color::DarkGray),
219 border_focused: Style::default().fg(Color::Cyan),
220
221 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 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 cursor: Style::default().fg(Color::Green),
235
236 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 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 code_block: Style::default().fg(Color::Rgb(180, 180, 180)),
260
261 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_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_border: Style::default().fg(Color::DarkGray),
278 input_text: Style::default(),
279 prompt: Style::default(),
280
281 throbber_label: Style::default().fg(Color::DarkGray),
283 throbber_spinner: Style::default().fg(Color::Cyan),
284
285 status_help: Style::default().fg(Color::DarkGray),
287 status_model: Style::default().fg(Color::DarkGray),
288
289 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 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