1use ratatui::style::{Color, Modifier, Style};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum ThemeVariant {
11 #[default]
12 Dark,
13 Light,
14}
15
16#[derive(Debug, Clone)]
18pub struct Theme {
19 pub variant: ThemeVariant,
21
22 pub background: Color,
24 pub foreground: Color,
25 pub muted: Color,
26
27 pub selected: Style,
29 pub hover: Style,
30
31 pub size_huge: Color, pub size_large: Color, pub size_medium: Color, pub size_small: Color, pub size_tiny: Color, pub success: Color,
40 pub warning: Color,
41 pub error: Color,
42 pub info: Color,
43
44 pub border: Style,
46 pub title: Style,
47 pub help_key: Style,
48 pub help_desc: Style,
49
50 pub tree_lines: Style,
52 pub directory: Style,
53 pub file: Style,
54 pub symlink: Style,
55 pub executable: Style,
56
57 pub progress_bar: Style,
59 pub progress_bg: Style,
60
61 pub header: Style,
63 pub footer: Style,
64
65 pub help_key_bg: Color,
67 pub help_key_fg: Color,
68 pub help_desc_fg: Color,
69 pub footer_bg: Color,
70
71 pub command_prompt: Style,
73 pub command_input: Style,
74 pub command_cursor: Style,
75
76 pub marked: Style,
78}
79
80impl Theme {
81 pub fn dark() -> Self {
83 let slate_50 = Color::Rgb(248, 250, 252);
85 let slate_100 = Color::Rgb(241, 245, 249);
86 let slate_300 = Color::Rgb(203, 213, 225);
87 let slate_400 = Color::Rgb(148, 163, 184);
88 let slate_500 = Color::Rgb(100, 116, 139);
89 let slate_600 = Color::Rgb(71, 85, 105);
90 let slate_700 = Color::Rgb(51, 65, 85);
91 let slate_800 = Color::Rgb(30, 41, 59);
92 let slate_900 = Color::Rgb(15, 23, 42);
93
94 let blue_400 = Color::Rgb(96, 165, 250);
96 let blue_500 = Color::Rgb(59, 130, 246);
97 let green_500 = Color::Rgb(34, 197, 94);
98 let yellow_500 = Color::Rgb(234, 179, 8);
99 let orange_500 = Color::Rgb(249, 115, 22);
100 let red_500 = Color::Rgb(239, 68, 68);
101 let cyan_400 = Color::Rgb(34, 211, 238);
102 let amber_500 = Color::Rgb(245, 158, 11);
103
104 Self {
105 variant: ThemeVariant::Dark,
106 background: slate_900,
107 foreground: slate_100,
108 muted: slate_500,
109
110 selected: Style::new().bg(slate_700).fg(slate_50).add_modifier(Modifier::BOLD),
111 hover: Style::new().bg(slate_800),
112
113 size_huge: red_500,
114 size_large: orange_500,
115 size_medium: yellow_500,
116 size_small: green_500,
117 size_tiny: slate_600,
118
119 success: green_500,
120 warning: yellow_500,
121 error: red_500,
122 info: blue_400,
123
124 border: Style::new().fg(slate_600),
125 title: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
126 help_key: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
127 help_desc: Style::new().fg(slate_400),
128
129 tree_lines: Style::new().fg(slate_600),
130 directory: Style::new().fg(blue_500).add_modifier(Modifier::BOLD),
131 file: Style::new().fg(slate_300),
132 symlink: Style::new().fg(cyan_400),
133 executable: Style::new().fg(green_500),
134
135 progress_bar: Style::new().fg(blue_500),
136 progress_bg: Style::new().fg(slate_700),
137
138 header: Style::new().bg(slate_800).fg(slate_100),
139 footer: Style::new().bg(slate_800).fg(slate_400),
140
141 help_key_bg: slate_700,
143 help_key_fg: blue_400,
144 help_desc_fg: slate_400,
145 footer_bg: slate_800,
146
147 command_prompt: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
148 command_input: Style::new().fg(slate_100),
149 command_cursor: Style::new().add_modifier(Modifier::REVERSED),
150
151 marked: Style::new().fg(amber_500).add_modifier(Modifier::BOLD),
152 }
153 }
154
155 pub fn light() -> Self {
157 let slate_50 = Color::Rgb(248, 250, 252);
159 let slate_100 = Color::Rgb(241, 245, 249);
160 let slate_200 = Color::Rgb(226, 232, 240);
161 let slate_400 = Color::Rgb(148, 163, 184);
162 let slate_500 = Color::Rgb(100, 116, 139);
163 let slate_600 = Color::Rgb(71, 85, 105);
164 let slate_700 = Color::Rgb(51, 65, 85);
165 let slate_800 = Color::Rgb(30, 41, 59);
166 let slate_900 = Color::Rgb(15, 23, 42);
167
168 let blue_600 = Color::Rgb(37, 99, 235);
170 let blue_700 = Color::Rgb(29, 78, 216);
171 let green_600 = Color::Rgb(22, 163, 74);
172 let yellow_600 = Color::Rgb(202, 138, 4);
173 let orange_600 = Color::Rgb(234, 88, 12);
174 let red_600 = Color::Rgb(220, 38, 38);
175 let cyan_600 = Color::Rgb(8, 145, 178);
176 let amber_600 = Color::Rgb(217, 119, 6);
177
178 Self {
179 variant: ThemeVariant::Light,
180 background: slate_50,
181 foreground: slate_900,
182 muted: slate_500,
183
184 selected: Style::new().bg(slate_200).fg(slate_900).add_modifier(Modifier::BOLD),
185 hover: Style::new().bg(slate_100),
186
187 size_huge: red_600,
188 size_large: orange_600,
189 size_medium: yellow_600,
190 size_small: green_600,
191 size_tiny: slate_400,
192
193 success: green_600,
194 warning: yellow_600,
195 error: red_600,
196 info: blue_600,
197
198 border: Style::new().fg(slate_400),
199 title: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
200 help_key: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
201 help_desc: Style::new().fg(slate_600),
202
203 tree_lines: Style::new().fg(slate_400),
204 directory: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
205 file: Style::new().fg(slate_700),
206 symlink: Style::new().fg(cyan_600),
207 executable: Style::new().fg(green_600),
208
209 progress_bar: Style::new().fg(blue_600),
210 progress_bg: Style::new().fg(slate_200),
211
212 header: Style::new().bg(slate_100).fg(slate_800),
213 footer: Style::new().bg(slate_100).fg(slate_600),
214
215 help_key_bg: slate_200,
217 help_key_fg: blue_700,
218 help_desc_fg: slate_600,
219 footer_bg: slate_100,
220
221 command_prompt: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
222 command_input: Style::new().fg(slate_900),
223 command_cursor: Style::new().add_modifier(Modifier::REVERSED),
224
225 marked: Style::new().fg(amber_600).add_modifier(Modifier::BOLD),
226 }
227 }
228
229 pub fn from_variant(variant: ThemeVariant) -> Self {
231 match variant {
232 ThemeVariant::Dark => Self::dark(),
233 ThemeVariant::Light => Self::light(),
234 }
235 }
236
237 pub fn toggle(&self) -> Self {
239 match self.variant {
240 ThemeVariant::Dark => Self::light(),
241 ThemeVariant::Light => Self::dark(),
242 }
243 }
244
245 pub fn size_color(&self, ratio: f64) -> Color {
247 match ratio {
248 r if r > 0.50 => self.size_huge,
249 r if r > 0.25 => self.size_large,
250 r if r > 0.10 => self.size_medium,
251 r if r > 0.01 => self.size_small,
252 _ => self.size_tiny,
253 }
254 }
255
256 pub fn size_bar_style(&self, ratio: f64) -> Style {
258 Style::new().fg(self.size_color(ratio))
259 }
260
261 pub fn help_key_style(&self) -> Style {
263 Style::new()
264 .bg(self.help_key_bg)
265 .fg(self.help_key_fg)
266 .add_modifier(Modifier::BOLD)
267 }
268
269 pub fn help_desc_style(&self) -> Style {
271 Style::new().fg(self.help_desc_fg)
272 }
273
274 pub fn footer_style(&self) -> Style {
276 Style::new().bg(self.footer_bg)
277 }
278}
279
280impl Default for Theme {
281 fn default() -> Self {
282 Self::dark()
283 }
284}