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 command_prompt: Style,
67 pub command_input: Style,
68 pub command_cursor: Style,
69
70 pub marked: Style,
72}
73
74impl Theme {
75 pub fn dark() -> Self {
77 let slate_50 = Color::Rgb(248, 250, 252);
79 let slate_100 = Color::Rgb(241, 245, 249);
80 let slate_300 = Color::Rgb(203, 213, 225);
81 let slate_400 = Color::Rgb(148, 163, 184);
82 let slate_500 = Color::Rgb(100, 116, 139);
83 let slate_600 = Color::Rgb(71, 85, 105);
84 let slate_700 = Color::Rgb(51, 65, 85);
85 let slate_800 = Color::Rgb(30, 41, 59);
86 let slate_900 = Color::Rgb(15, 23, 42);
87
88 let blue_400 = Color::Rgb(96, 165, 250);
90 let blue_500 = Color::Rgb(59, 130, 246);
91 let green_500 = Color::Rgb(34, 197, 94);
92 let yellow_500 = Color::Rgb(234, 179, 8);
93 let orange_500 = Color::Rgb(249, 115, 22);
94 let red_500 = Color::Rgb(239, 68, 68);
95 let cyan_400 = Color::Rgb(34, 211, 238);
96 let amber_500 = Color::Rgb(245, 158, 11);
97
98 Self {
99 variant: ThemeVariant::Dark,
100 background: slate_900,
101 foreground: slate_100,
102 muted: slate_500,
103
104 selected: Style::new().bg(slate_700).fg(slate_50).add_modifier(Modifier::BOLD),
105 hover: Style::new().bg(slate_800),
106
107 size_huge: red_500,
108 size_large: orange_500,
109 size_medium: yellow_500,
110 size_small: green_500,
111 size_tiny: slate_600,
112
113 success: green_500,
114 warning: yellow_500,
115 error: red_500,
116 info: blue_400,
117
118 border: Style::new().fg(slate_600),
119 title: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
120 help_key: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
121 help_desc: Style::new().fg(slate_400),
122
123 tree_lines: Style::new().fg(slate_600),
124 directory: Style::new().fg(blue_500).add_modifier(Modifier::BOLD),
125 file: Style::new().fg(slate_300),
126 symlink: Style::new().fg(cyan_400),
127 executable: Style::new().fg(green_500),
128
129 progress_bar: Style::new().fg(blue_500),
130 progress_bg: Style::new().fg(slate_700),
131
132 header: Style::new().bg(slate_800).fg(slate_100),
133 footer: Style::new().bg(slate_800).fg(slate_400),
134
135 command_prompt: Style::new().fg(blue_400).add_modifier(Modifier::BOLD),
136 command_input: Style::new().fg(slate_100),
137 command_cursor: Style::new().add_modifier(Modifier::REVERSED),
138
139 marked: Style::new().fg(amber_500).add_modifier(Modifier::BOLD),
140 }
141 }
142
143 pub fn light() -> Self {
145 let slate_50 = Color::Rgb(248, 250, 252);
147 let slate_100 = Color::Rgb(241, 245, 249);
148 let slate_200 = Color::Rgb(226, 232, 240);
149 let slate_400 = Color::Rgb(148, 163, 184);
150 let slate_500 = Color::Rgb(100, 116, 139);
151 let slate_600 = Color::Rgb(71, 85, 105);
152 let slate_700 = Color::Rgb(51, 65, 85);
153 let slate_800 = Color::Rgb(30, 41, 59);
154 let slate_900 = Color::Rgb(15, 23, 42);
155
156 let blue_600 = Color::Rgb(37, 99, 235);
158 let blue_700 = Color::Rgb(29, 78, 216);
159 let green_600 = Color::Rgb(22, 163, 74);
160 let yellow_600 = Color::Rgb(202, 138, 4);
161 let orange_600 = Color::Rgb(234, 88, 12);
162 let red_600 = Color::Rgb(220, 38, 38);
163 let cyan_600 = Color::Rgb(8, 145, 178);
164 let amber_600 = Color::Rgb(217, 119, 6);
165
166 Self {
167 variant: ThemeVariant::Light,
168 background: slate_50,
169 foreground: slate_900,
170 muted: slate_500,
171
172 selected: Style::new().bg(slate_200).fg(slate_900).add_modifier(Modifier::BOLD),
173 hover: Style::new().bg(slate_100),
174
175 size_huge: red_600,
176 size_large: orange_600,
177 size_medium: yellow_600,
178 size_small: green_600,
179 size_tiny: slate_400,
180
181 success: green_600,
182 warning: yellow_600,
183 error: red_600,
184 info: blue_600,
185
186 border: Style::new().fg(slate_400),
187 title: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
188 help_key: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
189 help_desc: Style::new().fg(slate_600),
190
191 tree_lines: Style::new().fg(slate_400),
192 directory: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
193 file: Style::new().fg(slate_700),
194 symlink: Style::new().fg(cyan_600),
195 executable: Style::new().fg(green_600),
196
197 progress_bar: Style::new().fg(blue_600),
198 progress_bg: Style::new().fg(slate_200),
199
200 header: Style::new().bg(slate_100).fg(slate_800),
201 footer: Style::new().bg(slate_100).fg(slate_600),
202
203 command_prompt: Style::new().fg(blue_700).add_modifier(Modifier::BOLD),
204 command_input: Style::new().fg(slate_900),
205 command_cursor: Style::new().add_modifier(Modifier::REVERSED),
206
207 marked: Style::new().fg(amber_600).add_modifier(Modifier::BOLD),
208 }
209 }
210
211 pub fn from_variant(variant: ThemeVariant) -> Self {
213 match variant {
214 ThemeVariant::Dark => Self::dark(),
215 ThemeVariant::Light => Self::light(),
216 }
217 }
218
219 pub fn toggle(&self) -> Self {
221 match self.variant {
222 ThemeVariant::Dark => Self::light(),
223 ThemeVariant::Light => Self::dark(),
224 }
225 }
226
227 pub fn size_color(&self, ratio: f64) -> Color {
229 match ratio {
230 r if r > 0.50 => self.size_huge,
231 r if r > 0.25 => self.size_large,
232 r if r > 0.10 => self.size_medium,
233 r if r > 0.01 => self.size_small,
234 _ => self.size_tiny,
235 }
236 }
237
238 pub fn size_bar_style(&self, ratio: f64) -> Style {
240 Style::new().fg(self.size_color(ratio))
241 }
242}
243
244impl Default for Theme {
245 fn default() -> Self {
246 Self::dark()
247 }
248}