1use ratatui::style::Color;
10
11#[derive(Clone, Debug, PartialEq)]
15pub struct Theme {
16 pub name: &'static str,
17 pub status_bg: Color,
19 pub status_fg: Color,
20 pub status_mode_fg: Color,
21 pub status_cost_fg: Color,
22 pub status_dim_fg: Color,
23 pub input_border: Color,
25 pub input_prompt_fg: Color,
26 pub input_bash_fg: Color,
27 pub input_note_fg: Color,
28 pub input_command_fg: Color,
29 pub input_atfile_fg: Color,
30 pub user_bar: Color,
32 pub assistant_bar: Color,
33 pub system_bar: Color,
34 pub tool_call_icon: Color,
35 pub tool_ok_fg: Color,
36 pub tool_err_fg: Color,
37 pub code_fg: Color,
38 pub diff_add: Color,
39 pub diff_del: Color,
40}
41
42pub const DARK: Theme = Theme {
45 name: "dark",
46 status_bg: Color::DarkGray,
47 status_fg: Color::White,
48 status_mode_fg: Color::Green,
49 status_cost_fg: Color::Cyan,
50 status_dim_fg: Color::DarkGray,
51 input_border: Color::DarkGray,
52 input_prompt_fg: Color::Cyan,
53 input_bash_fg: Color::Yellow,
54 input_note_fg: Color::Green,
55 input_command_fg: Color::Magenta,
56 input_atfile_fg: Color::Cyan,
57 user_bar: Color::Blue,
58 assistant_bar: Color::Green,
59 system_bar: Color::DarkGray,
60 tool_call_icon: Color::Yellow,
61 tool_ok_fg: Color::Green,
62 tool_err_fg: Color::Red,
63 code_fg: Color::Cyan,
64 diff_add: Color::Green,
65 diff_del: Color::Red,
66};
67
68pub const LIGHT: Theme = Theme {
69 name: "light",
70 status_bg: Color::Gray,
71 status_fg: Color::Black,
72 status_mode_fg: Color::Blue,
73 status_cost_fg: Color::DarkGray,
74 status_dim_fg: Color::Gray,
75 input_border: Color::Gray,
76 input_prompt_fg: Color::Blue,
77 input_bash_fg: Color::Rgb(180, 100, 0),
78 input_note_fg: Color::Rgb(0, 120, 0),
79 input_command_fg: Color::Rgb(100, 0, 150),
80 input_atfile_fg: Color::Blue,
81 user_bar: Color::Blue,
82 assistant_bar: Color::Rgb(0, 130, 0),
83 system_bar: Color::Gray,
84 tool_call_icon: Color::Rgb(180, 100, 0),
85 tool_ok_fg: Color::Rgb(0, 130, 0),
86 tool_err_fg: Color::Red,
87 code_fg: Color::Blue,
88 diff_add: Color::Rgb(0, 130, 0),
89 diff_del: Color::Red,
90};
91
92pub const SOLARIZED: Theme = Theme {
93 name: "solarized",
94 status_bg: Color::Rgb(7, 54, 66),
95 status_fg: Color::Rgb(131, 148, 150),
96 status_mode_fg: Color::Rgb(38, 139, 210),
97 status_cost_fg: Color::Rgb(42, 161, 152),
98 status_dim_fg: Color::Rgb(88, 110, 117),
99 input_border: Color::Rgb(88, 110, 117),
100 input_prompt_fg: Color::Rgb(42, 161, 152),
101 input_bash_fg: Color::Rgb(181, 137, 0),
102 input_note_fg: Color::Rgb(133, 153, 0),
103 input_command_fg: Color::Rgb(108, 113, 196),
104 input_atfile_fg: Color::Rgb(42, 161, 152),
105 user_bar: Color::Rgb(38, 139, 210),
106 assistant_bar: Color::Rgb(133, 153, 0),
107 system_bar: Color::Rgb(88, 110, 117),
108 tool_call_icon: Color::Rgb(181, 137, 0),
109 tool_ok_fg: Color::Rgb(133, 153, 0),
110 tool_err_fg: Color::Rgb(220, 50, 47),
111 code_fg: Color::Rgb(42, 161, 152),
112 diff_add: Color::Rgb(133, 153, 0),
113 diff_del: Color::Rgb(220, 50, 47),
114};
115
116pub const ALL_THEMES: &[&Theme] = &[&DARK, &LIGHT, &SOLARIZED];
117
118pub fn find_theme(name: &str) -> &'static Theme {
120 ALL_THEMES
121 .iter()
122 .copied()
123 .find(|t| t.name == name)
124 .unwrap_or(&DARK)
125}
126
127#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn dark_theme_has_expected_name() {
135 assert_eq!(DARK.name, "dark");
136 }
137
138 #[test]
139 fn find_theme_returns_dark_for_unknown() {
140 assert_eq!(find_theme("nope").name, "dark");
141 assert_eq!(find_theme("").name, "dark");
142 }
143
144 #[test]
145 fn all_themes_have_unique_names() {
146 let names: Vec<&str> = ALL_THEMES.iter().map(|t| t.name).collect();
147 let unique: std::collections::HashSet<&&str> = names.iter().collect();
148 assert_eq!(names.len(), unique.len(), "duplicate theme names");
149 }
150
151 #[test]
152 fn find_theme_returns_correct_theme() {
153 assert_eq!(find_theme("light").name, "light");
154 assert_eq!(find_theme("solarized").name, "solarized");
155 assert_eq!(find_theme("dark").name, "dark");
156 }
157}