Skip to main content

par_term_config/
themes.rs

1/// Color theme definitions for the terminal
2use serde::{Deserialize, Serialize};
3
4/// A color in RGB format
5#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
6pub struct Color {
7    pub r: u8,
8    pub g: u8,
9    pub b: u8,
10}
11
12impl Color {
13    pub const fn new(r: u8, g: u8, b: u8) -> Self {
14        Self { r, g, b }
15    }
16
17    pub fn as_array(&self) -> [u8; 3] {
18        [self.r, self.g, self.b]
19    }
20}
21
22/// Terminal color theme with 16 ANSI colors plus foreground/background
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Theme {
25    pub name: String,
26    pub foreground: Color,
27    pub background: Color,
28    pub cursor: Color,
29    pub selection_bg: Color,
30    pub selection_fg: Color,
31
32    // ANSI colors (0-15)
33    pub black: Color,
34    pub red: Color,
35    pub green: Color,
36    pub yellow: Color,
37    pub blue: Color,
38    pub magenta: Color,
39    pub cyan: Color,
40    pub white: Color,
41    pub bright_black: Color,
42    pub bright_red: Color,
43    pub bright_green: Color,
44    pub bright_yellow: Color,
45    pub bright_blue: Color,
46    pub bright_magenta: Color,
47    pub bright_cyan: Color,
48    pub bright_white: Color,
49}
50
51impl Theme {
52    /// Get ANSI color by index (0-15)
53    pub fn ansi_color(&self, index: u8) -> Color {
54        match index {
55            0 => self.black,
56            1 => self.red,
57            2 => self.green,
58            3 => self.yellow,
59            4 => self.blue,
60            5 => self.magenta,
61            6 => self.cyan,
62            7 => self.white,
63            8 => self.bright_black,
64            9 => self.bright_red,
65            10 => self.bright_green,
66            11 => self.bright_yellow,
67            12 => self.bright_blue,
68            13 => self.bright_magenta,
69            14 => self.bright_cyan,
70            15 => self.bright_white,
71            _ => self.foreground,
72        }
73    }
74
75    /// Dracula theme
76    pub fn dracula() -> Self {
77        Self {
78            name: "Dracula".to_string(),
79            foreground: Color::new(248, 248, 242),
80            background: Color::new(40, 42, 54),
81            cursor: Color::new(248, 248, 240),
82            selection_bg: Color::new(68, 71, 90),
83            selection_fg: Color::new(248, 248, 242),
84            black: Color::new(0, 0, 0),
85            red: Color::new(255, 85, 85),
86            green: Color::new(80, 250, 123),
87            yellow: Color::new(241, 250, 140),
88            blue: Color::new(189, 147, 249),
89            magenta: Color::new(255, 121, 198),
90            cyan: Color::new(139, 233, 253),
91            white: Color::new(255, 255, 255),
92            bright_black: Color::new(98, 114, 164),
93            bright_red: Color::new(255, 110, 103),
94            bright_green: Color::new(90, 247, 142),
95            bright_yellow: Color::new(244, 244, 161),
96            bright_blue: Color::new(189, 147, 249),
97            bright_magenta: Color::new(255, 121, 198),
98            bright_cyan: Color::new(139, 233, 253),
99            bright_white: Color::new(255, 255, 255),
100        }
101    }
102
103    /// Solarized Dark theme
104    pub fn solarized_dark() -> Self {
105        Self {
106            name: "Solarized Dark".to_string(),
107            foreground: Color::new(131, 148, 150),
108            background: Color::new(0, 43, 54),
109            cursor: Color::new(147, 161, 161),
110            selection_bg: Color::new(7, 54, 66),
111            selection_fg: Color::new(147, 161, 161),
112            black: Color::new(7, 54, 66),
113            red: Color::new(220, 50, 47),
114            green: Color::new(133, 153, 0),
115            yellow: Color::new(181, 137, 0),
116            blue: Color::new(38, 139, 210),
117            magenta: Color::new(211, 54, 130),
118            cyan: Color::new(42, 161, 152),
119            white: Color::new(238, 232, 213),
120            bright_black: Color::new(0, 43, 54),
121            bright_red: Color::new(203, 75, 22),
122            bright_green: Color::new(88, 110, 117),
123            bright_yellow: Color::new(101, 123, 131),
124            bright_blue: Color::new(131, 148, 150),
125            bright_magenta: Color::new(108, 113, 196),
126            bright_cyan: Color::new(147, 161, 161),
127            bright_white: Color::new(253, 246, 227),
128        }
129    }
130
131    /// Nord theme
132    pub fn nord() -> Self {
133        Self {
134            name: "Nord".to_string(),
135            foreground: Color::new(216, 222, 233),
136            background: Color::new(46, 52, 64),
137            cursor: Color::new(216, 222, 233),
138            selection_bg: Color::new(59, 66, 82),
139            selection_fg: Color::new(216, 222, 233),
140            black: Color::new(59, 66, 82),
141            red: Color::new(191, 97, 106),
142            green: Color::new(163, 190, 140),
143            yellow: Color::new(235, 203, 139),
144            blue: Color::new(129, 161, 193),
145            magenta: Color::new(180, 142, 173),
146            cyan: Color::new(136, 192, 208),
147            white: Color::new(229, 233, 240),
148            bright_black: Color::new(76, 86, 106),
149            bright_red: Color::new(191, 97, 106),
150            bright_green: Color::new(163, 190, 140),
151            bright_yellow: Color::new(235, 203, 139),
152            bright_blue: Color::new(129, 161, 193),
153            bright_magenta: Color::new(180, 142, 173),
154            bright_cyan: Color::new(143, 188, 187),
155            bright_white: Color::new(236, 239, 244),
156        }
157    }
158
159    /// Monokai theme
160    pub fn monokai() -> Self {
161        Self {
162            name: "Monokai".to_string(),
163            foreground: Color::new(248, 248, 242),
164            background: Color::new(39, 40, 34),
165            cursor: Color::new(253, 254, 236),
166            selection_bg: Color::new(73, 72, 62),
167            selection_fg: Color::new(248, 248, 242),
168            black: Color::new(39, 40, 34),
169            red: Color::new(249, 38, 114),
170            green: Color::new(166, 226, 46),
171            yellow: Color::new(244, 191, 117),
172            blue: Color::new(102, 217, 239),
173            magenta: Color::new(174, 129, 255),
174            cyan: Color::new(161, 239, 228),
175            white: Color::new(248, 248, 242),
176            bright_black: Color::new(117, 113, 94),
177            bright_red: Color::new(249, 38, 114),
178            bright_green: Color::new(166, 226, 46),
179            bright_yellow: Color::new(244, 191, 117),
180            bright_blue: Color::new(102, 217, 239),
181            bright_magenta: Color::new(174, 129, 255),
182            bright_cyan: Color::new(161, 239, 228),
183            bright_white: Color::new(249, 248, 245),
184        }
185    }
186
187    /// One Dark theme
188    pub fn one_dark() -> Self {
189        Self {
190            name: "One Dark".to_string(),
191            foreground: Color::new(171, 178, 191),
192            background: Color::new(40, 44, 52),
193            cursor: Color::new(171, 178, 191),
194            selection_bg: Color::new(56, 61, 71),
195            selection_fg: Color::new(171, 178, 191),
196            black: Color::new(40, 44, 52),
197            red: Color::new(224, 108, 117),
198            green: Color::new(152, 195, 121),
199            yellow: Color::new(229, 192, 123),
200            blue: Color::new(97, 175, 239),
201            magenta: Color::new(198, 120, 221),
202            cyan: Color::new(86, 182, 194),
203            white: Color::new(171, 178, 191),
204            bright_black: Color::new(92, 99, 112),
205            bright_red: Color::new(224, 108, 117),
206            bright_green: Color::new(152, 195, 121),
207            bright_yellow: Color::new(229, 192, 123),
208            bright_blue: Color::new(97, 175, 239),
209            bright_magenta: Color::new(198, 120, 221),
210            bright_cyan: Color::new(86, 182, 194),
211            bright_white: Color::new(255, 255, 255),
212        }
213    }
214
215    /// Default dark background theme
216    pub fn default_dark() -> Self {
217        Self {
218            name: "Default Dark".to_string(),
219            foreground: Color::new(255, 255, 255),
220            background: Color::new(0, 0, 0),
221            cursor: Color::new(255, 255, 255),
222            selection_bg: Color::new(100, 100, 100),
223            selection_fg: Color::new(255, 255, 255),
224            black: Color::new(0, 0, 0),
225            red: Color::new(205, 0, 0),
226            green: Color::new(0, 205, 0),
227            yellow: Color::new(205, 205, 0),
228            blue: Color::new(0, 0, 238),
229            magenta: Color::new(205, 0, 205),
230            cyan: Color::new(0, 205, 205),
231            white: Color::new(229, 229, 229),
232            bright_black: Color::new(127, 127, 127),
233            bright_red: Color::new(255, 0, 0),
234            bright_green: Color::new(0, 255, 0),
235            bright_yellow: Color::new(255, 255, 0),
236            bright_blue: Color::new(92, 92, 255),
237            bright_magenta: Color::new(255, 0, 255),
238            bright_cyan: Color::new(0, 255, 255),
239            bright_white: Color::new(255, 255, 255),
240        }
241    }
242
243    /// Dark Background theme (iTerm2)
244    pub fn dark_background() -> Self {
245        Self {
246            name: "Dark Background".to_string(),
247            foreground: Color::new(199, 199, 199),
248            background: Color::new(0, 0, 0),
249            cursor: Color::new(199, 199, 199),
250            selection_bg: Color::new(193, 222, 255),
251            selection_fg: Color::new(0, 0, 0),
252            black: Color::new(0, 0, 0),
253            red: Color::new(201, 27, 0),
254            green: Color::new(0, 194, 0),
255            yellow: Color::new(199, 196, 0),
256            blue: Color::new(2, 37, 199),
257            magenta: Color::new(202, 48, 199),
258            cyan: Color::new(0, 197, 199),
259            white: Color::new(199, 199, 199),
260            bright_black: Color::new(104, 104, 104),
261            bright_red: Color::new(255, 110, 103),
262            bright_green: Color::new(95, 250, 104),
263            bright_yellow: Color::new(255, 252, 103),
264            bright_blue: Color::new(104, 113, 255),
265            bright_magenta: Color::new(255, 119, 255),
266            bright_cyan: Color::new(96, 253, 255),
267            bright_white: Color::new(255, 255, 255),
268        }
269    }
270
271    /// High Contrast theme (iTerm2)
272    pub fn high_contrast() -> Self {
273        Self {
274            name: "High Contrast".to_string(),
275            foreground: Color::new(199, 199, 199),
276            background: Color::new(0, 0, 0),
277            cursor: Color::new(199, 199, 199),
278            selection_bg: Color::new(193, 222, 255),
279            selection_fg: Color::new(0, 0, 0),
280            black: Color::new(0, 0, 0),
281            red: Color::new(201, 27, 0),
282            green: Color::new(0, 194, 0),
283            yellow: Color::new(199, 196, 0),
284            blue: Color::new(2, 37, 199),
285            magenta: Color::new(202, 48, 199),
286            cyan: Color::new(0, 197, 199),
287            white: Color::new(199, 199, 199),
288            bright_black: Color::new(104, 104, 104),
289            bright_red: Color::new(255, 110, 103),
290            bright_green: Color::new(95, 250, 104),
291            bright_yellow: Color::new(255, 252, 103),
292            bright_blue: Color::new(104, 113, 255),
293            bright_magenta: Color::new(255, 119, 255),
294            bright_cyan: Color::new(96, 253, 255),
295            bright_white: Color::new(255, 255, 255),
296        }
297    }
298
299    /// Light Background theme (iTerm2)
300    pub fn light_background() -> Self {
301        Self {
302            name: "Light Background".to_string(),
303            foreground: Color::new(0, 0, 0),
304            background: Color::new(255, 255, 255),
305            cursor: Color::new(0, 0, 0),
306            selection_bg: Color::new(193, 222, 255),
307            selection_fg: Color::new(0, 0, 0),
308            black: Color::new(0, 0, 0),
309            red: Color::new(201, 27, 0),
310            green: Color::new(0, 194, 0),
311            yellow: Color::new(199, 196, 0),
312            blue: Color::new(2, 37, 199),
313            magenta: Color::new(202, 48, 199),
314            cyan: Color::new(0, 197, 199),
315            white: Color::new(199, 199, 199),
316            bright_black: Color::new(104, 104, 104),
317            bright_red: Color::new(255, 110, 103),
318            bright_green: Color::new(95, 250, 104),
319            bright_yellow: Color::new(255, 252, 103),
320            bright_blue: Color::new(104, 113, 255),
321            bright_magenta: Color::new(255, 119, 255),
322            bright_cyan: Color::new(96, 253, 255),
323            bright_white: Color::new(255, 255, 255),
324        }
325    }
326
327    /// Pastel theme (Dark Background)
328    pub fn pastel_dark() -> Self {
329        Self {
330            name: "Pastel (Dark Background)".to_string(),
331            foreground: Color::new(199, 199, 199),
332            background: Color::new(0, 0, 0),
333            cursor: Color::new(255, 180, 115),
334            selection_bg: Color::new(69, 77, 150),
335            selection_fg: Color::new(244, 244, 244),
336            black: Color::new(98, 98, 98),
337            red: Color::new(255, 131, 115),
338            green: Color::new(180, 251, 115),
339            yellow: Color::new(255, 253, 195),
340            blue: Color::new(165, 213, 254),
341            magenta: Color::new(255, 144, 254),
342            cyan: Color::new(209, 209, 254),
343            white: Color::new(241, 241, 241),
344            bright_black: Color::new(143, 143, 143),
345            bright_red: Color::new(255, 196, 190),
346            bright_green: Color::new(214, 252, 186),
347            bright_yellow: Color::new(255, 254, 213),
348            bright_blue: Color::new(194, 227, 255),
349            bright_magenta: Color::new(255, 178, 254),
350            bright_cyan: Color::new(230, 230, 254),
351            bright_white: Color::new(255, 255, 255),
352        }
353    }
354
355    /// Regular theme (light)
356    pub fn regular() -> Self {
357        Self {
358            name: "Regular".to_string(),
359            foreground: Color::new(16, 16, 16),
360            background: Color::new(250, 250, 250),
361            cursor: Color::new(0, 0, 0),
362            selection_bg: Color::new(179, 215, 255),
363            selection_fg: Color::new(0, 0, 0),
364            black: Color::new(20, 25, 30),
365            red: Color::new(180, 60, 42),
366            green: Color::new(0, 194, 0),
367            yellow: Color::new(199, 196, 0),
368            blue: Color::new(39, 68, 199),
369            magenta: Color::new(192, 64, 190),
370            cyan: Color::new(0, 197, 199),
371            white: Color::new(199, 199, 199),
372            bright_black: Color::new(104, 104, 104),
373            bright_red: Color::new(221, 121, 117),
374            bright_green: Color::new(88, 231, 144),
375            bright_yellow: Color::new(236, 225, 0),
376            bright_blue: Color::new(167, 171, 242),
377            bright_magenta: Color::new(225, 126, 225),
378            bright_cyan: Color::new(96, 253, 255),
379            bright_white: Color::new(255, 255, 255),
380        }
381    }
382
383    /// Smoooooth theme (dark)
384    pub fn smoooooth() -> Self {
385        Self {
386            name: "Smoooooth".to_string(),
387            foreground: Color::new(220, 220, 220),
388            background: Color::new(21, 25, 31),
389            cursor: Color::new(255, 255, 255),
390            selection_bg: Color::new(179, 215, 255),
391            selection_fg: Color::new(0, 0, 0),
392            black: Color::new(20, 25, 30),
393            red: Color::new(180, 60, 42),
394            green: Color::new(0, 194, 0),
395            yellow: Color::new(199, 196, 0),
396            blue: Color::new(39, 68, 199),
397            magenta: Color::new(192, 64, 190),
398            cyan: Color::new(0, 197, 199),
399            white: Color::new(199, 199, 199),
400            bright_black: Color::new(104, 104, 104),
401            bright_red: Color::new(221, 121, 117),
402            bright_green: Color::new(88, 231, 144),
403            bright_yellow: Color::new(236, 225, 0),
404            bright_blue: Color::new(167, 171, 242),
405            bright_magenta: Color::new(225, 126, 225),
406            bright_cyan: Color::new(96, 253, 255),
407            bright_white: Color::new(255, 255, 255),
408        }
409    }
410
411    /// Solarized base theme (dark)
412    pub fn solarized() -> Self {
413        Self {
414            name: "Solarized".to_string(),
415            foreground: Color::new(131, 148, 150),
416            background: Color::new(0, 43, 54),
417            cursor: Color::new(131, 148, 150),
418            selection_bg: Color::new(7, 54, 66),
419            selection_fg: Color::new(147, 161, 161),
420            black: Color::new(7, 54, 66),
421            red: Color::new(220, 50, 47),
422            green: Color::new(133, 153, 0),
423            yellow: Color::new(181, 137, 0),
424            blue: Color::new(38, 139, 210),
425            magenta: Color::new(211, 54, 130),
426            cyan: Color::new(42, 161, 152),
427            white: Color::new(238, 232, 213),
428            bright_black: Color::new(0, 43, 54),
429            bright_red: Color::new(203, 75, 22),
430            bright_green: Color::new(88, 110, 117),
431            bright_yellow: Color::new(101, 123, 131),
432            bright_blue: Color::new(131, 148, 150),
433            bright_magenta: Color::new(108, 113, 196),
434            bright_cyan: Color::new(147, 161, 161),
435            bright_white: Color::new(253, 246, 227),
436        }
437    }
438
439    /// Solarized Light theme
440    pub fn solarized_light() -> Self {
441        Self {
442            name: "Solarized Light".to_string(),
443            foreground: Color::new(101, 123, 131),
444            background: Color::new(253, 246, 227),
445            cursor: Color::new(88, 110, 117),
446            selection_bg: Color::new(238, 232, 213),
447            selection_fg: Color::new(88, 110, 117),
448            black: Color::new(238, 232, 213),
449            red: Color::new(220, 50, 47),
450            green: Color::new(133, 153, 0),
451            yellow: Color::new(181, 137, 0),
452            blue: Color::new(38, 139, 210),
453            magenta: Color::new(211, 54, 130),
454            cyan: Color::new(42, 161, 152),
455            white: Color::new(7, 54, 66),
456            bright_black: Color::new(253, 246, 227),
457            bright_red: Color::new(203, 75, 22),
458            bright_green: Color::new(147, 161, 161),
459            bright_yellow: Color::new(131, 148, 150),
460            bright_blue: Color::new(101, 123, 131),
461            bright_magenta: Color::new(108, 113, 196),
462            bright_cyan: Color::new(88, 110, 117),
463            bright_white: Color::new(0, 43, 54),
464        }
465    }
466
467    /// iTerm2 Dark default theme
468    pub fn iterm2_dark() -> Self {
469        Self {
470            name: "iTerm2 Dark".to_string(),
471            foreground: Color::new(178, 178, 178),
472            background: Color::new(0, 0, 0),
473            cursor: Color::new(255, 255, 255),
474            selection_bg: Color::new(179, 215, 255),
475            selection_fg: Color::new(0, 0, 0),
476            black: Color::new(0, 0, 0),
477            red: Color::new(171, 53, 37),
478            green: Color::new(87, 191, 55),
479            yellow: Color::new(198, 196, 63),
480            blue: Color::new(45, 66, 192),
481            magenta: Color::new(177, 72, 184),
482            cyan: Color::new(88, 194, 197),
483            white: Color::new(199, 199, 199),
484            bright_black: Color::new(103, 103, 103),
485            bright_red: Color::new(207, 126, 119),
486            bright_green: Color::new(129, 227, 151),
487            bright_yellow: Color::new(233, 221, 0),
488            bright_blue: Color::new(167, 170, 236),
489            bright_magenta: Color::new(211, 130, 219),
490            bright_cyan: Color::new(142, 249, 253),
491            bright_white: Color::new(254, 254, 254),
492        }
493    }
494
495    /// Tango Dark theme
496    pub fn tango_dark() -> Self {
497        Self {
498            name: "Tango Dark".to_string(),
499            foreground: Color::new(211, 215, 207),
500            background: Color::new(46, 52, 54),
501            cursor: Color::new(211, 215, 207),
502            selection_bg: Color::new(238, 238, 236),
503            selection_fg: Color::new(85, 87, 83),
504            black: Color::new(46, 52, 54),
505            red: Color::new(204, 0, 0),
506            green: Color::new(78, 154, 6),
507            yellow: Color::new(196, 160, 0),
508            blue: Color::new(52, 101, 164),
509            magenta: Color::new(117, 80, 123),
510            cyan: Color::new(6, 152, 154),
511            white: Color::new(211, 215, 207),
512            bright_black: Color::new(85, 87, 83),
513            bright_red: Color::new(239, 41, 41),
514            bright_green: Color::new(138, 226, 52),
515            bright_yellow: Color::new(252, 233, 79),
516            bright_blue: Color::new(114, 159, 207),
517            bright_magenta: Color::new(173, 127, 168),
518            bright_cyan: Color::new(52, 226, 226),
519            bright_white: Color::new(238, 238, 236),
520        }
521    }
522
523    /// Tango Light theme
524    pub fn tango_light() -> Self {
525        Self {
526            name: "Tango Light".to_string(),
527            foreground: Color::new(46, 52, 54),
528            background: Color::new(255, 255, 255),
529            cursor: Color::new(46, 52, 54),
530            selection_bg: Color::new(203, 228, 255),
531            selection_fg: Color::new(46, 52, 54),
532            black: Color::new(46, 52, 54),
533            red: Color::new(204, 0, 0),
534            green: Color::new(78, 154, 6),
535            yellow: Color::new(196, 160, 0),
536            blue: Color::new(52, 101, 164),
537            magenta: Color::new(117, 80, 123),
538            cyan: Color::new(6, 152, 154),
539            white: Color::new(211, 215, 207),
540            bright_black: Color::new(85, 87, 83),
541            bright_red: Color::new(239, 41, 41),
542            bright_green: Color::new(138, 226, 52),
543            bright_yellow: Color::new(252, 233, 79),
544            bright_blue: Color::new(114, 159, 207),
545            bright_magenta: Color::new(173, 127, 168),
546            bright_cyan: Color::new(52, 226, 226),
547            bright_white: Color::new(238, 238, 236),
548        }
549    }
550
551    /// Get theme by name
552    pub fn by_name(name: &str) -> Option<Self> {
553        let normalized = name.trim().to_lowercase().replace(['_', ' '], "-");
554
555        match normalized.as_str() {
556            "dracula" => Some(Self::dracula()),
557            "solarized" => Some(Self::solarized()),
558            "solarized-dark" => Some(Self::solarized_dark()),
559            "solarized-light" => Some(Self::solarized_light()),
560            "nord" => Some(Self::nord()),
561            "monokai" => Some(Self::monokai()),
562            "one-dark" | "onedark" => Some(Self::one_dark()),
563            "default-dark" | "default" => Some(Self::default_dark()),
564            "dark-background" => Some(Self::dark_background()),
565            "high-contrast" => Some(Self::high_contrast()),
566            "light-background" => Some(Self::light_background()),
567            "pastel-dark" => Some(Self::pastel_dark()),
568            "regular" => Some(Self::regular()),
569            "smoooooth" => Some(Self::smoooooth()),
570            "iterm2-dark" | "iterm2" => Some(Self::iterm2_dark()),
571            "tango-dark" => Some(Self::tango_dark()),
572            "tango-light" => Some(Self::tango_light()),
573            _ => None,
574        }
575    }
576
577    /// Get all available theme names
578    pub fn available_themes() -> Vec<&'static str> {
579        vec![
580            "Dark Background",
581            "Default Dark",
582            "Dracula",
583            "High Contrast",
584            "iTerm2 Dark",
585            "Light Background",
586            "Monokai",
587            "Nord",
588            "One Dark",
589            "Pastel (Dark Background)",
590            "Regular",
591            "Smoooooth",
592            "Solarized",
593            "Solarized Dark",
594            "Solarized Light",
595            "Tango Dark",
596            "Tango Light",
597        ]
598    }
599}
600
601impl Default for Theme {
602    fn default() -> Self {
603        Self::default_dark()
604    }
605}