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 {
157 self.bold
158 }
159 pub fn italic(&self) -> Modifier {
160 self.italic
161 }
162 pub fn strikethrough(&self) -> Modifier {
163 self.strikethrough
164 }
165 pub fn inline_code(&self) -> Style {
166 self.inline_code
167 }
168 pub fn link_text(&self) -> Style {
169 self.link_text
170 }
171 pub fn link_url(&self) -> Style {
172 self.link_url
173 }
174 pub fn heading_1(&self) -> Style {
175 self.heading_1
176 }
177 pub fn heading_2(&self) -> Style {
178 self.heading_2
179 }
180 pub fn heading_3(&self) -> Style {
181 self.heading_3
182 }
183 pub fn heading_4(&self) -> Style {
184 self.heading_4
185 }
186 pub fn code_block(&self) -> Style {
187 self.code_block
188 }
189 pub fn assistant_prefix(&self) -> Style {
190 self.assistant_prefix
191 }
192
193 pub fn table_header(&self) -> Style {
195 self.table_header
196 }
197 pub fn table_cell(&self) -> Style {
198 self.table_cell
199 }
200 pub fn table_border(&self) -> Style {
201 self.table_border
202 }
203
204 pub fn border(&self) -> Style {
206 self.border
207 }
208 pub fn border_focused(&self) -> Style {
209 self.border_focused
210 }
211
212 pub fn popup_border(&self) -> Style {
214 self.popup_border
215 }
216 pub fn popup_header(&self) -> Style {
217 self.popup_header
218 }
219 pub fn popup_item(&self) -> Style {
220 self.popup_item
221 }
222 pub fn popup_item_selected(&self) -> Style {
223 self.popup_item_selected
224 }
225 pub fn popup_item_desc(&self) -> Style {
226 self.popup_item_desc
227 }
228 pub fn popup_item_desc_selected(&self) -> Style {
229 self.popup_item_desc_selected
230 }
231 pub fn popup_selected_bg(&self) -> Style {
232 self.popup_selected_bg
233 }
234 pub fn popup_empty(&self) -> Style {
235 self.popup_empty
236 }
237
238 pub fn status_help(&self) -> Style {
240 self.status_help
241 }
242 pub fn background(&self) -> Style {
243 self.background
244 }
245 pub fn text(&self) -> Style {
246 self.text
247 }
248 pub fn cursor(&self) -> Style {
249 self.cursor
250 }
251
252 pub fn help_text(&self) -> Style {
254 self.help_text
255 }
256 pub fn muted_text(&self) -> Style {
257 self.muted_text
258 }
259 pub fn focused_text(&self) -> Style {
260 self.focused_text
261 }
262 pub fn focus_indicator(&self) -> Style {
263 self.focus_indicator
264 }
265 pub fn selected(&self) -> Style {
266 self.selected
267 }
268 pub fn unselected(&self) -> Style {
269 self.unselected
270 }
271 pub fn button_confirm(&self) -> Style {
272 self.button_confirm
273 }
274 pub fn button_confirm_focused(&self) -> Style {
275 self.button_confirm_focused
276 }
277 pub fn button_cancel(&self) -> Style {
278 self.button_cancel
279 }
280 pub fn button_cancel_focused(&self) -> Style {
281 self.button_cancel_focused
282 }
283 pub fn warning(&self) -> Style {
284 self.warning
285 }
286 pub fn category(&self) -> Style {
287 self.category
288 }
289 pub fn resource(&self) -> Style {
290 self.resource
291 }
292}
293
294impl Default for Theme {
295 fn default() -> Self {
296 Self {
297 background: Style::default(),
299 text: Style::default(),
300
301 border: Style::default().fg(Color::DarkGray),
303 border_focused: Style::default().fg(Color::Cyan),
304
305 title_separator: Style::default().fg(Color::DarkGray),
307 title_indicator_connected: Style::default().fg(Color::Green),
308 title_indicator_disconnected: Style::default().fg(Color::Red),
309 title_text: Style::default(),
310
311 user_prefix: Style::default().fg(Color::Blue),
313 system_prefix: Style::default().fg(Color::Yellow),
314 assistant_prefix: Style::default().fg(Color::Magenta),
315 timestamp: Style::default().fg(Color::DarkGray),
316
317 cursor: Style::default().fg(Color::Green),
319
320 bold: Modifier::BOLD,
322 italic: Modifier::ITALIC,
323 strikethrough: Modifier::CROSSED_OUT,
324 inline_code: Style::default().fg(Color::Yellow),
325 link_text: Style::default().fg(Color::Cyan),
326 link_url: Style::default().fg(Color::DarkGray),
327
328 heading_1: Style::default()
330 .fg(Color::Cyan)
331 .add_modifier(Modifier::BOLD),
332 heading_2: Style::default()
333 .fg(Color::Green)
334 .add_modifier(Modifier::BOLD),
335 heading_3: Style::default()
336 .fg(Color::Yellow)
337 .add_modifier(Modifier::BOLD),
338 heading_4: Style::default()
339 .fg(Color::White)
340 .add_modifier(Modifier::BOLD),
341
342 code_block: Style::default().fg(Color::Rgb(180, 180, 180)),
344
345 table_header: Style::default()
347 .fg(Color::Cyan)
348 .add_modifier(Modifier::BOLD),
349 table_cell: Style::default().fg(Color::White),
350 table_border: Style::default().fg(Color::DarkGray),
351
352 tool_header: Style::default().fg(Color::Cyan),
354 tool_executing: Style::default()
355 .fg(Color::Gray)
356 .add_modifier(Modifier::ITALIC),
357 tool_completed: Style::default().fg(Color::Green),
358 tool_failed: Style::default().fg(Color::Red),
359
360 input_border: Style::default().fg(Color::DarkGray),
362 input_text: Style::default(),
363 prompt: Style::default(),
364
365 throbber_label: Style::default().fg(Color::DarkGray),
367 throbber_spinner: Style::default().fg(Color::Cyan),
368
369 status_help: Style::default().fg(Color::DarkGray),
371 status_model: Style::default().fg(Color::DarkGray),
372
373 popup_border: Style::default().fg(Color::DarkGray),
375 popup_header: Style::default().fg(Color::DarkGray),
376 popup_item: Style::default().fg(Color::White),
377 popup_item_selected: Style::default()
378 .fg(Color::Cyan)
379 .add_modifier(Modifier::BOLD),
380 popup_item_desc: Style::default().fg(Color::DarkGray),
381 popup_item_desc_selected: Style::default().fg(Color::Gray),
382 popup_selected_bg: Style::default().bg(Color::DarkGray),
383 popup_empty: Style::default()
384 .fg(Color::DarkGray)
385 .add_modifier(Modifier::ITALIC),
386
387 help_text: Style::default().fg(Color::DarkGray),
389 muted_text: Style::default().fg(Color::Gray),
390 focused_text: Style::default()
391 .fg(Color::Cyan)
392 .add_modifier(Modifier::BOLD),
393 focus_indicator: Style::default().fg(Color::Yellow),
394 selected: Style::default().fg(Color::Green),
395 unselected: Style::default().fg(Color::Gray),
396 button_confirm: Style::default().fg(Color::Rgb(60, 120, 60)),
397 button_confirm_focused: Style::default()
398 .fg(Color::LightGreen)
399 .add_modifier(Modifier::BOLD),
400 button_cancel: Style::default().fg(Color::Rgb(140, 70, 70)),
401 button_cancel_focused: Style::default()
402 .fg(Color::LightRed)
403 .add_modifier(Modifier::BOLD),
404 warning: Style::default().fg(Color::Yellow),
405 category: Style::default().fg(Color::Magenta),
406 resource: Style::default().fg(Color::Cyan),
407 }
408 }
409}