Skip to main content

ratatui_markdown/
theme.rs

1use ratatui::style::Color;
2
3#[cfg(feature = "mermaid")]
4use crate::mermaid::theme::MermaidTheme;
5
6#[derive(Debug, Clone, Copy)]
7pub struct CodeColors {
8    pub comment: Color,
9    pub keyword: Color,
10    pub string: Color,
11    pub string_escape: Color,
12    pub number: Color,
13    pub constant: Color,
14    pub function: Color,
15    pub r#type: Color,
16    pub variable: Color,
17    pub property: Color,
18    pub operator: Color,
19    pub punctuation: Color,
20    pub attribute: Color,
21    pub tag: Color,
22    pub label: Color,
23    pub error: Color,
24}
25
26impl Default for CodeColors {
27    fn default() -> Self {
28        Self::DEFAULT
29    }
30}
31
32impl CodeColors {
33    pub const DEFAULT: Self = Self {
34        comment: Color::DarkGray,
35        keyword: Color::Magenta,
36        string: Color::Green,
37        string_escape: Color::LightGreen,
38        number: Color::Yellow,
39        constant: Color::Yellow,
40        function: Color::Cyan,
41        r#type: Color::LightCyan,
42        variable: Color::White,
43        property: Color::LightBlue,
44        operator: Color::LightMagenta,
45        punctuation: Color::DarkGray,
46        attribute: Color::LightYellow,
47        tag: Color::Cyan,
48        label: Color::LightRed,
49        error: Color::Red,
50    };
51
52    pub fn builder() -> CodeColorsBuilder {
53        CodeColorsBuilder(Self::default())
54    }
55}
56
57pub struct CodeColorsBuilder(CodeColors);
58
59impl CodeColorsBuilder {
60    pub fn comment(mut self, c: Color) -> Self {
61        self.0.comment = c;
62        self
63    }
64    pub fn keyword(mut self, c: Color) -> Self {
65        self.0.keyword = c;
66        self
67    }
68    pub fn string(mut self, c: Color) -> Self {
69        self.0.string = c;
70        self
71    }
72    pub fn string_escape(mut self, c: Color) -> Self {
73        self.0.string_escape = c;
74        self
75    }
76    pub fn number(mut self, c: Color) -> Self {
77        self.0.number = c;
78        self
79    }
80    pub fn constant(mut self, c: Color) -> Self {
81        self.0.constant = c;
82        self
83    }
84    pub fn function(mut self, c: Color) -> Self {
85        self.0.function = c;
86        self
87    }
88    pub fn r#type(mut self, c: Color) -> Self {
89        self.0.r#type = c;
90        self
91    }
92    pub fn variable(mut self, c: Color) -> Self {
93        self.0.variable = c;
94        self
95    }
96    pub fn property(mut self, c: Color) -> Self {
97        self.0.property = c;
98        self
99    }
100    pub fn operator(mut self, c: Color) -> Self {
101        self.0.operator = c;
102        self
103    }
104    pub fn punctuation(mut self, c: Color) -> Self {
105        self.0.punctuation = c;
106        self
107    }
108    pub fn attribute(mut self, c: Color) -> Self {
109        self.0.attribute = c;
110        self
111    }
112    pub fn tag(mut self, c: Color) -> Self {
113        self.0.tag = c;
114        self
115    }
116    pub fn label(mut self, c: Color) -> Self {
117        self.0.label = c;
118        self
119    }
120    pub fn error(mut self, c: Color) -> Self {
121        self.0.error = c;
122        self
123    }
124    pub fn build(self) -> CodeColors {
125        self.0
126    }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
130pub struct Generation(pub u64);
131
132impl Generation {
133    pub fn next(&self) -> Self {
134        Self(self.0 + 1)
135    }
136}
137
138pub trait RichTextTheme {
139    fn generation(&self) -> Generation;
140    fn get_text_color(&self) -> Color;
141    fn get_muted_text_color(&self) -> Color;
142    fn get_primary_color(&self) -> Color;
143    fn get_popup_selected_background(&self) -> Color;
144    fn get_border_color(&self) -> Color;
145    fn get_focused_border_color(&self) -> Color;
146    fn get_secondary_color(&self) -> Color;
147    fn get_info_color(&self) -> Color;
148    fn get_json_key_color(&self) -> Color;
149    fn get_json_string_color(&self) -> Color;
150    fn get_json_number_color(&self) -> Color;
151    fn get_json_bool_color(&self) -> Color;
152    fn get_json_null_color(&self) -> Color;
153    fn get_accent_yellow(&self) -> Color;
154
155    fn get_code_colors(&self) -> CodeColors {
156        CodeColors::default()
157    }
158
159    fn get_popup_selected_text_color(&self) -> Color {
160        Color::White
161    }
162    fn get_background_color(&self) -> Color {
163        Color::Black
164    }
165
166    #[cfg(feature = "mermaid")]
167    fn get_mermaid_theme(&self) -> MermaidTheme {
168        MermaidTheme::for_background(self.get_background_color())
169    }
170}
171
172#[derive(Debug, Clone, Copy)]
173pub struct ThemeConfig {
174    pub gen: Generation,
175    pub text_color: Color,
176    pub muted_text_color: Color,
177    pub primary_color: Color,
178    pub popup_selected_background: Color,
179    pub border_color: Color,
180    pub focused_border_color: Color,
181    pub secondary_color: Color,
182    pub info_color: Color,
183    pub json_key_color: Color,
184    pub json_string_color: Color,
185    pub json_number_color: Color,
186    pub json_bool_color: Color,
187    pub json_null_color: Color,
188    pub accent_yellow: Color,
189    pub code_colors: CodeColors,
190}
191
192impl Default for ThemeConfig {
193    fn default() -> Self {
194        Self {
195            gen: Generation(1),
196            text_color: Color::White,
197            muted_text_color: Color::DarkGray,
198            primary_color: Color::Cyan,
199            popup_selected_background: Color::DarkGray,
200            border_color: Color::DarkGray,
201            focused_border_color: Color::White,
202            secondary_color: Color::Blue,
203            info_color: Color::LightBlue,
204            json_key_color: Color::LightCyan,
205            json_string_color: Color::Green,
206            json_number_color: Color::Yellow,
207            json_bool_color: Color::Magenta,
208            json_null_color: Color::DarkGray,
209            accent_yellow: Color::Yellow,
210            code_colors: CodeColors::DEFAULT,
211        }
212    }
213}
214
215impl ThemeConfig {
216    pub fn new() -> Self {
217        Self::default()
218    }
219
220    pub fn builder() -> ThemeBuilder {
221        ThemeBuilder {
222            config: Self::default(),
223        }
224    }
225
226    pub fn with_generation(mut self, gen: Generation) -> Self {
227        self.gen = gen;
228        self
229    }
230
231    pub fn with_text_color(mut self, c: Color) -> Self {
232        self.text_color = c;
233        self
234    }
235
236    pub fn with_muted_text_color(mut self, c: Color) -> Self {
237        self.muted_text_color = c;
238        self
239    }
240
241    pub fn with_primary_color(mut self, c: Color) -> Self {
242        self.primary_color = c;
243        self
244    }
245
246    pub fn with_popup_selected_background(mut self, c: Color) -> Self {
247        self.popup_selected_background = c;
248        self
249    }
250
251    pub fn with_border_color(mut self, c: Color) -> Self {
252        self.border_color = c;
253        self
254    }
255
256    pub fn with_focused_border_color(mut self, c: Color) -> Self {
257        self.focused_border_color = c;
258        self
259    }
260
261    pub fn with_secondary_color(mut self, c: Color) -> Self {
262        self.secondary_color = c;
263        self
264    }
265
266    pub fn with_info_color(mut self, c: Color) -> Self {
267        self.info_color = c;
268        self
269    }
270
271    pub fn with_json_key_color(mut self, c: Color) -> Self {
272        self.json_key_color = c;
273        self
274    }
275
276    pub fn with_json_string_color(mut self, c: Color) -> Self {
277        self.json_string_color = c;
278        self
279    }
280
281    pub fn with_json_number_color(mut self, c: Color) -> Self {
282        self.json_number_color = c;
283        self
284    }
285
286    pub fn with_json_bool_color(mut self, c: Color) -> Self {
287        self.json_bool_color = c;
288        self
289    }
290
291    pub fn with_json_null_color(mut self, c: Color) -> Self {
292        self.json_null_color = c;
293        self
294    }
295
296    pub fn with_accent_yellow(mut self, c: Color) -> Self {
297        self.accent_yellow = c;
298        self
299    }
300
301    pub fn with_code_colors(mut self, colors: CodeColors) -> Self {
302        self.code_colors = colors;
303        self
304    }
305}
306
307impl RichTextTheme for ThemeConfig {
308    fn generation(&self) -> Generation {
309        self.gen
310    }
311    fn get_text_color(&self) -> Color {
312        self.text_color
313    }
314    fn get_muted_text_color(&self) -> Color {
315        self.muted_text_color
316    }
317    fn get_primary_color(&self) -> Color {
318        self.primary_color
319    }
320    fn get_popup_selected_background(&self) -> Color {
321        self.popup_selected_background
322    }
323    fn get_border_color(&self) -> Color {
324        self.border_color
325    }
326    fn get_focused_border_color(&self) -> Color {
327        self.focused_border_color
328    }
329    fn get_secondary_color(&self) -> Color {
330        self.secondary_color
331    }
332    fn get_info_color(&self) -> Color {
333        self.info_color
334    }
335    fn get_json_key_color(&self) -> Color {
336        self.json_key_color
337    }
338    fn get_json_string_color(&self) -> Color {
339        self.json_string_color
340    }
341    fn get_json_number_color(&self) -> Color {
342        self.json_number_color
343    }
344    fn get_json_bool_color(&self) -> Color {
345        self.json_bool_color
346    }
347    fn get_json_null_color(&self) -> Color {
348        self.json_null_color
349    }
350    fn get_accent_yellow(&self) -> Color {
351        self.accent_yellow
352    }
353    fn get_code_colors(&self) -> CodeColors {
354        self.code_colors
355    }
356}
357
358pub struct ThemeBuilder {
359    config: ThemeConfig,
360}
361
362impl ThemeBuilder {
363    pub fn with_generation(mut self, gen: Generation) -> Self {
364        self.config.gen = gen;
365        self
366    }
367
368    pub fn with_text_color(mut self, c: Color) -> Self {
369        self.config.text_color = c;
370        self
371    }
372
373    pub fn with_muted_text_color(mut self, c: Color) -> Self {
374        self.config.muted_text_color = c;
375        self
376    }
377
378    pub fn with_primary_color(mut self, c: Color) -> Self {
379        self.config.primary_color = c;
380        self
381    }
382
383    pub fn with_popup_selected_background(mut self, c: Color) -> Self {
384        self.config.popup_selected_background = c;
385        self
386    }
387
388    pub fn with_border_color(mut self, c: Color) -> Self {
389        self.config.border_color = c;
390        self
391    }
392
393    pub fn with_focused_border_color(mut self, c: Color) -> Self {
394        self.config.focused_border_color = c;
395        self
396    }
397
398    pub fn with_secondary_color(mut self, c: Color) -> Self {
399        self.config.secondary_color = c;
400        self
401    }
402
403    pub fn with_info_color(mut self, c: Color) -> Self {
404        self.config.info_color = c;
405        self
406    }
407
408    pub fn with_json_key_color(mut self, c: Color) -> Self {
409        self.config.json_key_color = c;
410        self
411    }
412
413    pub fn with_json_string_color(mut self, c: Color) -> Self {
414        self.config.json_string_color = c;
415        self
416    }
417
418    pub fn with_json_number_color(mut self, c: Color) -> Self {
419        self.config.json_number_color = c;
420        self
421    }
422
423    pub fn with_json_bool_color(mut self, c: Color) -> Self {
424        self.config.json_bool_color = c;
425        self
426    }
427
428    pub fn with_json_null_color(mut self, c: Color) -> Self {
429        self.config.json_null_color = c;
430        self
431    }
432
433    pub fn with_accent_yellow(mut self, c: Color) -> Self {
434        self.config.accent_yellow = c;
435        self
436    }
437
438    pub fn with_code_colors(mut self, colors: CodeColors) -> Self {
439        self.config.code_colors = colors;
440        self
441    }
442
443    pub fn build(self) -> ThemeConfig {
444        self.config
445    }
446}
447
448#[deprecated(since = "0.3.0", note = "Use `ThemeConfig::default()` instead")]
449pub struct DefaultTheme;
450
451#[allow(deprecated)]
452impl RichTextTheme for DefaultTheme {
453    fn generation(&self) -> Generation {
454        Generation(1)
455    }
456    fn get_text_color(&self) -> Color {
457        Color::White
458    }
459    fn get_muted_text_color(&self) -> Color {
460        Color::DarkGray
461    }
462    fn get_primary_color(&self) -> Color {
463        Color::Cyan
464    }
465    fn get_secondary_color(&self) -> Color {
466        Color::Blue
467    }
468    fn get_info_color(&self) -> Color {
469        Color::LightBlue
470    }
471    fn get_border_color(&self) -> Color {
472        Color::DarkGray
473    }
474    fn get_focused_border_color(&self) -> Color {
475        Color::White
476    }
477    fn get_popup_selected_background(&self) -> Color {
478        Color::DarkGray
479    }
480    fn get_json_key_color(&self) -> Color {
481        Color::LightCyan
482    }
483    fn get_json_string_color(&self) -> Color {
484        Color::Green
485    }
486    fn get_json_number_color(&self) -> Color {
487        Color::Yellow
488    }
489    fn get_json_bool_color(&self) -> Color {
490        Color::Magenta
491    }
492    fn get_json_null_color(&self) -> Color {
493        Color::DarkGray
494    }
495    fn get_accent_yellow(&self) -> Color {
496        Color::Yellow
497    }
498}