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