1use ratatui::style::Color;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9
10pub const THEME_DARK: &str = "dark";
11pub const THEME_LIGHT: &str = "light";
12pub const THEME_HIGH_CONTRAST: &str = "high-contrast";
13pub const THEME_NOSTALGIA: &str = "nostalgia";
14pub const THEME_DRACULA: &str = "dracula";
15pub const THEME_NORD: &str = "nord";
16pub const THEME_SOLARIZED_DARK: &str = "solarized-dark";
17
18pub struct BuiltinTheme {
20 pub name: &'static str,
21 pub pack: &'static str,
23 pub json: &'static str,
24}
25
26include!(concat!(env!("OUT_DIR"), "/builtin_themes.rs"));
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct ThemeInfo {
32 pub name: String,
34 pub pack: String,
36}
37
38impl ThemeInfo {
39 pub fn new(name: impl Into<String>, pack: impl Into<String>) -> Self {
41 Self {
42 name: name.into(),
43 pack: pack.into(),
44 }
45 }
46
47 pub fn display_name(&self) -> String {
49 if self.pack.is_empty() {
50 self.name.clone()
51 } else {
52 format!("{} ({})", self.name, self.pack)
53 }
54 }
55}
56
57pub fn color_to_rgb(color: Color) -> Option<(u8, u8, u8)> {
60 match color {
61 Color::Rgb(r, g, b) => Some((r, g, b)),
62 Color::White => Some((255, 255, 255)),
63 Color::Black => Some((0, 0, 0)),
64 Color::Red => Some((205, 0, 0)),
65 Color::Green => Some((0, 205, 0)),
66 Color::Blue => Some((0, 0, 238)),
67 Color::Yellow => Some((205, 205, 0)),
68 Color::Magenta => Some((205, 0, 205)),
69 Color::Cyan => Some((0, 205, 205)),
70 Color::Gray => Some((229, 229, 229)),
71 Color::DarkGray => Some((127, 127, 127)),
72 Color::LightRed => Some((255, 0, 0)),
73 Color::LightGreen => Some((0, 255, 0)),
74 Color::LightBlue => Some((92, 92, 255)),
75 Color::LightYellow => Some((255, 255, 0)),
76 Color::LightMagenta => Some((255, 0, 255)),
77 Color::LightCyan => Some((0, 255, 255)),
78 Color::Reset | Color::Indexed(_) => None,
79 }
80}
81
82pub fn brighten_color(color: Color, amount: u8) -> Color {
85 if let Some((r, g, b)) = color_to_rgb(color) {
86 Color::Rgb(
87 r.saturating_add(amount),
88 g.saturating_add(amount),
89 b.saturating_add(amount),
90 )
91 } else {
92 color
93 }
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
98#[serde(untagged)]
99pub enum ColorDef {
100 Rgb(u8, u8, u8),
102 Named(String),
104}
105
106impl From<ColorDef> for Color {
107 fn from(def: ColorDef) -> Self {
108 match def {
109 ColorDef::Rgb(r, g, b) => Color::Rgb(r, g, b),
110 ColorDef::Named(name) => match name.as_str() {
111 "Black" => Color::Black,
112 "Red" => Color::Red,
113 "Green" => Color::Green,
114 "Yellow" => Color::Yellow,
115 "Blue" => Color::Blue,
116 "Magenta" => Color::Magenta,
117 "Cyan" => Color::Cyan,
118 "Gray" => Color::Gray,
119 "DarkGray" => Color::DarkGray,
120 "LightRed" => Color::LightRed,
121 "LightGreen" => Color::LightGreen,
122 "LightYellow" => Color::LightYellow,
123 "LightBlue" => Color::LightBlue,
124 "LightMagenta" => Color::LightMagenta,
125 "LightCyan" => Color::LightCyan,
126 "White" => Color::White,
127 "Default" | "Reset" => Color::Reset,
129 _ => Color::White, },
131 }
132 }
133}
134
135impl From<Color> for ColorDef {
136 fn from(color: Color) -> Self {
137 match color {
138 Color::Rgb(r, g, b) => ColorDef::Rgb(r, g, b),
139 Color::White => ColorDef::Named("White".to_string()),
140 Color::Black => ColorDef::Named("Black".to_string()),
141 Color::Red => ColorDef::Named("Red".to_string()),
142 Color::Green => ColorDef::Named("Green".to_string()),
143 Color::Blue => ColorDef::Named("Blue".to_string()),
144 Color::Yellow => ColorDef::Named("Yellow".to_string()),
145 Color::Magenta => ColorDef::Named("Magenta".to_string()),
146 Color::Cyan => ColorDef::Named("Cyan".to_string()),
147 Color::Gray => ColorDef::Named("Gray".to_string()),
148 Color::DarkGray => ColorDef::Named("DarkGray".to_string()),
149 Color::LightRed => ColorDef::Named("LightRed".to_string()),
150 Color::LightGreen => ColorDef::Named("LightGreen".to_string()),
151 Color::LightBlue => ColorDef::Named("LightBlue".to_string()),
152 Color::LightYellow => ColorDef::Named("LightYellow".to_string()),
153 Color::LightMagenta => ColorDef::Named("LightMagenta".to_string()),
154 Color::LightCyan => ColorDef::Named("LightCyan".to_string()),
155 Color::Reset => ColorDef::Named("Default".to_string()),
156 Color::Indexed(_) => {
157 if let Some((r, g, b)) = color_to_rgb(color) {
159 ColorDef::Rgb(r, g, b)
160 } else {
161 ColorDef::Named("Default".to_string())
162 }
163 }
164 }
165 }
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
170pub struct ThemeFile {
171 pub name: String,
173 pub editor: EditorColors,
175 pub ui: UiColors,
177 pub search: SearchColors,
179 pub diagnostic: DiagnosticColors,
181 pub syntax: SyntaxColors,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
187pub struct EditorColors {
188 #[serde(default = "default_editor_bg")]
190 pub bg: ColorDef,
191 #[serde(default = "default_editor_fg")]
193 pub fg: ColorDef,
194 #[serde(default = "default_cursor")]
196 pub cursor: ColorDef,
197 #[serde(default = "default_inactive_cursor")]
199 pub inactive_cursor: ColorDef,
200 #[serde(default = "default_selection_bg")]
202 pub selection_bg: ColorDef,
203 #[serde(default = "default_current_line_bg")]
205 pub current_line_bg: ColorDef,
206 #[serde(default = "default_line_number_fg")]
208 pub line_number_fg: ColorDef,
209 #[serde(default = "default_line_number_bg")]
211 pub line_number_bg: ColorDef,
212 #[serde(default = "default_diff_add_bg")]
214 pub diff_add_bg: ColorDef,
215 #[serde(default = "default_diff_remove_bg")]
217 pub diff_remove_bg: ColorDef,
218 #[serde(default = "default_diff_modify_bg")]
220 pub diff_modify_bg: ColorDef,
221}
222
223fn default_editor_bg() -> ColorDef {
225 ColorDef::Rgb(30, 30, 30)
226}
227fn default_editor_fg() -> ColorDef {
228 ColorDef::Rgb(212, 212, 212)
229}
230fn default_cursor() -> ColorDef {
231 ColorDef::Rgb(255, 255, 255)
232}
233fn default_inactive_cursor() -> ColorDef {
234 ColorDef::Named("DarkGray".to_string())
235}
236fn default_selection_bg() -> ColorDef {
237 ColorDef::Rgb(38, 79, 120)
238}
239fn default_current_line_bg() -> ColorDef {
240 ColorDef::Rgb(40, 40, 40)
241}
242fn default_line_number_fg() -> ColorDef {
243 ColorDef::Rgb(100, 100, 100)
244}
245fn default_line_number_bg() -> ColorDef {
246 ColorDef::Rgb(30, 30, 30)
247}
248fn default_diff_add_bg() -> ColorDef {
249 ColorDef::Rgb(35, 60, 35) }
251fn default_diff_remove_bg() -> ColorDef {
252 ColorDef::Rgb(70, 35, 35) }
254fn default_diff_modify_bg() -> ColorDef {
255 ColorDef::Rgb(40, 38, 30) }
257
258#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
260pub struct UiColors {
261 #[serde(default = "default_tab_active_fg")]
263 pub tab_active_fg: ColorDef,
264 #[serde(default = "default_tab_active_bg")]
266 pub tab_active_bg: ColorDef,
267 #[serde(default = "default_tab_inactive_fg")]
269 pub tab_inactive_fg: ColorDef,
270 #[serde(default = "default_tab_inactive_bg")]
272 pub tab_inactive_bg: ColorDef,
273 #[serde(default = "default_tab_separator_bg")]
275 pub tab_separator_bg: ColorDef,
276 #[serde(default = "default_tab_close_hover_fg")]
278 pub tab_close_hover_fg: ColorDef,
279 #[serde(default = "default_tab_hover_bg")]
281 pub tab_hover_bg: ColorDef,
282 #[serde(default = "default_menu_bg")]
284 pub menu_bg: ColorDef,
285 #[serde(default = "default_menu_fg")]
287 pub menu_fg: ColorDef,
288 #[serde(default = "default_menu_active_bg")]
290 pub menu_active_bg: ColorDef,
291 #[serde(default = "default_menu_active_fg")]
293 pub menu_active_fg: ColorDef,
294 #[serde(default = "default_menu_dropdown_bg")]
296 pub menu_dropdown_bg: ColorDef,
297 #[serde(default = "default_menu_dropdown_fg")]
299 pub menu_dropdown_fg: ColorDef,
300 #[serde(default = "default_menu_highlight_bg")]
302 pub menu_highlight_bg: ColorDef,
303 #[serde(default = "default_menu_highlight_fg")]
305 pub menu_highlight_fg: ColorDef,
306 #[serde(default = "default_menu_border_fg")]
308 pub menu_border_fg: ColorDef,
309 #[serde(default = "default_menu_separator_fg")]
311 pub menu_separator_fg: ColorDef,
312 #[serde(default = "default_menu_hover_bg")]
314 pub menu_hover_bg: ColorDef,
315 #[serde(default = "default_menu_hover_fg")]
317 pub menu_hover_fg: ColorDef,
318 #[serde(default = "default_menu_disabled_fg")]
320 pub menu_disabled_fg: ColorDef,
321 #[serde(default = "default_menu_disabled_bg")]
323 pub menu_disabled_bg: ColorDef,
324 #[serde(default = "default_status_bar_fg")]
326 pub status_bar_fg: ColorDef,
327 #[serde(default = "default_status_bar_bg")]
329 pub status_bar_bg: ColorDef,
330 #[serde(default = "default_prompt_fg")]
332 pub prompt_fg: ColorDef,
333 #[serde(default = "default_prompt_bg")]
335 pub prompt_bg: ColorDef,
336 #[serde(default = "default_prompt_selection_fg")]
338 pub prompt_selection_fg: ColorDef,
339 #[serde(default = "default_prompt_selection_bg")]
341 pub prompt_selection_bg: ColorDef,
342 #[serde(default = "default_popup_border_fg")]
344 pub popup_border_fg: ColorDef,
345 #[serde(default = "default_popup_bg")]
347 pub popup_bg: ColorDef,
348 #[serde(default = "default_popup_selection_bg")]
350 pub popup_selection_bg: ColorDef,
351 #[serde(default = "default_popup_selection_fg")]
353 pub popup_selection_fg: ColorDef,
354 #[serde(default = "default_popup_text_fg")]
356 pub popup_text_fg: ColorDef,
357 #[serde(default = "default_suggestion_bg")]
359 pub suggestion_bg: ColorDef,
360 #[serde(default = "default_suggestion_selected_bg")]
362 pub suggestion_selected_bg: ColorDef,
363 #[serde(default = "default_help_bg")]
365 pub help_bg: ColorDef,
366 #[serde(default = "default_help_fg")]
368 pub help_fg: ColorDef,
369 #[serde(default = "default_help_key_fg")]
371 pub help_key_fg: ColorDef,
372 #[serde(default = "default_help_separator_fg")]
374 pub help_separator_fg: ColorDef,
375 #[serde(default = "default_help_indicator_fg")]
377 pub help_indicator_fg: ColorDef,
378 #[serde(default = "default_help_indicator_bg")]
380 pub help_indicator_bg: ColorDef,
381 #[serde(default = "default_inline_code_bg")]
383 pub inline_code_bg: ColorDef,
384 #[serde(default = "default_split_separator_fg")]
386 pub split_separator_fg: ColorDef,
387 #[serde(default = "default_split_separator_hover_fg")]
389 pub split_separator_hover_fg: ColorDef,
390 #[serde(default = "default_scrollbar_track_fg")]
392 pub scrollbar_track_fg: ColorDef,
393 #[serde(default = "default_scrollbar_thumb_fg")]
395 pub scrollbar_thumb_fg: ColorDef,
396 #[serde(default = "default_scrollbar_track_hover_fg")]
398 pub scrollbar_track_hover_fg: ColorDef,
399 #[serde(default = "default_scrollbar_thumb_hover_fg")]
401 pub scrollbar_thumb_hover_fg: ColorDef,
402 #[serde(default = "default_compose_margin_bg")]
404 pub compose_margin_bg: ColorDef,
405 #[serde(default = "default_semantic_highlight_bg")]
407 pub semantic_highlight_bg: ColorDef,
408 #[serde(default = "default_terminal_bg")]
410 pub terminal_bg: ColorDef,
411 #[serde(default = "default_terminal_fg")]
413 pub terminal_fg: ColorDef,
414 #[serde(default = "default_status_warning_indicator_bg")]
416 pub status_warning_indicator_bg: ColorDef,
417 #[serde(default = "default_status_warning_indicator_fg")]
419 pub status_warning_indicator_fg: ColorDef,
420 #[serde(default = "default_status_error_indicator_bg")]
422 pub status_error_indicator_bg: ColorDef,
423 #[serde(default = "default_status_error_indicator_fg")]
425 pub status_error_indicator_fg: ColorDef,
426 #[serde(default = "default_status_warning_indicator_hover_bg")]
428 pub status_warning_indicator_hover_bg: ColorDef,
429 #[serde(default = "default_status_warning_indicator_hover_fg")]
431 pub status_warning_indicator_hover_fg: ColorDef,
432 #[serde(default = "default_status_error_indicator_hover_bg")]
434 pub status_error_indicator_hover_bg: ColorDef,
435 #[serde(default = "default_status_error_indicator_hover_fg")]
437 pub status_error_indicator_hover_fg: ColorDef,
438 #[serde(default = "default_tab_drop_zone_bg")]
440 pub tab_drop_zone_bg: ColorDef,
441 #[serde(default = "default_tab_drop_zone_border")]
443 pub tab_drop_zone_border: ColorDef,
444 #[serde(default = "default_settings_selected_bg")]
446 pub settings_selected_bg: ColorDef,
447 #[serde(default = "default_settings_selected_fg")]
449 pub settings_selected_fg: ColorDef,
450}
451
452fn default_tab_active_fg() -> ColorDef {
455 ColorDef::Named("Yellow".to_string())
456}
457fn default_tab_active_bg() -> ColorDef {
458 ColorDef::Named("Blue".to_string())
459}
460fn default_tab_inactive_fg() -> ColorDef {
461 ColorDef::Named("White".to_string())
462}
463fn default_tab_inactive_bg() -> ColorDef {
464 ColorDef::Named("DarkGray".to_string())
465}
466fn default_tab_separator_bg() -> ColorDef {
467 ColorDef::Named("Black".to_string())
468}
469fn default_tab_close_hover_fg() -> ColorDef {
470 ColorDef::Rgb(255, 100, 100) }
472fn default_tab_hover_bg() -> ColorDef {
473 ColorDef::Rgb(70, 70, 75) }
475
476fn default_menu_bg() -> ColorDef {
478 ColorDef::Rgb(60, 60, 65)
479}
480fn default_menu_fg() -> ColorDef {
481 ColorDef::Rgb(220, 220, 220)
482}
483fn default_menu_active_bg() -> ColorDef {
484 ColorDef::Rgb(60, 60, 60)
485}
486fn default_menu_active_fg() -> ColorDef {
487 ColorDef::Rgb(255, 255, 255)
488}
489fn default_menu_dropdown_bg() -> ColorDef {
490 ColorDef::Rgb(50, 50, 50)
491}
492fn default_menu_dropdown_fg() -> ColorDef {
493 ColorDef::Rgb(220, 220, 220)
494}
495fn default_menu_highlight_bg() -> ColorDef {
496 ColorDef::Rgb(70, 130, 180)
497}
498fn default_menu_highlight_fg() -> ColorDef {
499 ColorDef::Rgb(255, 255, 255)
500}
501fn default_menu_border_fg() -> ColorDef {
502 ColorDef::Rgb(100, 100, 100)
503}
504fn default_menu_separator_fg() -> ColorDef {
505 ColorDef::Rgb(80, 80, 80)
506}
507fn default_menu_hover_bg() -> ColorDef {
508 ColorDef::Rgb(55, 55, 55)
509}
510fn default_menu_hover_fg() -> ColorDef {
511 ColorDef::Rgb(255, 255, 255)
512}
513fn default_menu_disabled_fg() -> ColorDef {
514 ColorDef::Rgb(100, 100, 100) }
516fn default_menu_disabled_bg() -> ColorDef {
517 ColorDef::Rgb(50, 50, 50) }
519fn default_status_bar_fg() -> ColorDef {
521 ColorDef::Named("White".to_string())
522}
523fn default_status_bar_bg() -> ColorDef {
524 ColorDef::Named("DarkGray".to_string())
525}
526
527fn default_prompt_fg() -> ColorDef {
529 ColorDef::Named("White".to_string())
530}
531fn default_prompt_bg() -> ColorDef {
532 ColorDef::Named("Black".to_string())
533}
534fn default_prompt_selection_fg() -> ColorDef {
535 ColorDef::Named("White".to_string())
536}
537fn default_prompt_selection_bg() -> ColorDef {
538 ColorDef::Rgb(58, 79, 120)
539}
540
541fn default_popup_border_fg() -> ColorDef {
543 ColorDef::Named("Gray".to_string())
544}
545fn default_popup_bg() -> ColorDef {
546 ColorDef::Rgb(30, 30, 30)
547}
548fn default_popup_selection_bg() -> ColorDef {
549 ColorDef::Rgb(58, 79, 120)
550}
551fn default_popup_selection_fg() -> ColorDef {
552 ColorDef::Rgb(255, 255, 255) }
554fn default_popup_text_fg() -> ColorDef {
555 ColorDef::Named("White".to_string())
556}
557
558fn default_suggestion_bg() -> ColorDef {
560 ColorDef::Rgb(30, 30, 30)
561}
562fn default_suggestion_selected_bg() -> ColorDef {
563 ColorDef::Rgb(58, 79, 120)
564}
565
566fn default_help_bg() -> ColorDef {
568 ColorDef::Named("Black".to_string())
569}
570fn default_help_fg() -> ColorDef {
571 ColorDef::Named("White".to_string())
572}
573fn default_help_key_fg() -> ColorDef {
574 ColorDef::Named("Cyan".to_string())
575}
576fn default_help_separator_fg() -> ColorDef {
577 ColorDef::Named("DarkGray".to_string())
578}
579fn default_help_indicator_fg() -> ColorDef {
580 ColorDef::Named("Red".to_string())
581}
582fn default_help_indicator_bg() -> ColorDef {
583 ColorDef::Named("Black".to_string())
584}
585
586fn default_inline_code_bg() -> ColorDef {
587 ColorDef::Named("DarkGray".to_string())
588}
589
590fn default_split_separator_fg() -> ColorDef {
592 ColorDef::Rgb(100, 100, 100)
593}
594fn default_split_separator_hover_fg() -> ColorDef {
595 ColorDef::Rgb(100, 149, 237) }
597fn default_scrollbar_track_fg() -> ColorDef {
598 ColorDef::Named("DarkGray".to_string())
599}
600fn default_scrollbar_thumb_fg() -> ColorDef {
601 ColorDef::Named("Gray".to_string())
602}
603fn default_scrollbar_track_hover_fg() -> ColorDef {
604 ColorDef::Named("Gray".to_string())
605}
606fn default_scrollbar_thumb_hover_fg() -> ColorDef {
607 ColorDef::Named("White".to_string())
608}
609fn default_compose_margin_bg() -> ColorDef {
610 ColorDef::Rgb(18, 18, 18) }
612fn default_semantic_highlight_bg() -> ColorDef {
613 ColorDef::Rgb(60, 60, 80) }
615fn default_terminal_bg() -> ColorDef {
616 ColorDef::Named("Default".to_string()) }
618fn default_terminal_fg() -> ColorDef {
619 ColorDef::Named("Default".to_string()) }
621fn default_status_warning_indicator_bg() -> ColorDef {
622 ColorDef::Rgb(181, 137, 0) }
624fn default_status_warning_indicator_fg() -> ColorDef {
625 ColorDef::Rgb(0, 0, 0) }
627fn default_status_error_indicator_bg() -> ColorDef {
628 ColorDef::Rgb(220, 50, 47) }
630fn default_status_error_indicator_fg() -> ColorDef {
631 ColorDef::Rgb(255, 255, 255) }
633fn default_status_warning_indicator_hover_bg() -> ColorDef {
634 ColorDef::Rgb(211, 167, 30) }
636fn default_status_warning_indicator_hover_fg() -> ColorDef {
637 ColorDef::Rgb(0, 0, 0) }
639fn default_status_error_indicator_hover_bg() -> ColorDef {
640 ColorDef::Rgb(250, 80, 77) }
642fn default_status_error_indicator_hover_fg() -> ColorDef {
643 ColorDef::Rgb(255, 255, 255) }
645fn default_tab_drop_zone_bg() -> ColorDef {
646 ColorDef::Rgb(70, 130, 180) }
648fn default_tab_drop_zone_border() -> ColorDef {
649 ColorDef::Rgb(100, 149, 237) }
651fn default_settings_selected_bg() -> ColorDef {
652 ColorDef::Rgb(60, 60, 70) }
654fn default_settings_selected_fg() -> ColorDef {
655 ColorDef::Rgb(255, 255, 255) }
657
658#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
660pub struct SearchColors {
661 #[serde(default = "default_search_match_bg")]
663 pub match_bg: ColorDef,
664 #[serde(default = "default_search_match_fg")]
666 pub match_fg: ColorDef,
667}
668
669fn default_search_match_bg() -> ColorDef {
671 ColorDef::Rgb(100, 100, 20)
672}
673fn default_search_match_fg() -> ColorDef {
674 ColorDef::Rgb(255, 255, 255)
675}
676
677#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
679pub struct DiagnosticColors {
680 #[serde(default = "default_diagnostic_error_fg")]
682 pub error_fg: ColorDef,
683 #[serde(default = "default_diagnostic_error_bg")]
685 pub error_bg: ColorDef,
686 #[serde(default = "default_diagnostic_warning_fg")]
688 pub warning_fg: ColorDef,
689 #[serde(default = "default_diagnostic_warning_bg")]
691 pub warning_bg: ColorDef,
692 #[serde(default = "default_diagnostic_info_fg")]
694 pub info_fg: ColorDef,
695 #[serde(default = "default_diagnostic_info_bg")]
697 pub info_bg: ColorDef,
698 #[serde(default = "default_diagnostic_hint_fg")]
700 pub hint_fg: ColorDef,
701 #[serde(default = "default_diagnostic_hint_bg")]
703 pub hint_bg: ColorDef,
704}
705
706fn default_diagnostic_error_fg() -> ColorDef {
708 ColorDef::Named("Red".to_string())
709}
710fn default_diagnostic_error_bg() -> ColorDef {
711 ColorDef::Rgb(60, 20, 20)
712}
713fn default_diagnostic_warning_fg() -> ColorDef {
714 ColorDef::Named("Yellow".to_string())
715}
716fn default_diagnostic_warning_bg() -> ColorDef {
717 ColorDef::Rgb(60, 50, 0)
718}
719fn default_diagnostic_info_fg() -> ColorDef {
720 ColorDef::Named("Blue".to_string())
721}
722fn default_diagnostic_info_bg() -> ColorDef {
723 ColorDef::Rgb(0, 30, 60)
724}
725fn default_diagnostic_hint_fg() -> ColorDef {
726 ColorDef::Named("Gray".to_string())
727}
728fn default_diagnostic_hint_bg() -> ColorDef {
729 ColorDef::Rgb(30, 30, 30)
730}
731
732#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
734pub struct SyntaxColors {
735 #[serde(default = "default_syntax_keyword")]
737 pub keyword: ColorDef,
738 #[serde(default = "default_syntax_string")]
740 pub string: ColorDef,
741 #[serde(default = "default_syntax_comment")]
743 pub comment: ColorDef,
744 #[serde(default = "default_syntax_function")]
746 pub function: ColorDef,
747 #[serde(rename = "type", default = "default_syntax_type")]
749 pub type_: ColorDef,
750 #[serde(default = "default_syntax_variable")]
752 pub variable: ColorDef,
753 #[serde(default = "default_syntax_constant")]
755 pub constant: ColorDef,
756 #[serde(default = "default_syntax_operator")]
758 pub operator: ColorDef,
759}
760
761fn default_syntax_keyword() -> ColorDef {
763 ColorDef::Rgb(86, 156, 214)
764}
765fn default_syntax_string() -> ColorDef {
766 ColorDef::Rgb(206, 145, 120)
767}
768fn default_syntax_comment() -> ColorDef {
769 ColorDef::Rgb(106, 153, 85)
770}
771fn default_syntax_function() -> ColorDef {
772 ColorDef::Rgb(220, 220, 170)
773}
774fn default_syntax_type() -> ColorDef {
775 ColorDef::Rgb(78, 201, 176)
776}
777fn default_syntax_variable() -> ColorDef {
778 ColorDef::Rgb(156, 220, 254)
779}
780fn default_syntax_constant() -> ColorDef {
781 ColorDef::Rgb(79, 193, 255)
782}
783fn default_syntax_operator() -> ColorDef {
784 ColorDef::Rgb(212, 212, 212)
785}
786
787#[derive(Debug, Clone)]
789pub struct Theme {
790 pub name: String,
792
793 pub editor_bg: Color,
795 pub editor_fg: Color,
796 pub cursor: Color,
797 pub inactive_cursor: Color,
798 pub selection_bg: Color,
799 pub current_line_bg: Color,
800 pub line_number_fg: Color,
801 pub line_number_bg: Color,
802
803 pub diff_add_bg: Color,
805 pub diff_remove_bg: Color,
806 pub diff_modify_bg: Color,
807 pub diff_add_highlight_bg: Color,
809 pub diff_remove_highlight_bg: Color,
811
812 pub tab_active_fg: Color,
814 pub tab_active_bg: Color,
815 pub tab_inactive_fg: Color,
816 pub tab_inactive_bg: Color,
817 pub tab_separator_bg: Color,
818 pub tab_close_hover_fg: Color,
819 pub tab_hover_bg: Color,
820
821 pub menu_bg: Color,
823 pub menu_fg: Color,
824 pub menu_active_bg: Color,
825 pub menu_active_fg: Color,
826 pub menu_dropdown_bg: Color,
827 pub menu_dropdown_fg: Color,
828 pub menu_highlight_bg: Color,
829 pub menu_highlight_fg: Color,
830 pub menu_border_fg: Color,
831 pub menu_separator_fg: Color,
832 pub menu_hover_bg: Color,
833 pub menu_hover_fg: Color,
834 pub menu_disabled_fg: Color,
835 pub menu_disabled_bg: Color,
836
837 pub status_bar_fg: Color,
838 pub status_bar_bg: Color,
839 pub prompt_fg: Color,
840 pub prompt_bg: Color,
841 pub prompt_selection_fg: Color,
842 pub prompt_selection_bg: Color,
843
844 pub popup_border_fg: Color,
845 pub popup_bg: Color,
846 pub popup_selection_bg: Color,
847 pub popup_selection_fg: Color,
848 pub popup_text_fg: Color,
849
850 pub suggestion_bg: Color,
851 pub suggestion_selected_bg: Color,
852
853 pub help_bg: Color,
854 pub help_fg: Color,
855 pub help_key_fg: Color,
856 pub help_separator_fg: Color,
857
858 pub help_indicator_fg: Color,
859 pub help_indicator_bg: Color,
860
861 pub inline_code_bg: Color,
863
864 pub split_separator_fg: Color,
865 pub split_separator_hover_fg: Color,
866
867 pub scrollbar_track_fg: Color,
869 pub scrollbar_thumb_fg: Color,
870 pub scrollbar_track_hover_fg: Color,
871 pub scrollbar_thumb_hover_fg: Color,
872
873 pub compose_margin_bg: Color,
875
876 pub semantic_highlight_bg: Color,
878
879 pub terminal_bg: Color,
881 pub terminal_fg: Color,
882
883 pub status_warning_indicator_bg: Color,
885 pub status_warning_indicator_fg: Color,
886 pub status_error_indicator_bg: Color,
887 pub status_error_indicator_fg: Color,
888 pub status_warning_indicator_hover_bg: Color,
889 pub status_warning_indicator_hover_fg: Color,
890 pub status_error_indicator_hover_bg: Color,
891 pub status_error_indicator_hover_fg: Color,
892
893 pub tab_drop_zone_bg: Color,
895 pub tab_drop_zone_border: Color,
896
897 pub settings_selected_bg: Color,
899 pub settings_selected_fg: Color,
900
901 pub search_match_bg: Color,
903 pub search_match_fg: Color,
904
905 pub diagnostic_error_fg: Color,
907 pub diagnostic_error_bg: Color,
908 pub diagnostic_warning_fg: Color,
909 pub diagnostic_warning_bg: Color,
910 pub diagnostic_info_fg: Color,
911 pub diagnostic_info_bg: Color,
912 pub diagnostic_hint_fg: Color,
913 pub diagnostic_hint_bg: Color,
914
915 pub syntax_keyword: Color,
917 pub syntax_string: Color,
918 pub syntax_comment: Color,
919 pub syntax_function: Color,
920 pub syntax_type: Color,
921 pub syntax_variable: Color,
922 pub syntax_constant: Color,
923 pub syntax_operator: Color,
924}
925
926impl From<ThemeFile> for Theme {
927 fn from(file: ThemeFile) -> Self {
928 Self {
929 name: file.name,
930 editor_bg: file.editor.bg.into(),
931 editor_fg: file.editor.fg.into(),
932 cursor: file.editor.cursor.into(),
933 inactive_cursor: file.editor.inactive_cursor.into(),
934 selection_bg: file.editor.selection_bg.into(),
935 current_line_bg: file.editor.current_line_bg.into(),
936 line_number_fg: file.editor.line_number_fg.into(),
937 line_number_bg: file.editor.line_number_bg.into(),
938 diff_add_bg: file.editor.diff_add_bg.clone().into(),
939 diff_remove_bg: file.editor.diff_remove_bg.clone().into(),
940 diff_modify_bg: file.editor.diff_modify_bg.into(),
941 diff_add_highlight_bg: brighten_color(file.editor.diff_add_bg.into(), 40),
943 diff_remove_highlight_bg: brighten_color(file.editor.diff_remove_bg.into(), 40),
944 tab_active_fg: file.ui.tab_active_fg.into(),
945 tab_active_bg: file.ui.tab_active_bg.into(),
946 tab_inactive_fg: file.ui.tab_inactive_fg.into(),
947 tab_inactive_bg: file.ui.tab_inactive_bg.into(),
948 tab_separator_bg: file.ui.tab_separator_bg.into(),
949 tab_close_hover_fg: file.ui.tab_close_hover_fg.into(),
950 tab_hover_bg: file.ui.tab_hover_bg.into(),
951 menu_bg: file.ui.menu_bg.into(),
952 menu_fg: file.ui.menu_fg.into(),
953 menu_active_bg: file.ui.menu_active_bg.into(),
954 menu_active_fg: file.ui.menu_active_fg.into(),
955 menu_dropdown_bg: file.ui.menu_dropdown_bg.into(),
956 menu_dropdown_fg: file.ui.menu_dropdown_fg.into(),
957 menu_highlight_bg: file.ui.menu_highlight_bg.into(),
958 menu_highlight_fg: file.ui.menu_highlight_fg.into(),
959 menu_border_fg: file.ui.menu_border_fg.into(),
960 menu_separator_fg: file.ui.menu_separator_fg.into(),
961 menu_hover_bg: file.ui.menu_hover_bg.into(),
962 menu_hover_fg: file.ui.menu_hover_fg.into(),
963 menu_disabled_fg: file.ui.menu_disabled_fg.into(),
964 menu_disabled_bg: file.ui.menu_disabled_bg.into(),
965 status_bar_fg: file.ui.status_bar_fg.into(),
966 status_bar_bg: file.ui.status_bar_bg.into(),
967 prompt_fg: file.ui.prompt_fg.into(),
968 prompt_bg: file.ui.prompt_bg.into(),
969 prompt_selection_fg: file.ui.prompt_selection_fg.into(),
970 prompt_selection_bg: file.ui.prompt_selection_bg.into(),
971 popup_border_fg: file.ui.popup_border_fg.into(),
972 popup_bg: file.ui.popup_bg.into(),
973 popup_selection_bg: file.ui.popup_selection_bg.into(),
974 popup_selection_fg: file.ui.popup_selection_fg.into(),
975 popup_text_fg: file.ui.popup_text_fg.into(),
976 suggestion_bg: file.ui.suggestion_bg.into(),
977 suggestion_selected_bg: file.ui.suggestion_selected_bg.into(),
978 help_bg: file.ui.help_bg.into(),
979 help_fg: file.ui.help_fg.into(),
980 help_key_fg: file.ui.help_key_fg.into(),
981 help_separator_fg: file.ui.help_separator_fg.into(),
982 help_indicator_fg: file.ui.help_indicator_fg.into(),
983 help_indicator_bg: file.ui.help_indicator_bg.into(),
984 inline_code_bg: file.ui.inline_code_bg.into(),
985 split_separator_fg: file.ui.split_separator_fg.into(),
986 split_separator_hover_fg: file.ui.split_separator_hover_fg.into(),
987 scrollbar_track_fg: file.ui.scrollbar_track_fg.into(),
988 scrollbar_thumb_fg: file.ui.scrollbar_thumb_fg.into(),
989 scrollbar_track_hover_fg: file.ui.scrollbar_track_hover_fg.into(),
990 scrollbar_thumb_hover_fg: file.ui.scrollbar_thumb_hover_fg.into(),
991 compose_margin_bg: file.ui.compose_margin_bg.into(),
992 semantic_highlight_bg: file.ui.semantic_highlight_bg.into(),
993 terminal_bg: file.ui.terminal_bg.into(),
994 terminal_fg: file.ui.terminal_fg.into(),
995 status_warning_indicator_bg: file.ui.status_warning_indicator_bg.into(),
996 status_warning_indicator_fg: file.ui.status_warning_indicator_fg.into(),
997 status_error_indicator_bg: file.ui.status_error_indicator_bg.into(),
998 status_error_indicator_fg: file.ui.status_error_indicator_fg.into(),
999 status_warning_indicator_hover_bg: file.ui.status_warning_indicator_hover_bg.into(),
1000 status_warning_indicator_hover_fg: file.ui.status_warning_indicator_hover_fg.into(),
1001 status_error_indicator_hover_bg: file.ui.status_error_indicator_hover_bg.into(),
1002 status_error_indicator_hover_fg: file.ui.status_error_indicator_hover_fg.into(),
1003 tab_drop_zone_bg: file.ui.tab_drop_zone_bg.into(),
1004 tab_drop_zone_border: file.ui.tab_drop_zone_border.into(),
1005 settings_selected_bg: file.ui.settings_selected_bg.into(),
1006 settings_selected_fg: file.ui.settings_selected_fg.into(),
1007 search_match_bg: file.search.match_bg.into(),
1008 search_match_fg: file.search.match_fg.into(),
1009 diagnostic_error_fg: file.diagnostic.error_fg.into(),
1010 diagnostic_error_bg: file.diagnostic.error_bg.into(),
1011 diagnostic_warning_fg: file.diagnostic.warning_fg.into(),
1012 diagnostic_warning_bg: file.diagnostic.warning_bg.into(),
1013 diagnostic_info_fg: file.diagnostic.info_fg.into(),
1014 diagnostic_info_bg: file.diagnostic.info_bg.into(),
1015 diagnostic_hint_fg: file.diagnostic.hint_fg.into(),
1016 diagnostic_hint_bg: file.diagnostic.hint_bg.into(),
1017 syntax_keyword: file.syntax.keyword.into(),
1018 syntax_string: file.syntax.string.into(),
1019 syntax_comment: file.syntax.comment.into(),
1020 syntax_function: file.syntax.function.into(),
1021 syntax_type: file.syntax.type_.into(),
1022 syntax_variable: file.syntax.variable.into(),
1023 syntax_constant: file.syntax.constant.into(),
1024 syntax_operator: file.syntax.operator.into(),
1025 }
1026 }
1027}
1028
1029impl From<Theme> for ThemeFile {
1030 fn from(theme: Theme) -> Self {
1031 Self {
1032 name: theme.name,
1033 editor: EditorColors {
1034 bg: theme.editor_bg.into(),
1035 fg: theme.editor_fg.into(),
1036 cursor: theme.cursor.into(),
1037 inactive_cursor: theme.inactive_cursor.into(),
1038 selection_bg: theme.selection_bg.into(),
1039 current_line_bg: theme.current_line_bg.into(),
1040 line_number_fg: theme.line_number_fg.into(),
1041 line_number_bg: theme.line_number_bg.into(),
1042 diff_add_bg: theme.diff_add_bg.into(),
1043 diff_remove_bg: theme.diff_remove_bg.into(),
1044 diff_modify_bg: theme.diff_modify_bg.into(),
1045 },
1046 ui: UiColors {
1047 tab_active_fg: theme.tab_active_fg.into(),
1048 tab_active_bg: theme.tab_active_bg.into(),
1049 tab_inactive_fg: theme.tab_inactive_fg.into(),
1050 tab_inactive_bg: theme.tab_inactive_bg.into(),
1051 tab_separator_bg: theme.tab_separator_bg.into(),
1052 tab_close_hover_fg: theme.tab_close_hover_fg.into(),
1053 tab_hover_bg: theme.tab_hover_bg.into(),
1054 menu_bg: theme.menu_bg.into(),
1055 menu_fg: theme.menu_fg.into(),
1056 menu_active_bg: theme.menu_active_bg.into(),
1057 menu_active_fg: theme.menu_active_fg.into(),
1058 menu_dropdown_bg: theme.menu_dropdown_bg.into(),
1059 menu_dropdown_fg: theme.menu_dropdown_fg.into(),
1060 menu_highlight_bg: theme.menu_highlight_bg.into(),
1061 menu_highlight_fg: theme.menu_highlight_fg.into(),
1062 menu_border_fg: theme.menu_border_fg.into(),
1063 menu_separator_fg: theme.menu_separator_fg.into(),
1064 menu_hover_bg: theme.menu_hover_bg.into(),
1065 menu_hover_fg: theme.menu_hover_fg.into(),
1066 menu_disabled_fg: theme.menu_disabled_fg.into(),
1067 menu_disabled_bg: theme.menu_disabled_bg.into(),
1068 status_bar_fg: theme.status_bar_fg.into(),
1069 status_bar_bg: theme.status_bar_bg.into(),
1070 prompt_fg: theme.prompt_fg.into(),
1071 prompt_bg: theme.prompt_bg.into(),
1072 prompt_selection_fg: theme.prompt_selection_fg.into(),
1073 prompt_selection_bg: theme.prompt_selection_bg.into(),
1074 popup_border_fg: theme.popup_border_fg.into(),
1075 popup_bg: theme.popup_bg.into(),
1076 popup_selection_bg: theme.popup_selection_bg.into(),
1077 popup_selection_fg: theme.popup_selection_fg.into(),
1078 popup_text_fg: theme.popup_text_fg.into(),
1079 suggestion_bg: theme.suggestion_bg.into(),
1080 suggestion_selected_bg: theme.suggestion_selected_bg.into(),
1081 help_bg: theme.help_bg.into(),
1082 help_fg: theme.help_fg.into(),
1083 help_key_fg: theme.help_key_fg.into(),
1084 help_separator_fg: theme.help_separator_fg.into(),
1085 help_indicator_fg: theme.help_indicator_fg.into(),
1086 help_indicator_bg: theme.help_indicator_bg.into(),
1087 inline_code_bg: theme.inline_code_bg.into(),
1088 split_separator_fg: theme.split_separator_fg.into(),
1089 split_separator_hover_fg: theme.split_separator_hover_fg.into(),
1090 scrollbar_track_fg: theme.scrollbar_track_fg.into(),
1091 scrollbar_thumb_fg: theme.scrollbar_thumb_fg.into(),
1092 scrollbar_track_hover_fg: theme.scrollbar_track_hover_fg.into(),
1093 scrollbar_thumb_hover_fg: theme.scrollbar_thumb_hover_fg.into(),
1094 compose_margin_bg: theme.compose_margin_bg.into(),
1095 semantic_highlight_bg: theme.semantic_highlight_bg.into(),
1096 terminal_bg: theme.terminal_bg.into(),
1097 terminal_fg: theme.terminal_fg.into(),
1098 status_warning_indicator_bg: theme.status_warning_indicator_bg.into(),
1099 status_warning_indicator_fg: theme.status_warning_indicator_fg.into(),
1100 status_error_indicator_bg: theme.status_error_indicator_bg.into(),
1101 status_error_indicator_fg: theme.status_error_indicator_fg.into(),
1102 status_warning_indicator_hover_bg: theme.status_warning_indicator_hover_bg.into(),
1103 status_warning_indicator_hover_fg: theme.status_warning_indicator_hover_fg.into(),
1104 status_error_indicator_hover_bg: theme.status_error_indicator_hover_bg.into(),
1105 status_error_indicator_hover_fg: theme.status_error_indicator_hover_fg.into(),
1106 tab_drop_zone_bg: theme.tab_drop_zone_bg.into(),
1107 tab_drop_zone_border: theme.tab_drop_zone_border.into(),
1108 settings_selected_bg: theme.settings_selected_bg.into(),
1109 settings_selected_fg: theme.settings_selected_fg.into(),
1110 },
1111 search: SearchColors {
1112 match_bg: theme.search_match_bg.into(),
1113 match_fg: theme.search_match_fg.into(),
1114 },
1115 diagnostic: DiagnosticColors {
1116 error_fg: theme.diagnostic_error_fg.into(),
1117 error_bg: theme.diagnostic_error_bg.into(),
1118 warning_fg: theme.diagnostic_warning_fg.into(),
1119 warning_bg: theme.diagnostic_warning_bg.into(),
1120 info_fg: theme.diagnostic_info_fg.into(),
1121 info_bg: theme.diagnostic_info_bg.into(),
1122 hint_fg: theme.diagnostic_hint_fg.into(),
1123 hint_bg: theme.diagnostic_hint_bg.into(),
1124 },
1125 syntax: SyntaxColors {
1126 keyword: theme.syntax_keyword.into(),
1127 string: theme.syntax_string.into(),
1128 comment: theme.syntax_comment.into(),
1129 function: theme.syntax_function.into(),
1130 type_: theme.syntax_type.into(),
1131 variable: theme.syntax_variable.into(),
1132 constant: theme.syntax_constant.into(),
1133 operator: theme.syntax_operator.into(),
1134 },
1135 }
1136 }
1137}
1138
1139impl Theme {
1140 pub fn load_builtin(name: &str) -> Option<Self> {
1142 BUILTIN_THEMES
1143 .iter()
1144 .find(|t| t.name == name)
1145 .and_then(|t| serde_json::from_str::<ThemeFile>(t.json).ok())
1146 .map(|tf| tf.into())
1147 }
1148
1149 pub fn from_json(json: &str) -> Result<Self, String> {
1151 let theme_file: ThemeFile =
1152 serde_json::from_str(json).map_err(|e| format!("Failed to parse theme JSON: {}", e))?;
1153 Ok(theme_file.into())
1154 }
1155
1156 pub fn resolve_theme_key(&self, key: &str) -> Option<Color> {
1167 let parts: Vec<&str> = key.split('.').collect();
1169 if parts.len() != 2 {
1170 return None;
1171 }
1172
1173 let (section, field) = (parts[0], parts[1]);
1174
1175 match section {
1176 "editor" => match field {
1177 "bg" => Some(self.editor_bg),
1178 "fg" => Some(self.editor_fg),
1179 "cursor" => Some(self.cursor),
1180 "inactive_cursor" => Some(self.inactive_cursor),
1181 "selection_bg" => Some(self.selection_bg),
1182 "current_line_bg" => Some(self.current_line_bg),
1183 "line_number_fg" => Some(self.line_number_fg),
1184 "line_number_bg" => Some(self.line_number_bg),
1185 "diff_add_bg" => Some(self.diff_add_bg),
1186 "diff_remove_bg" => Some(self.diff_remove_bg),
1187 "diff_modify_bg" => Some(self.diff_modify_bg),
1188 _ => None,
1189 },
1190 "ui" => match field {
1191 "tab_active_fg" => Some(self.tab_active_fg),
1192 "tab_active_bg" => Some(self.tab_active_bg),
1193 "tab_inactive_fg" => Some(self.tab_inactive_fg),
1194 "tab_inactive_bg" => Some(self.tab_inactive_bg),
1195 "status_bar_fg" => Some(self.status_bar_fg),
1196 "status_bar_bg" => Some(self.status_bar_bg),
1197 "prompt_fg" => Some(self.prompt_fg),
1198 "prompt_bg" => Some(self.prompt_bg),
1199 "prompt_selection_fg" => Some(self.prompt_selection_fg),
1200 "prompt_selection_bg" => Some(self.prompt_selection_bg),
1201 "popup_bg" => Some(self.popup_bg),
1202 "popup_border_fg" => Some(self.popup_border_fg),
1203 "popup_selection_bg" => Some(self.popup_selection_bg),
1204 "popup_selection_fg" => Some(self.popup_selection_fg),
1205 "popup_text_fg" => Some(self.popup_text_fg),
1206 "menu_bg" => Some(self.menu_bg),
1207 "menu_fg" => Some(self.menu_fg),
1208 "menu_active_bg" => Some(self.menu_active_bg),
1209 "menu_active_fg" => Some(self.menu_active_fg),
1210 "help_bg" => Some(self.help_bg),
1211 "help_fg" => Some(self.help_fg),
1212 "help_key_fg" => Some(self.help_key_fg),
1213 "split_separator_fg" => Some(self.split_separator_fg),
1214 "scrollbar_thumb_fg" => Some(self.scrollbar_thumb_fg),
1215 "semantic_highlight_bg" => Some(self.semantic_highlight_bg),
1216 _ => None,
1217 },
1218 "syntax" => match field {
1219 "keyword" => Some(self.syntax_keyword),
1220 "string" => Some(self.syntax_string),
1221 "comment" => Some(self.syntax_comment),
1222 "function" => Some(self.syntax_function),
1223 "type" => Some(self.syntax_type),
1224 "variable" => Some(self.syntax_variable),
1225 "constant" => Some(self.syntax_constant),
1226 "operator" => Some(self.syntax_operator),
1227 _ => None,
1228 },
1229 "diagnostic" => match field {
1230 "error_fg" => Some(self.diagnostic_error_fg),
1231 "error_bg" => Some(self.diagnostic_error_bg),
1232 "warning_fg" => Some(self.diagnostic_warning_fg),
1233 "warning_bg" => Some(self.diagnostic_warning_bg),
1234 "info_fg" => Some(self.diagnostic_info_fg),
1235 "info_bg" => Some(self.diagnostic_info_bg),
1236 "hint_fg" => Some(self.diagnostic_hint_fg),
1237 "hint_bg" => Some(self.diagnostic_hint_bg),
1238 _ => None,
1239 },
1240 "search" => match field {
1241 "match_bg" => Some(self.search_match_bg),
1242 "match_fg" => Some(self.search_match_fg),
1243 _ => None,
1244 },
1245 _ => None,
1246 }
1247 }
1248}
1249
1250pub fn get_theme_schema() -> serde_json::Value {
1258 use schemars::schema_for;
1259 let schema = schema_for!(ThemeFile);
1260 serde_json::to_value(&schema).unwrap_or_default()
1261}
1262
1263pub fn get_builtin_themes() -> serde_json::Value {
1265 let mut map = serde_json::Map::new();
1266 for theme in BUILTIN_THEMES {
1267 map.insert(
1268 theme.name.to_string(),
1269 serde_json::Value::String(theme.json.to_string()),
1270 );
1271 }
1272 serde_json::Value::Object(map)
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277 use super::*;
1278
1279 #[test]
1280 fn test_load_builtin_theme() {
1281 let dark = Theme::load_builtin(THEME_DARK).expect("Dark theme must exist");
1282 assert_eq!(dark.name, THEME_DARK);
1283
1284 let light = Theme::load_builtin(THEME_LIGHT).expect("Light theme must exist");
1285 assert_eq!(light.name, THEME_LIGHT);
1286
1287 let high_contrast =
1288 Theme::load_builtin(THEME_HIGH_CONTRAST).expect("High contrast theme must exist");
1289 assert_eq!(high_contrast.name, THEME_HIGH_CONTRAST);
1290 }
1291
1292 #[test]
1293 fn test_builtin_themes_match_schema() {
1294 for theme in BUILTIN_THEMES {
1295 let _: ThemeFile = serde_json::from_str(theme.json)
1296 .unwrap_or_else(|_| panic!("Theme '{}' does not match schema", theme.name));
1297 }
1298 }
1299
1300 #[test]
1301 fn test_from_json() {
1302 let json = r#"{"name":"test","editor":{},"ui":{},"search":{},"diagnostic":{},"syntax":{}}"#;
1303 let theme = Theme::from_json(json).expect("Should parse minimal theme");
1304 assert_eq!(theme.name, "test");
1305 }
1306
1307 #[test]
1308 fn test_default_reset_color() {
1309 let color: Color = ColorDef::Named("Default".to_string()).into();
1311 assert_eq!(color, Color::Reset);
1312
1313 let color: Color = ColorDef::Named("Reset".to_string()).into();
1315 assert_eq!(color, Color::Reset);
1316 }
1317}