Skip to main content

telex/
theme.rs

1//! Theming support for Telex applications.
2//!
3//! Provides customizable color schemes for UI elements.
4//!
5//! # Example
6//! ```rust,ignore
7//! use telex::theme::Theme;
8//!
9//! let theme = Theme::dark();
10//! // or customize:
11//! let custom = Theme::default()
12//!     .with_primary(Color::Cyan)
13//!     .with_background(Color::Black);
14//! ```
15
16use crossterm::style::Color;
17
18/// Color scheme for the application.
19#[derive(Debug, Clone)]
20pub struct Theme {
21    /// Primary accent color (focused elements, highlights)
22    pub primary: Color,
23    /// Secondary accent color
24    pub secondary: Color,
25    /// Background color
26    pub background: Color,
27    /// Foreground/text color
28    pub foreground: Color,
29    /// Muted/dimmed text color
30    pub muted: Color,
31    /// Error color
32    pub error: Color,
33    /// Success color
34    pub success: Color,
35    /// Warning color
36    pub warning: Color,
37    /// Border color
38    pub border: Color,
39    /// Focused border color
40    pub border_focused: Color,
41    /// Button background
42    pub button_bg: Color,
43    /// Button foreground
44    pub button_fg: Color,
45    /// Button focused background
46    pub button_focused_bg: Color,
47    /// Button focused foreground
48    pub button_focused_fg: Color,
49    /// Selection/highlight background
50    pub selection_bg: Color,
51    /// Selection/highlight foreground
52    pub selection_fg: Color,
53    /// Input field background
54    pub input_bg: Color,
55    /// Input field foreground
56    pub input_fg: Color,
57    /// Placeholder text color
58    pub placeholder: Color,
59    /// Cursor color (foreground of cursor character/block)
60    pub cursor: Color,
61    /// Cursor text color (background behind cursor, text color when on a character)
62    pub cursor_text: Color,
63}
64
65impl Default for Theme {
66    fn default() -> Self {
67        Self::dark()
68    }
69}
70
71impl Theme {
72    /// Create a dark theme (default).
73    pub fn dark() -> Self {
74        Self {
75            primary: Color::Cyan,
76            secondary: Color::Magenta,
77            background: Color::Reset,
78            foreground: Color::Reset,
79            muted: Color::DarkGrey,
80            error: Color::Red,
81            success: Color::Green,
82            warning: Color::Yellow,
83            border: Color::Reset,
84            border_focused: Color::Cyan,
85            button_bg: Color::Reset,
86            button_fg: Color::Grey,
87            button_focused_bg: Color::White,
88            button_focused_fg: Color::Black,
89            selection_bg: Color::Grey,  // Softer than White
90            selection_fg: Color::Black,
91            input_bg: Color::Reset,
92            input_fg: Color::Reset,
93            placeholder: Color::DarkGrey,
94            // Cursor must contrast with selection_bg (the "general highlight" when widget has focus).
95            // Since selection_bg=Grey, cursor must NOT be Grey or it disappears.
96            cursor: Color::Black,
97            cursor_text: Color::Grey,
98        }
99    }
100
101    /// Create a light theme.
102    pub fn light() -> Self {
103        Self {
104            primary: Color::Blue,
105            secondary: Color::Magenta,
106            background: Color::White,
107            foreground: Color::Black,
108            muted: Color::DarkGrey,
109            error: Color::Red,
110            success: Color::Green,
111            warning: Color::Yellow,
112            border: Color::DarkGrey,
113            border_focused: Color::Blue,
114            button_bg: Color::DarkGrey,
115            button_fg: Color::White,
116            button_focused_bg: Color::Blue,
117            button_focused_fg: Color::White,
118            selection_bg: Color::Blue,
119            selection_fg: Color::White,
120            input_bg: Color::White,
121            input_fg: Color::Black,
122            placeholder: Color::DarkGrey,
123            cursor: Color::Black,
124            cursor_text: Color::White,
125        }
126    }
127
128    /// Create a nord-inspired theme.
129    pub fn nord() -> Self {
130        Self {
131            primary: Color::Rgb {
132                r: 136,
133                g: 192,
134                b: 208,
135            }, // Nord8 - frost
136            secondary: Color::Rgb {
137                r: 180,
138                g: 142,
139                b: 173,
140            }, // Nord15 - aurora
141            background: Color::Rgb {
142                r: 46,
143                g: 52,
144                b: 64,
145            }, // Nord0 - polar night
146            foreground: Color::Rgb {
147                r: 236,
148                g: 239,
149                b: 244,
150            }, // Nord6 - snow storm
151            muted: Color::Rgb {
152                r: 76,
153                g: 86,
154                b: 106,
155            }, // Nord3
156            error: Color::Rgb {
157                r: 191,
158                g: 97,
159                b: 106,
160            }, // Nord11
161            success: Color::Rgb {
162                r: 163,
163                g: 190,
164                b: 140,
165            }, // Nord14
166            warning: Color::Rgb {
167                r: 235,
168                g: 203,
169                b: 139,
170            }, // Nord13
171            border: Color::Rgb {
172                r: 76,
173                g: 86,
174                b: 106,
175            }, // Nord3
176            border_focused: Color::Rgb {
177                r: 136,
178                g: 192,
179                b: 208,
180            }, // Nord8
181            button_bg: Color::Rgb {
182                r: 59,
183                g: 66,
184                b: 82,
185            }, // Nord1
186            button_fg: Color::Rgb {
187                r: 236,
188                g: 239,
189                b: 244,
190            }, // Nord6
191            button_focused_bg: Color::Rgb {
192                r: 136,
193                g: 192,
194                b: 208,
195            }, // Nord8
196            button_focused_fg: Color::Rgb {
197                r: 46,
198                g: 52,
199                b: 64,
200            }, // Nord0
201            selection_bg: Color::Rgb {
202                r: 136,
203                g: 192,
204                b: 208,
205            }, // Nord8
206            selection_fg: Color::Rgb {
207                r: 46,
208                g: 52,
209                b: 64,
210            }, // Nord0
211            input_bg: Color::Rgb {
212                r: 59,
213                g: 66,
214                b: 82,
215            }, // Nord1
216            input_fg: Color::Rgb {
217                r: 236,
218                g: 239,
219                b: 244,
220            }, // Nord6
221            placeholder: Color::Rgb {
222                r: 76,
223                g: 86,
224                b: 106,
225            }, // Nord3
226            cursor: Color::Rgb {
227                r: 236,
228                g: 239,
229                b: 244,
230            }, // Nord6 - snow storm (foreground)
231            cursor_text: Color::Rgb {
232                r: 46,
233                g: 52,
234                b: 64,
235            }, // Nord0 - polar night
236        }
237    }
238
239    /// Create a monokai-inspired theme.
240    pub fn monokai() -> Self {
241        Self {
242            primary: Color::Rgb {
243                r: 102,
244                g: 217,
245                b: 239,
246            }, // Cyan
247            secondary: Color::Rgb {
248                r: 174,
249                g: 129,
250                b: 255,
251            }, // Purple
252            background: Color::Rgb {
253                r: 39,
254                g: 40,
255                b: 34,
256            }, // Dark bg
257            foreground: Color::Rgb {
258                r: 248,
259                g: 248,
260                b: 242,
261            }, // Light fg
262            muted: Color::Rgb {
263                r: 117,
264                g: 113,
265                b: 94,
266            }, // Comment grey
267            error: Color::Rgb {
268                r: 249,
269                g: 38,
270                b: 114,
271            }, // Pink/red
272            success: Color::Rgb {
273                r: 166,
274                g: 226,
275                b: 46,
276            }, // Green
277            warning: Color::Rgb {
278                r: 253,
279                g: 151,
280                b: 31,
281            }, // Orange
282            border: Color::Rgb {
283                r: 117,
284                g: 113,
285                b: 94,
286            },
287            border_focused: Color::Rgb {
288                r: 102,
289                g: 217,
290                b: 239,
291            },
292            button_bg: Color::Rgb {
293                r: 73,
294                g: 72,
295                b: 62,
296            },
297            button_fg: Color::Rgb {
298                r: 248,
299                g: 248,
300                b: 242,
301            },
302            button_focused_bg: Color::Rgb {
303                r: 102,
304                g: 217,
305                b: 239,
306            },
307            button_focused_fg: Color::Rgb {
308                r: 39,
309                g: 40,
310                b: 34,
311            },
312            selection_bg: Color::Rgb {
313                r: 102,
314                g: 217,
315                b: 239,
316            },
317            selection_fg: Color::Rgb {
318                r: 39,
319                g: 40,
320                b: 34,
321            },
322            input_bg: Color::Rgb {
323                r: 73,
324                g: 72,
325                b: 62,
326            },
327            input_fg: Color::Rgb {
328                r: 248,
329                g: 248,
330                b: 242,
331            },
332            placeholder: Color::Rgb {
333                r: 117,
334                g: 113,
335                b: 94,
336            },
337            cursor: Color::Rgb {
338                r: 248,
339                g: 248,
340                b: 242,
341            }, // Light fg (foreground)
342            cursor_text: Color::Rgb {
343                r: 39,
344                g: 40,
345                b: 34,
346            }, // Dark bg
347        }
348    }
349
350    /// Create a Catppuccin Mocha theme (dark).
351    pub fn catppuccin_mocha() -> Self {
352        Self {
353            primary: Color::Rgb {
354                r: 137,
355                g: 180,
356                b: 250,
357            }, // Blue
358            secondary: Color::Rgb {
359                r: 203,
360                g: 166,
361                b: 247,
362            }, // Mauve
363            background: Color::Rgb {
364                r: 30,
365                g: 30,
366                b: 46,
367            }, // Base
368            foreground: Color::Rgb {
369                r: 205,
370                g: 214,
371                b: 244,
372            }, // Text
373            muted: Color::Rgb {
374                r: 108,
375                g: 112,
376                b: 134,
377            }, // Overlay0
378            error: Color::Rgb {
379                r: 243,
380                g: 139,
381                b: 168,
382            }, // Red
383            success: Color::Rgb {
384                r: 166,
385                g: 227,
386                b: 161,
387            }, // Green
388            warning: Color::Rgb {
389                r: 249,
390                g: 226,
391                b: 175,
392            }, // Yellow
393            border: Color::Rgb {
394                r: 69,
395                g: 71,
396                b: 90,
397            }, // Surface1
398            border_focused: Color::Rgb {
399                r: 137,
400                g: 180,
401                b: 250,
402            }, // Blue
403            button_bg: Color::Rgb {
404                r: 49,
405                g: 50,
406                b: 68,
407            }, // Surface0
408            button_fg: Color::Rgb {
409                r: 205,
410                g: 214,
411                b: 244,
412            }, // Text
413            button_focused_bg: Color::Rgb {
414                r: 137,
415                g: 180,
416                b: 250,
417            }, // Blue
418            button_focused_fg: Color::Rgb {
419                r: 30,
420                g: 30,
421                b: 46,
422            }, // Base
423            selection_bg: Color::Rgb {
424                r: 137,
425                g: 180,
426                b: 250,
427            }, // Blue
428            selection_fg: Color::Rgb {
429                r: 30,
430                g: 30,
431                b: 46,
432            }, // Base
433            input_bg: Color::Rgb {
434                r: 49,
435                g: 50,
436                b: 68,
437            }, // Surface0
438            input_fg: Color::Rgb {
439                r: 205,
440                g: 214,
441                b: 244,
442            }, // Text
443            placeholder: Color::Rgb {
444                r: 108,
445                g: 112,
446                b: 134,
447            }, // Overlay0
448            cursor: Color::Rgb {
449                r: 205,
450                g: 214,
451                b: 244,
452            }, // Text (foreground)
453            cursor_text: Color::Rgb {
454                r: 30,
455                g: 30,
456                b: 46,
457            }, // Base
458        }
459    }
460
461    /// Create a Catppuccin Latte theme (light).
462    pub fn catppuccin_latte() -> Self {
463        Self {
464            primary: Color::Rgb {
465                r: 30,
466                g: 102,
467                b: 245,
468            }, // Blue
469            secondary: Color::Rgb {
470                r: 136,
471                g: 57,
472                b: 239,
473            }, // Mauve
474            background: Color::Rgb {
475                r: 239,
476                g: 241,
477                b: 245,
478            }, // Base
479            foreground: Color::Rgb {
480                r: 76,
481                g: 79,
482                b: 105,
483            }, // Text
484            muted: Color::Rgb {
485                r: 156,
486                g: 160,
487                b: 176,
488            }, // Overlay0
489            error: Color::Rgb {
490                r: 210,
491                g: 15,
492                b: 57,
493            }, // Red
494            success: Color::Rgb {
495                r: 64,
496                g: 160,
497                b: 43,
498            }, // Green
499            warning: Color::Rgb {
500                r: 223,
501                g: 142,
502                b: 29,
503            }, // Yellow
504            border: Color::Rgb {
505                r: 188,
506                g: 192,
507                b: 204,
508            }, // Surface1
509            border_focused: Color::Rgb {
510                r: 30,
511                g: 102,
512                b: 245,
513            }, // Blue
514            button_bg: Color::Rgb {
515                r: 204,
516                g: 208,
517                b: 218,
518            }, // Surface0
519            button_fg: Color::Rgb {
520                r: 76,
521                g: 79,
522                b: 105,
523            }, // Text
524            button_focused_bg: Color::Rgb {
525                r: 30,
526                g: 102,
527                b: 245,
528            }, // Blue
529            button_focused_fg: Color::Rgb {
530                r: 239,
531                g: 241,
532                b: 245,
533            }, // Base
534            selection_bg: Color::Rgb {
535                r: 30,
536                g: 102,
537                b: 245,
538            }, // Blue
539            selection_fg: Color::Rgb {
540                r: 239,
541                g: 241,
542                b: 245,
543            }, // Base
544            input_bg: Color::Rgb {
545                r: 204,
546                g: 208,
547                b: 218,
548            }, // Surface0
549            input_fg: Color::Rgb {
550                r: 76,
551                g: 79,
552                b: 105,
553            }, // Text
554            placeholder: Color::Rgb {
555                r: 156,
556                g: 160,
557                b: 176,
558            }, // Overlay0
559            cursor: Color::Rgb {
560                r: 76,
561                g: 79,
562                b: 105,
563            }, // Text (foreground)
564            cursor_text: Color::Rgb {
565                r: 239,
566                g: 241,
567                b: 245,
568            }, // Base
569        }
570    }
571
572    /// Create a Dracula theme.
573    pub fn dracula() -> Self {
574        Self {
575            primary: Color::Rgb {
576                r: 139,
577                g: 233,
578                b: 253,
579            }, // Cyan
580            secondary: Color::Rgb {
581                r: 189,
582                g: 147,
583                b: 249,
584            }, // Purple
585            background: Color::Rgb {
586                r: 40,
587                g: 42,
588                b: 54,
589            }, // Background
590            foreground: Color::Rgb {
591                r: 248,
592                g: 248,
593                b: 242,
594            }, // Foreground
595            muted: Color::Rgb {
596                r: 98,
597                g: 114,
598                b: 164,
599            }, // Comment
600            error: Color::Rgb {
601                r: 255,
602                g: 85,
603                b: 85,
604            }, // Red
605            success: Color::Rgb {
606                r: 80,
607                g: 250,
608                b: 123,
609            }, // Green
610            warning: Color::Rgb {
611                r: 255,
612                g: 184,
613                b: 108,
614            }, // Orange
615            border: Color::Rgb {
616                r: 68,
617                g: 71,
618                b: 90,
619            }, // Selection
620            border_focused: Color::Rgb {
621                r: 139,
622                g: 233,
623                b: 253,
624            }, // Cyan
625            button_bg: Color::Rgb {
626                r: 68,
627                g: 71,
628                b: 90,
629            }, // Selection
630            button_fg: Color::Rgb {
631                r: 248,
632                g: 248,
633                b: 242,
634            }, // Foreground
635            button_focused_bg: Color::Rgb {
636                r: 139,
637                g: 233,
638                b: 253,
639            }, // Cyan
640            button_focused_fg: Color::Rgb {
641                r: 40,
642                g: 42,
643                b: 54,
644            }, // Background
645            selection_bg: Color::Rgb {
646                r: 68,
647                g: 71,
648                b: 90,
649            }, // Selection
650            selection_fg: Color::Rgb {
651                r: 248,
652                g: 248,
653                b: 242,
654            }, // Foreground
655            input_bg: Color::Rgb {
656                r: 68,
657                g: 71,
658                b: 90,
659            }, // Selection
660            input_fg: Color::Rgb {
661                r: 248,
662                g: 248,
663                b: 242,
664            }, // Foreground
665            placeholder: Color::Rgb {
666                r: 98,
667                g: 114,
668                b: 164,
669            }, // Comment
670            cursor: Color::Rgb {
671                r: 248,
672                g: 248,
673                b: 242,
674            }, // Foreground
675            cursor_text: Color::Rgb {
676                r: 40,
677                g: 42,
678                b: 54,
679            }, // Background
680        }
681    }
682
683    /// Create a Gruvbox Dark theme.
684    pub fn gruvbox_dark() -> Self {
685        Self {
686            primary: Color::Rgb {
687                r: 131,
688                g: 165,
689                b: 152,
690            }, // Blue
691            secondary: Color::Rgb {
692                r: 211,
693                g: 134,
694                b: 155,
695            }, // Purple
696            background: Color::Rgb {
697                r: 40,
698                g: 40,
699                b: 40,
700            }, // bg0
701            foreground: Color::Rgb {
702                r: 235,
703                g: 219,
704                b: 178,
705            }, // fg (light1)
706            muted: Color::Rgb {
707                r: 146,
708                g: 131,
709                b: 116,
710            }, // gray
711            error: Color::Rgb {
712                r: 251,
713                g: 73,
714                b: 52,
715            }, // Red bright
716            success: Color::Rgb {
717                r: 184,
718                g: 187,
719                b: 38,
720            }, // Green bright
721            warning: Color::Rgb {
722                r: 250,
723                g: 189,
724                b: 47,
725            }, // Yellow bright
726            border: Color::Rgb {
727                r: 80,
728                g: 73,
729                b: 69,
730            }, // bg2
731            border_focused: Color::Rgb {
732                r: 131,
733                g: 165,
734                b: 152,
735            }, // Blue
736            button_bg: Color::Rgb {
737                r: 60,
738                g: 56,
739                b: 54,
740            }, // bg1
741            button_fg: Color::Rgb {
742                r: 235,
743                g: 219,
744                b: 178,
745            }, // fg
746            button_focused_bg: Color::Rgb {
747                r: 131,
748                g: 165,
749                b: 152,
750            }, // Blue
751            button_focused_fg: Color::Rgb {
752                r: 40,
753                g: 40,
754                b: 40,
755            }, // bg0
756            selection_bg: Color::Rgb {
757                r: 131,
758                g: 165,
759                b: 152,
760            }, // Blue
761            selection_fg: Color::Rgb {
762                r: 40,
763                g: 40,
764                b: 40,
765            }, // bg0
766            input_bg: Color::Rgb {
767                r: 60,
768                g: 56,
769                b: 54,
770            }, // bg1
771            input_fg: Color::Rgb {
772                r: 235,
773                g: 219,
774                b: 178,
775            }, // fg
776            placeholder: Color::Rgb {
777                r: 146,
778                g: 131,
779                b: 116,
780            }, // gray
781            cursor: Color::Rgb {
782                r: 235,
783                g: 219,
784                b: 178,
785            }, // fg (foreground)
786            cursor_text: Color::Rgb {
787                r: 40,
788                g: 40,
789                b: 40,
790            }, // bg0
791        }
792    }
793
794    /// Create a Solarized Dark theme.
795    pub fn solarized_dark() -> Self {
796        Self {
797            primary: Color::Rgb {
798                r: 38,
799                g: 139,
800                b: 210,
801            }, // Blue
802            secondary: Color::Rgb {
803                r: 108,
804                g: 113,
805                b: 196,
806            }, // Violet
807            background: Color::Rgb { r: 0, g: 43, b: 54 }, // base03
808            foreground: Color::Rgb {
809                r: 131,
810                g: 148,
811                b: 150,
812            }, // base0
813            muted: Color::Rgb {
814                r: 88,
815                g: 110,
816                b: 117,
817            }, // base01
818            error: Color::Rgb {
819                r: 220,
820                g: 50,
821                b: 47,
822            }, // Red
823            success: Color::Rgb {
824                r: 133,
825                g: 153,
826                b: 0,
827            }, // Green
828            warning: Color::Rgb {
829                r: 181,
830                g: 137,
831                b: 0,
832            }, // Yellow
833            border: Color::Rgb { r: 7, g: 54, b: 66 },     // base02
834            border_focused: Color::Rgb {
835                r: 38,
836                g: 139,
837                b: 210,
838            }, // Blue
839            button_bg: Color::Rgb { r: 7, g: 54, b: 66 },  // base02
840            button_fg: Color::Rgb {
841                r: 131,
842                g: 148,
843                b: 150,
844            }, // base0
845            button_focused_bg: Color::Rgb {
846                r: 38,
847                g: 139,
848                b: 210,
849            }, // Blue
850            button_focused_fg: Color::Rgb {
851                r: 253,
852                g: 246,
853                b: 227,
854            }, // base3
855            selection_bg: Color::Rgb {
856                r: 38,
857                g: 139,
858                b: 210,
859            }, // Blue
860            selection_fg: Color::Rgb {
861                r: 253,
862                g: 246,
863                b: 227,
864            }, // base3
865            input_bg: Color::Rgb { r: 7, g: 54, b: 66 },   // base02
866            input_fg: Color::Rgb {
867                r: 131,
868                g: 148,
869                b: 150,
870            }, // base0
871            placeholder: Color::Rgb {
872                r: 88,
873                g: 110,
874                b: 117,
875            }, // base01
876            cursor: Color::Rgb {
877                r: 131,
878                g: 148,
879                b: 150,
880            }, // base0 (foreground)
881            cursor_text: Color::Rgb {
882                r: 0,
883                g: 43,
884                b: 54,
885            }, // base03
886        }
887    }
888
889    /// Create a Rosé Pine theme (dark).
890    pub fn rose_pine() -> Self {
891        Self {
892            primary: Color::Rgb {
893                r: 156,
894                g: 207,
895                b: 216,
896            }, // Foam
897            secondary: Color::Rgb {
898                r: 196,
899                g: 167,
900                b: 231,
901            }, // Iris
902            background: Color::Rgb {
903                r: 25,
904                g: 23,
905                b: 36,
906            }, // Base
907            foreground: Color::Rgb {
908                r: 224,
909                g: 222,
910                b: 244,
911            }, // Text
912            muted: Color::Rgb {
913                r: 110,
914                g: 106,
915                b: 134,
916            }, // Muted
917            error: Color::Rgb {
918                r: 235,
919                g: 111,
920                b: 146,
921            }, // Love
922            success: Color::Rgb {
923                r: 49,
924                g: 116,
925                b: 143,
926            }, // Pine
927            warning: Color::Rgb {
928                r: 246,
929                g: 193,
930                b: 119,
931            }, // Gold
932            border: Color::Rgb {
933                r: 31,
934                g: 29,
935                b: 46,
936            }, // Surface
937            border_focused: Color::Rgb {
938                r: 156,
939                g: 207,
940                b: 216,
941            }, // Foam
942            button_bg: Color::Rgb {
943                r: 31,
944                g: 29,
945                b: 46,
946            }, // Surface
947            button_fg: Color::Rgb {
948                r: 224,
949                g: 222,
950                b: 244,
951            }, // Text
952            button_focused_bg: Color::Rgb {
953                r: 156,
954                g: 207,
955                b: 216,
956            }, // Foam
957            button_focused_fg: Color::Rgb {
958                r: 25,
959                g: 23,
960                b: 36,
961            }, // Base
962            selection_bg: Color::Rgb {
963                r: 156,
964                g: 207,
965                b: 216,
966            }, // Foam
967            selection_fg: Color::Rgb {
968                r: 25,
969                g: 23,
970                b: 36,
971            }, // Base
972            input_bg: Color::Rgb {
973                r: 31,
974                g: 29,
975                b: 46,
976            }, // Surface
977            input_fg: Color::Rgb {
978                r: 224,
979                g: 222,
980                b: 244,
981            }, // Text
982            placeholder: Color::Rgb {
983                r: 110,
984                g: 106,
985                b: 134,
986            }, // Muted
987            cursor: Color::Rgb {
988                r: 224,
989                g: 222,
990                b: 244,
991            }, // Text (foreground)
992            cursor_text: Color::Rgb {
993                r: 25,
994                g: 23,
995                b: 36,
996            }, // Base
997        }
998    }
999
1000    /// Create a HaX0R Blue theme (monochrome cyan/blue hacker style).
1001    pub fn hax0r_blue() -> Self {
1002        Self {
1003            primary: Color::Rgb {
1004                r: 16,
1005                g: 182,
1006                b: 255,
1007            }, // Bright cyan
1008            secondary: Color::Rgb {
1009                r: 0,
1010                g: 179,
1011                b: 247,
1012            }, // Cyan
1013            background: Color::Rgb { r: 1, g: 5, b: 21 }, // Dark navy
1014            foreground: Color::Rgb {
1015                r: 17,
1016                g: 183,
1017                b: 255,
1018            }, // Cyan text
1019            muted: Color::Rgb {
1020                r: 72,
1021                g: 65,
1022                b: 87,
1023            }, // Muted purple-gray
1024            error: Color::Rgb {
1025                r: 16,
1026                g: 182,
1027                b: 255,
1028            }, // Cyan (monochrome)
1029            success: Color::Rgb {
1030                r: 16,
1031                g: 182,
1032                b: 255,
1033            }, // Cyan (monochrome)
1034            warning: Color::Rgb {
1035                r: 16,
1036                g: 182,
1037                b: 255,
1038            }, // Cyan (monochrome)
1039            border: Color::Rgb {
1040                r: 16,
1041                g: 182,
1042                b: 255,
1043            }, // Cyan
1044            border_focused: Color::Rgb {
1045                r: 250,
1046                g: 250,
1047                b: 250,
1048            }, // White
1049            button_bg: Color::Rgb { r: 1, g: 9, b: 33 },  // Slightly lighter bg
1050            button_fg: Color::Rgb {
1051                r: 16,
1052                g: 182,
1053                b: 255,
1054            }, // Cyan
1055            button_focused_bg: Color::Rgb {
1056                r: 16,
1057                g: 182,
1058                b: 255,
1059            }, // Cyan
1060            button_focused_fg: Color::Rgb { r: 1, g: 5, b: 21 }, // Dark bg
1061            selection_bg: Color::Rgb {
1062                r: 193,
1063                g: 228,
1064                b: 255,
1065            }, // Light cyan
1066            selection_fg: Color::Rgb { r: 1, g: 5, b: 21 }, // Dark bg
1067            input_bg: Color::Rgb { r: 1, g: 9, b: 33 },   // Slightly lighter bg
1068            input_fg: Color::Rgb {
1069                r: 16,
1070                g: 182,
1071                b: 255,
1072            }, // Cyan
1073            placeholder: Color::Rgb {
1074                r: 72,
1075                g: 65,
1076                b: 87,
1077            }, // Muted
1078            cursor: Color::Rgb {
1079                r: 17,
1080                g: 183,
1081                b: 255,
1082            }, // Foreground cyan
1083            cursor_text: Color::Rgb { r: 1, g: 5, b: 21 }, // Dark navy
1084        }
1085    }
1086
1087    /// Create a HaX0R Green theme (monochrome green hacker style).
1088    pub fn hax0r_green() -> Self {
1089        Self {
1090            primary: Color::Rgb {
1091                r: 21,
1092                g: 208,
1093                b: 13,
1094            }, // Bright green
1095            secondary: Color::Rgb {
1096                r: 25,
1097                g: 226,
1098                b: 14,
1099            }, // Green
1100            background: Color::Rgb { r: 2, g: 15, b: 1 }, // Dark green-black
1101            foreground: Color::Rgb {
1102                r: 22,
1103                g: 177,
1104                b: 14,
1105            }, // Green text
1106            muted: Color::Rgb {
1107                r: 51,
1108                g: 72,
1109                b: 67,
1110            }, // Muted green-gray
1111            error: Color::Rgb {
1112                r: 21,
1113                g: 208,
1114                b: 13,
1115            }, // Green (monochrome)
1116            success: Color::Rgb {
1117                r: 21,
1118                g: 208,
1119                b: 13,
1120            }, // Green (monochrome)
1121            warning: Color::Rgb {
1122                r: 21,
1123                g: 208,
1124                b: 13,
1125            }, // Green (monochrome)
1126            border: Color::Rgb {
1127                r: 21,
1128                g: 208,
1129                b: 13,
1130            }, // Green
1131            border_focused: Color::Rgb {
1132                r: 250,
1133                g: 250,
1134                b: 250,
1135            }, // White
1136            button_bg: Color::Rgb { r: 0, g: 31, b: 11 }, // Slightly lighter bg
1137            button_fg: Color::Rgb {
1138                r: 21,
1139                g: 208,
1140                b: 13,
1141            }, // Green
1142            button_focused_bg: Color::Rgb {
1143                r: 21,
1144                g: 208,
1145                b: 13,
1146            }, // Green
1147            button_focused_fg: Color::Rgb { r: 2, g: 15, b: 1 }, // Dark bg
1148            selection_bg: Color::Rgb {
1149                r: 212,
1150                g: 255,
1151                b: 193,
1152            }, // Light green
1153            selection_fg: Color::Rgb { r: 2, g: 15, b: 1 }, // Dark bg
1154            input_bg: Color::Rgb { r: 0, g: 31, b: 11 },  // Slightly lighter bg
1155            input_fg: Color::Rgb {
1156                r: 21,
1157                g: 208,
1158                b: 13,
1159            }, // Green
1160            placeholder: Color::Rgb {
1161                r: 51,
1162                g: 72,
1163                b: 67,
1164            }, // Muted
1165            cursor: Color::Rgb {
1166                r: 22,
1167                g: 177,
1168                b: 14,
1169            }, // Foreground green
1170            cursor_text: Color::Rgb { r: 2, g: 15, b: 1 }, // Dark bg
1171        }
1172    }
1173
1174    /// Create a HaX0R Red theme (monochrome red hacker style).
1175    pub fn hax0r_red() -> Self {
1176        Self {
1177            primary: Color::Rgb {
1178                r: 176,
1179                g: 13,
1180                b: 13,
1181            }, // Dark red
1182            secondary: Color::Rgb {
1183                r: 255,
1184                g: 17,
1185                b: 17,
1186            }, // Bright red
1187            background: Color::Rgb { r: 32, g: 1, b: 1 }, // Dark red-black
1188            foreground: Color::Rgb {
1189                r: 177,
1190                g: 14,
1191                b: 14,
1192            }, // Red text
1193            muted: Color::Rgb {
1194                r: 85,
1195                g: 64,
1196                b: 64,
1197            }, // Muted red-gray
1198            error: Color::Rgb {
1199                r: 255,
1200                g: 17,
1201                b: 17,
1202            }, // Bright red
1203            success: Color::Rgb {
1204                r: 176,
1205                g: 13,
1206                b: 13,
1207            }, // Red (monochrome)
1208            warning: Color::Rgb {
1209                r: 176,
1210                g: 13,
1211                b: 13,
1212            }, // Red (monochrome)
1213            border: Color::Rgb {
1214                r: 176,
1215                g: 13,
1216                b: 13,
1217            }, // Red
1218            border_focused: Color::Rgb {
1219                r: 250,
1220                g: 250,
1221                b: 250,
1222            }, // White
1223            button_bg: Color::Rgb { r: 31, g: 0, b: 0 },  // Slightly lighter bg
1224            button_fg: Color::Rgb {
1225                r: 176,
1226                g: 13,
1227                b: 13,
1228            }, // Red
1229            button_focused_bg: Color::Rgb {
1230                r: 176,
1231                g: 13,
1232                b: 13,
1233            }, // Red
1234            button_focused_fg: Color::Rgb { r: 32, g: 1, b: 1 }, // Dark bg
1235            selection_bg: Color::Rgb {
1236                r: 235,
1237                g: 193,
1238                b: 255,
1239            }, // Light pink
1240            selection_fg: Color::Rgb { r: 32, g: 1, b: 1 }, // Dark bg
1241            input_bg: Color::Rgb { r: 31, g: 0, b: 0 },   // Slightly lighter bg
1242            input_fg: Color::Rgb {
1243                r: 176,
1244                g: 13,
1245                b: 13,
1246            }, // Red
1247            placeholder: Color::Rgb {
1248                r: 85,
1249                g: 64,
1250                b: 64,
1251            }, // Muted
1252            cursor: Color::Rgb {
1253                r: 177,
1254                g: 14,
1255                b: 14,
1256            }, // Foreground red
1257            cursor_text: Color::Rgb { r: 32, g: 1, b: 1 }, // Dark bg
1258        }
1259    }
1260
1261    /// Create a Tokyo Night theme (dark).
1262    pub fn tokyo_night() -> Self {
1263        Self {
1264            primary: Color::Rgb {
1265                r: 122,
1266                g: 162,
1267                b: 247,
1268            }, // Blue
1269            secondary: Color::Rgb {
1270                r: 187,
1271                g: 154,
1272                b: 247,
1273            }, // Magenta
1274            background: Color::Rgb {
1275                r: 26,
1276                g: 27,
1277                b: 38,
1278            }, // bg
1279            foreground: Color::Rgb {
1280                r: 192,
1281                g: 202,
1282                b: 245,
1283            }, // fg
1284            muted: Color::Rgb {
1285                r: 65,
1286                g: 72,
1287                b: 104,
1288            }, // Bright black
1289            error: Color::Rgb {
1290                r: 247,
1291                g: 118,
1292                b: 142,
1293            }, // Red
1294            success: Color::Rgb {
1295                r: 158,
1296                g: 206,
1297                b: 106,
1298            }, // Green
1299            warning: Color::Rgb {
1300                r: 224,
1301                g: 175,
1302                b: 104,
1303            }, // Yellow
1304            border: Color::Rgb {
1305                r: 41,
1306                g: 46,
1307                b: 66,
1308            }, // Surface
1309            border_focused: Color::Rgb {
1310                r: 122,
1311                g: 162,
1312                b: 247,
1313            }, // Blue
1314            button_bg: Color::Rgb {
1315                r: 41,
1316                g: 46,
1317                b: 66,
1318            }, // Surface
1319            button_fg: Color::Rgb {
1320                r: 192,
1321                g: 202,
1322                b: 245,
1323            }, // fg
1324            button_focused_bg: Color::Rgb {
1325                r: 122,
1326                g: 162,
1327                b: 247,
1328            }, // Blue
1329            button_focused_fg: Color::Rgb {
1330                r: 26,
1331                g: 27,
1332                b: 38,
1333            }, // bg
1334            selection_bg: Color::Rgb {
1335                r: 122,
1336                g: 162,
1337                b: 247,
1338            }, // Blue
1339            selection_fg: Color::Rgb {
1340                r: 26,
1341                g: 27,
1342                b: 38,
1343            }, // bg
1344            input_bg: Color::Rgb {
1345                r: 41,
1346                g: 46,
1347                b: 66,
1348            }, // Surface
1349            input_fg: Color::Rgb {
1350                r: 192,
1351                g: 202,
1352                b: 245,
1353            }, // fg
1354            placeholder: Color::Rgb {
1355                r: 65,
1356                g: 72,
1357                b: 104,
1358            }, // Bright black
1359            cursor: Color::Rgb {
1360                r: 192,
1361                g: 202,
1362                b: 245,
1363            }, // fg (foreground)
1364            cursor_text: Color::Rgb {
1365                r: 26,
1366                g: 27,
1367                b: 38,
1368            }, // bg
1369        }
1370    }
1371
1372    // Builder methods for customization
1373
1374    /// Set the primary accent color.
1375    pub fn with_primary(mut self, color: Color) -> Self {
1376        self.primary = color;
1377        self
1378    }
1379
1380    /// Set the secondary accent color.
1381    pub fn with_secondary(mut self, color: Color) -> Self {
1382        self.secondary = color;
1383        self
1384    }
1385
1386    /// Set the background color.
1387    pub fn with_background(mut self, color: Color) -> Self {
1388        self.background = color;
1389        self
1390    }
1391
1392    /// Set the foreground/text color.
1393    pub fn with_foreground(mut self, color: Color) -> Self {
1394        self.foreground = color;
1395        self
1396    }
1397
1398    /// Set the error color.
1399    pub fn with_error(mut self, color: Color) -> Self {
1400        self.error = color;
1401        self
1402    }
1403
1404    /// Set the success color.
1405    pub fn with_success(mut self, color: Color) -> Self {
1406        self.success = color;
1407        self
1408    }
1409
1410    /// Set the warning color.
1411    pub fn with_warning(mut self, color: Color) -> Self {
1412        self.warning = color;
1413        self
1414    }
1415}
1416
1417/// Global theme storage (thread-local for now).
1418use std::cell::RefCell;
1419
1420thread_local! {
1421    static CURRENT_THEME: RefCell<Theme> = RefCell::new(Theme::default());
1422}
1423
1424/// Set the current theme.
1425pub fn set_theme(theme: Theme) {
1426    CURRENT_THEME.with(|t| {
1427        *t.borrow_mut() = theme;
1428    });
1429}
1430
1431/// Get the current theme.
1432pub fn current_theme() -> Theme {
1433    CURRENT_THEME.with(|t| t.borrow().clone())
1434}
1435
1436/// Get a specific color from the current theme.
1437pub fn themed_color<F>(f: F) -> Color
1438where
1439    F: FnOnce(&Theme) -> Color,
1440{
1441    CURRENT_THEME.with(|t| f(&t.borrow()))
1442}
1443
1444/// Check if the terminal supports true color (24-bit RGB).
1445///
1446/// Returns `true` if the terminal likely supports RGB colors.
1447/// Returns `false` if running in a terminal known not to support true color
1448/// (e.g., Apple Terminal.app).
1449pub fn supports_true_color() -> bool {
1450    use std::env;
1451
1452    // Apple Terminal doesn't support true color
1453    if let Ok(term_program) = env::var("TERM_PROGRAM") {
1454        if term_program == "Apple_Terminal" {
1455            return false;
1456        }
1457    }
1458
1459    // Check for explicit true color support
1460    if let Ok(colorterm) = env::var("COLORTERM") {
1461        if colorterm == "truecolor" || colorterm == "24bit" {
1462            return true;
1463        }
1464    }
1465
1466    // Known true color terminals
1467    if let Ok(term) = env::var("TERM") {
1468        if term.contains("256color") || term.contains("truecolor") || term.contains("24bit") {
1469            // 256color doesn't guarantee true color, but many modern terminals
1470            // that set this also support true color
1471        }
1472    }
1473
1474    // Check for specific terminal programs known to support true color
1475    if let Ok(term_program) = env::var("TERM_PROGRAM") {
1476        let true_color_terminals = [
1477            "iTerm.app",
1478            "Hyper",
1479            "vscode",
1480            "Ghostty",
1481            "WezTerm",
1482            "Alacritty",
1483            "kitty",
1484        ];
1485        if true_color_terminals
1486            .iter()
1487            .any(|t| term_program.contains(t))
1488        {
1489            return true;
1490        }
1491    }
1492
1493    // Default: assume true color is supported (most modern terminals do)
1494    // User can check the warning if colors look wrong
1495    true
1496}
1497
1498/// Get the name of the current terminal, if detectable.
1499pub fn terminal_name() -> Option<String> {
1500    std::env::var("TERM_PROGRAM").ok()
1501}