Skip to main content

framework_tool_tui/tui/
theme.rs

1use std::str::FromStr;
2
3use ratatui::style::Color;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "lowercase")]
8pub enum ThemeVariant {
9    Default,
10    Framework,
11    Alucard,
12    CatppuccinFrappe,
13    CatppuccinLatte,
14    CatppuccinMacchiato,
15    CatppuccinMocha,
16    Dracula,
17    GameBoy,
18    GithubDark,
19    GithubLight,
20    GruvboxDark,
21    GruvboxLight,
22    MonochromeDark,
23    MonochromeLight,
24    MonokaiPro,
25}
26
27impl FromStr for ThemeVariant {
28    type Err = String;
29
30    fn from_str(s: &str) -> Result<Self, Self::Err> {
31        match s.to_lowercase().as_str() {
32            "default" => Ok(ThemeVariant::Default),
33            "framework" => Ok(ThemeVariant::Framework),
34            "alucard" => Ok(ThemeVariant::Alucard),
35            "catppuccin_frappe" => Ok(ThemeVariant::CatppuccinFrappe),
36            "catppuccin_latte" => Ok(ThemeVariant::CatppuccinLatte),
37            "catppuccin_macchiato" => Ok(ThemeVariant::CatppuccinMacchiato),
38            "catppuccin_mocha" => Ok(ThemeVariant::CatppuccinMocha),
39            "dracula" => Ok(ThemeVariant::Dracula),
40            "gameboy" => Ok(ThemeVariant::GameBoy),
41            "github_dark" => Ok(ThemeVariant::GithubDark),
42            "github_light" => Ok(ThemeVariant::GithubLight),
43            "gruvbox_dark" => Ok(ThemeVariant::GruvboxDark),
44            "gruvbox_light" => Ok(ThemeVariant::GruvboxLight),
45            "monochrome_dark" => Ok(ThemeVariant::MonochromeDark),
46            "monochrome_light" => Ok(ThemeVariant::MonochromeLight),
47            "monokai_pro" => Ok(ThemeVariant::MonokaiPro),
48            _ => Err(format!("Unknown theme: {}", s)),
49        }
50    }
51}
52
53impl ThemeVariant {
54    pub fn name(&self) -> &'static str {
55        match self {
56            ThemeVariant::Default => "Default",
57            ThemeVariant::Framework => "Framework",
58            ThemeVariant::Alucard => "Alucard",
59            ThemeVariant::CatppuccinFrappe => "Catppuccin Frappe",
60            ThemeVariant::CatppuccinLatte => "Catppuccin Latte",
61            ThemeVariant::CatppuccinMacchiato => "Catppuccin Macchiato",
62            ThemeVariant::CatppuccinMocha => "Catppuccin Mocha",
63            ThemeVariant::Dracula => "Dracula",
64            ThemeVariant::GameBoy => "Game Boy",
65            ThemeVariant::GithubDark => "GitHub Dark",
66            ThemeVariant::GithubLight => "GitHub Light",
67            ThemeVariant::GruvboxDark => "Gruvbox Dark",
68            ThemeVariant::GruvboxLight => "Gruvbox Light",
69            ThemeVariant::MonochromeDark => "Monochrome Dark",
70            ThemeVariant::MonochromeLight => "Monochrome Light",
71            ThemeVariant::MonokaiPro => "Monokai Pro",
72        }
73    }
74
75    pub const ALL: [ThemeVariant; 16] = [
76        ThemeVariant::Default,
77        ThemeVariant::Framework,
78        ThemeVariant::Alucard,
79        ThemeVariant::CatppuccinFrappe,
80        ThemeVariant::CatppuccinLatte,
81        ThemeVariant::CatppuccinMacchiato,
82        ThemeVariant::CatppuccinMocha,
83        ThemeVariant::Dracula,
84        ThemeVariant::GameBoy,
85        ThemeVariant::GithubDark,
86        ThemeVariant::GithubLight,
87        ThemeVariant::GruvboxDark,
88        ThemeVariant::GruvboxLight,
89        ThemeVariant::MonochromeDark,
90        ThemeVariant::MonochromeLight,
91        ThemeVariant::MonokaiPro,
92    ];
93
94    pub fn next(&self) -> Self {
95        let current_idx = Self::ALL.iter().position(|t| t == self).unwrap();
96        Self::ALL[(current_idx + 1) % Self::ALL.len()]
97    }
98
99    pub fn previous(&self) -> Self {
100        let current_idx = Self::ALL.iter().position(|t| t == self).unwrap();
101        let prev_idx = if current_idx == 0 {
102            Self::ALL.len() - 1
103        } else {
104            current_idx - 1
105        };
106        Self::ALL[prev_idx]
107    }
108}
109
110pub struct Theme {
111    pub variant: ThemeVariant,
112    pub text: Color,
113    pub background: Color,
114    pub border: Color,
115    pub border_active: Color,
116    pub indication_ok: Color,
117    pub indication_warning: Color,
118    pub brightness_bar: Color,
119    pub charge_bar: Color,
120    pub bar_background: Color,
121    pub highlighted_text: Color,
122    pub informative_text: Color,
123}
124
125impl Default for Theme {
126    fn default() -> Self {
127        Theme::from_variant(ThemeVariant::Default)
128    }
129}
130
131impl Theme {
132    pub fn from_variant(variant: ThemeVariant) -> Self {
133        match variant {
134            ThemeVariant::Default => Self::default0(),
135            ThemeVariant::Framework => Self::framework(),
136            ThemeVariant::Alucard => Self::alucard(),
137            ThemeVariant::CatppuccinFrappe => Self::catppuccin_frappe(),
138            ThemeVariant::CatppuccinLatte => Self::catppuccin_latte(),
139            ThemeVariant::CatppuccinMacchiato => Self::catppuccin_macchiato(),
140            ThemeVariant::CatppuccinMocha => Self::catppuccin_mocha(),
141            ThemeVariant::Dracula => Self::dracula(),
142            ThemeVariant::GameBoy => Self::gameboy(),
143            ThemeVariant::GithubDark => Self::github_dark(),
144            ThemeVariant::GithubLight => Self::github_light(),
145            ThemeVariant::GruvboxDark => Self::gruvbox_dark(),
146            ThemeVariant::GruvboxLight => Self::gruvbox_light(),
147            ThemeVariant::MonochromeDark => Self::monochrome_dark(),
148            ThemeVariant::MonochromeLight => Self::monochrome_light(),
149            ThemeVariant::MonokaiPro => Self::monokai_pro(),
150        }
151    }
152
153    pub fn default0() -> Self {
154        Self {
155            variant: ThemeVariant::Default,
156            text: Color::Indexed(7),
157            background: Color::Indexed(0),
158            border: Color::Indexed(15),
159            border_active: Color::Indexed(3),
160            indication_ok: Color::Indexed(2),
161            indication_warning: Color::Indexed(1),
162            brightness_bar: Color::Indexed(4),
163            charge_bar: Color::Indexed(4),
164            bar_background: Color::Indexed(0),
165            highlighted_text: Color::Indexed(4),
166            informative_text: Color::Indexed(5),
167        }
168    }
169
170    pub fn framework() -> Self {
171        Self {
172            variant: ThemeVariant::Framework,
173            text: Color::from_str("#F5F5F5").unwrap(),
174            background: Color::from_str("#1F1F1F").unwrap(),
175            border: Color::from_str("#F45A27").unwrap(),
176            border_active: Color::from_str("#FFD600").unwrap(),
177            indication_ok: Color::from_str("#00B16A").unwrap(),
178            indication_warning: Color::from_str("#E53935").unwrap(),
179            brightness_bar: Color::from_str("#fdbe54").unwrap(),
180            charge_bar: Color::from_str("#9481D8").unwrap(),
181            bar_background: Color::from_str("#363636").unwrap(),
182            highlighted_text: Color::from_str("#AEC2C9").unwrap(),
183            informative_text: Color::from_str("#9481D8").unwrap(),
184        }
185    }
186
187    pub fn alucard() -> Self {
188        Self {
189            variant: ThemeVariant::Alucard,
190            text: Color::from_str("#1F1F1F").unwrap(),
191            background: Color::from_str("#FFFBEB").unwrap(),
192            border: Color::from_str("#A34D14").unwrap(),
193            border_active: Color::from_str("#846E15").unwrap(),
194            indication_ok: Color::from_str("#14710A").unwrap(),
195            indication_warning: Color::from_str("#CB3A2A").unwrap(),
196            brightness_bar: Color::from_str("#846E15").unwrap(),
197            charge_bar: Color::from_str("#644AC9").unwrap(),
198            bar_background: Color::from_str("#CFCFDE").unwrap(),
199            highlighted_text: Color::from_str("#036A96").unwrap(),
200            informative_text: Color::from_str("#644AC9").unwrap(),
201        }
202    }
203
204    pub fn catppuccin_frappe() -> Self {
205        Self {
206            variant: ThemeVariant::CatppuccinFrappe,
207            text: Color::from_str("#C6D0F5").unwrap(),
208            background: Color::from_str("#232634").unwrap(),
209            border: Color::from_str("#EF9F76").unwrap(),
210            border_active: Color::from_str("#E5C890").unwrap(),
211            indication_ok: Color::from_str("#A6D189").unwrap(),
212            indication_warning: Color::from_str("#E78284").unwrap(),
213            brightness_bar: Color::from_str("#E5C890").unwrap(),
214            charge_bar: Color::from_str("#8CAAEE").unwrap(),
215            bar_background: Color::from_str("#303446").unwrap(),
216            highlighted_text: Color::from_str("#8caaee").unwrap(),
217            informative_text: Color::from_str("#CA9EE6").unwrap(),
218        }
219    }
220
221    pub fn catppuccin_latte() -> Self {
222        Self {
223            variant: ThemeVariant::CatppuccinLatte,
224            text: Color::from_str("#4C4F69").unwrap(),
225            background: Color::from_str("#DCE0E8").unwrap(),
226            border: Color::from_str("#D20F39").unwrap(),
227            border_active: Color::from_str("#DF8E1D").unwrap(),
228            indication_ok: Color::from_str("#40A02B").unwrap(),
229            indication_warning: Color::from_str("#D20F39").unwrap(),
230            brightness_bar: Color::from_str("#DF8E1D").unwrap(),
231            charge_bar: Color::from_str("#1E66F5").unwrap(),
232            bar_background: Color::from_str("#EFF1F5").unwrap(),
233            highlighted_text: Color::from_str("#1e66f5").unwrap(),
234            informative_text: Color::from_str("#8839EF").unwrap(),
235        }
236    }
237
238    pub fn catppuccin_macchiato() -> Self {
239        Self {
240            variant: ThemeVariant::CatppuccinMacchiato,
241            text: Color::from_str("#CAD3F5").unwrap(),
242            background: Color::from_str("#181926").unwrap(),
243            border: Color::from_str("#F5A97F").unwrap(),
244            border_active: Color::from_str("#EED49F").unwrap(),
245            indication_ok: Color::from_str("#A6DA95").unwrap(),
246            indication_warning: Color::from_str("#ED8796").unwrap(),
247            brightness_bar: Color::from_str("#EED49F").unwrap(),
248            charge_bar: Color::from_str("#8AADF4").unwrap(),
249            bar_background: Color::from_str("#24273A").unwrap(),
250            highlighted_text: Color::from_str("#8aadf4").unwrap(),
251            informative_text: Color::from_str("#C6A0F6").unwrap(),
252        }
253    }
254
255    pub fn catppuccin_mocha() -> Self {
256        Self {
257            variant: ThemeVariant::CatppuccinMocha,
258            text: Color::from_str("#CDD6F4").unwrap(),
259            background: Color::from_str("#11111B").unwrap(),
260            border: Color::from_str("#FAB387").unwrap(),
261            border_active: Color::from_str("#F9E2AF").unwrap(),
262            indication_ok: Color::from_str("#A6E3A1").unwrap(),
263            indication_warning: Color::from_str("#F38BA8").unwrap(),
264            brightness_bar: Color::from_str("#F9E2AF").unwrap(),
265            charge_bar: Color::from_str("#89B4FA").unwrap(),
266            bar_background: Color::from_str("#1E1E2E").unwrap(),
267            highlighted_text: Color::from_str("#89b4fa").unwrap(),
268            informative_text: Color::from_str("#CBA6f7").unwrap(),
269        }
270    }
271
272    pub fn dracula() -> Self {
273        Self {
274            variant: ThemeVariant::Dracula,
275            text: Color::from_str("#F8F8F2").unwrap(),
276            background: Color::from_str("#282A36").unwrap(),
277            border: Color::from_str("#FFB86C").unwrap(),
278            border_active: Color::from_str("#FFB86C").unwrap(),
279            indication_ok: Color::from_str("#50FA7B").unwrap(),
280            indication_warning: Color::from_str("#FF5555").unwrap(),
281            brightness_bar: Color::from_str("#F1FA8C").unwrap(),
282            charge_bar: Color::from_str("#8BE9FD").unwrap(),
283            bar_background: Color::from_str("#44475A").unwrap(),
284            highlighted_text: Color::from_str("#8BE9FD").unwrap(),
285            informative_text: Color::from_str("#BD93F9").unwrap(),
286        }
287    }
288
289    pub fn gameboy() -> Self {
290        Self {
291            variant: ThemeVariant::GameBoy,
292            text: Color::from_str("#9A9E3F").unwrap(),
293            background: Color::from_str("#1B2A09").unwrap(),
294            border: Color::from_str("#496B22").unwrap(),
295            border_active: Color::from_str("#9A9E3F").unwrap(),
296            indication_ok: Color::from_str("#9A9E3F").unwrap(),
297            indication_warning: Color::from_str("#9A9E3F").unwrap(),
298            brightness_bar: Color::from_str("#9A9E3F").unwrap(),
299            charge_bar: Color::from_str("#9A9E3F").unwrap(),
300            bar_background: Color::from_str("#0E450B").unwrap(),
301            highlighted_text: Color::from_str("#9A9E3F").unwrap(),
302            informative_text: Color::from_str("#9A9E3F").unwrap(),
303        }
304    }
305
306    pub fn github_dark() -> Self {
307        Self {
308            variant: ThemeVariant::GithubDark,
309            text: Color::from_str("#D1D7E0").unwrap(),
310            background: Color::from_str("#212830").unwrap(),
311            border: Color::from_str("#FF8E40").unwrap(),
312            border_active: Color::from_str("#D3FA37").unwrap(),
313            indication_ok: Color::from_str("#5fED83").unwrap(),
314            indication_warning: Color::from_str("#FF8E40").unwrap(),
315            brightness_bar: Color::from_str("#D3FA37").unwrap(),
316            charge_bar: Color::from_str("#1F6FEB").unwrap(),
317            bar_background: Color::from_str("#262C36").unwrap(),
318            highlighted_text: Color::from_str("#9EECFF").unwrap(),
319            informative_text: Color::from_str("#FF80D2").unwrap(),
320        }
321    }
322
323    pub fn github_light() -> Self {
324        Self {
325            variant: ThemeVariant::GithubLight,
326            text: Color::from_str("#000000").unwrap(),
327            background: Color::from_str("#FFFFFF").unwrap(),
328            border: Color::from_str("#703100").unwrap(),
329            border_active: Color::from_str("#DB9D00").unwrap(),
330            indication_ok: Color::from_str("#1f883D").unwrap(),
331            indication_warning: Color::from_str("#703100").unwrap(),
332            brightness_bar: Color::from_str("#DB9D00").unwrap(),
333            charge_bar: Color::from_str("#0969DA").unwrap(),
334            bar_background: Color::from_str("#F6F8FA").unwrap(),
335            highlighted_text: Color::from_str("#212183").unwrap(),
336            informative_text: Color::from_str("#8342FA").unwrap(),
337        }
338    }
339
340    pub fn gruvbox_dark() -> Self {
341        Self {
342            variant: ThemeVariant::GruvboxDark,
343            text: Color::from_str("#EBDBB2").unwrap(),
344            background: Color::from_str("#282828").unwrap(),
345            border: Color::from_str("#FE8019").unwrap(),
346            border_active: Color::from_str("#FABd2F").unwrap(),
347            indication_ok: Color::from_str("#B8BB26").unwrap(),
348            indication_warning: Color::from_str("#FE8019").unwrap(),
349            brightness_bar: Color::from_str("#FABD2F").unwrap(),
350            charge_bar: Color::from_str("#458588").unwrap(),
351            bar_background: Color::from_str("#504945").unwrap(),
352            highlighted_text: Color::from_str("#83A598").unwrap(),
353            informative_text: Color::from_str("#D3869B").unwrap(),
354        }
355    }
356
357    pub fn gruvbox_light() -> Self {
358        Self {
359            variant: ThemeVariant::GruvboxLight,
360            text: Color::from_str("#3C3836").unwrap(),
361            background: Color::from_str("#FFFFFF").unwrap(),
362            border: Color::from_str("#AF3A03").unwrap(),
363            border_active: Color::from_str("#D79921").unwrap(),
364            indication_ok: Color::from_str("#79740E").unwrap(),
365            indication_warning: Color::from_str("#AF3A03").unwrap(),
366            brightness_bar: Color::from_str("#B57614").unwrap(),
367            charge_bar: Color::from_str("#458588").unwrap(),
368            bar_background: Color::from_str("#D5C4A1").unwrap(),
369            highlighted_text: Color::from_str("#076678").unwrap(),
370            informative_text: Color::from_str("#8F3F71").unwrap(),
371        }
372    }
373
374    pub fn monochrome_dark() -> Self {
375        Self {
376            variant: ThemeVariant::MonochromeDark,
377            text: Color::from_str("#FFFFFF").unwrap(),
378            background: Color::from_str("#000000").unwrap(),
379            border: Color::from_str("#FFFFFF").unwrap(),
380            border_active: Color::from_str("#FFFFFF").unwrap(),
381            indication_ok: Color::from_str("#FFFFFF").unwrap(),
382            indication_warning: Color::from_str("#FFFFFF").unwrap(),
383            brightness_bar: Color::from_str("#FFFFFF").unwrap(),
384            charge_bar: Color::from_str("#FFFFFF").unwrap(),
385            bar_background: Color::from_str("#000000").unwrap(),
386            highlighted_text: Color::from_str("#FFFFFF").unwrap(),
387            informative_text: Color::from_str("#FFFFFF").unwrap(),
388        }
389    }
390
391    pub fn monochrome_light() -> Self {
392        Self {
393            variant: ThemeVariant::MonochromeLight,
394            text: Color::from_str("#000000").unwrap(),
395            background: Color::from_str("#FFFFFF").unwrap(),
396            border: Color::from_str("#000000").unwrap(),
397            border_active: Color::from_str("#000000").unwrap(),
398            indication_ok: Color::from_str("#000000").unwrap(),
399            indication_warning: Color::from_str("#000000").unwrap(),
400            brightness_bar: Color::from_str("#000000").unwrap(),
401            charge_bar: Color::from_str("#000000").unwrap(),
402            bar_background: Color::from_str("#FFFFFF").unwrap(),
403            highlighted_text: Color::from_str("#000000").unwrap(),
404            informative_text: Color::from_str("#000000").unwrap(),
405        }
406    }
407
408    pub fn monokai_pro() -> Self {
409        Self {
410            variant: ThemeVariant::MonokaiPro,
411            text: Color::from_str("#FFFFFF").unwrap(),
412            background: Color::from_str("#161517").unwrap(),
413            border: Color::from_str("#FC9867").unwrap(),
414            border_active: Color::from_str("#FFD866").unwrap(),
415            indication_ok: Color::from_str("#A9DC76").unwrap(),
416            indication_warning: Color::from_str("#FF6188").unwrap(),
417            brightness_bar: Color::from_str("#FFD866").unwrap(),
418            charge_bar: Color::from_str("#AB9DF2").unwrap(),
419            bar_background: Color::from_str("#373138").unwrap(),
420            highlighted_text: Color::from_str("#77DCE8").unwrap(),
421            informative_text: Color::from_str("#AB9DF2").unwrap(),
422        }
423    }
424}