Skip to main content

stynx_code_tui/
theme.rs

1use std::sync::RwLock;
2use std::sync::LazyLock;
3
4use ratatui::style::Color;
5
6#[derive(Clone, Copy, Debug)]
7pub struct Theme {
8    pub id: &'static str,
9    pub name: &'static str,
10    pub base: Color,
11    pub surface: Color,
12    pub overlay: Color,
13    pub muted: Color,
14    pub subtle: Color,
15    pub text: Color,
16    pub love: Color,
17    pub gold: Color,
18    pub rose: Color,
19    pub pine: Color,
20    pub foam: Color,
21    pub iris: Color,
22    pub hl_med: Color,
23    pub hl_high: Color,
24
25    pub background: Color,
26    pub background_panel: Color,
27    pub background_element: Color,
28    pub background_menu: Color,
29    pub text_muted: Color,
30    pub border: Color,
31    pub border_active: Color,
32    pub primary: Color,
33    pub accent: Color,
34    pub success: Color,
35    pub warning: Color,
36    pub error: Color,
37}
38
39pub const fn rose_pine() -> Theme {
40    let base = Color::Rgb(25, 23, 36);
41    let surface = Color::Rgb(31, 29, 46);
42    let overlay = Color::Rgb(38, 35, 58);
43    let muted = Color::Rgb(110, 106, 134);
44    let subtle = Color::Rgb(144, 140, 170);
45    let text = Color::Rgb(224, 222, 244);
46    let love = Color::Rgb(235, 111, 146);
47    let gold = Color::Rgb(246, 193, 119);
48    let rose = Color::Rgb(235, 188, 186);
49    let pine = Color::Rgb(49, 116, 143);
50    let foam = Color::Rgb(156, 207, 216);
51    let iris = Color::Rgb(196, 167, 231);
52    let hl_med = Color::Rgb(64, 61, 82);
53    let hl_high = Color::Rgb(82, 79, 103);
54    Theme {
55        id: "rose-pine", name: "Rosé Pine",
56        base, surface, overlay, muted, subtle, text,
57        love, gold, rose, pine, foam, iris, hl_med, hl_high,
58        background: base, background_panel: surface, background_element: overlay,
59        background_menu: hl_med, text_muted: muted, border: hl_med, border_active: hl_high,
60        primary: iris, accent: foam, success: pine, warning: gold, error: love,
61    }
62}
63
64pub const fn catppuccin_mocha() -> Theme {
65    let base = Color::Rgb(30, 30, 46);
66    let surface = Color::Rgb(49, 50, 68);
67    let overlay = Color::Rgb(69, 71, 90);
68    let muted = Color::Rgb(127, 132, 156);
69    let subtle = Color::Rgb(166, 173, 200);
70    let text = Color::Rgb(205, 214, 244);
71    let love = Color::Rgb(243, 139, 168);
72    let gold = Color::Rgb(249, 226, 175);
73    let rose = Color::Rgb(245, 194, 231);
74    let pine = Color::Rgb(166, 227, 161);
75    let foam = Color::Rgb(137, 220, 235);
76    let iris = Color::Rgb(203, 166, 247);
77    let hl_med = Color::Rgb(88, 91, 112);
78    let hl_high = Color::Rgb(108, 112, 134);
79    Theme {
80        id: "catppuccin-mocha", name: "Catppuccin Mocha",
81        base, surface, overlay, muted, subtle, text,
82        love, gold, rose, pine, foam, iris, hl_med, hl_high,
83        background: base, background_panel: surface, background_element: overlay,
84        background_menu: hl_med, text_muted: muted, border: hl_med, border_active: hl_high,
85        primary: iris, accent: foam, success: pine, warning: gold, error: love,
86    }
87}
88
89pub const fn tokyo_night() -> Theme {
90    let base = Color::Rgb(26, 27, 38);
91    let surface = Color::Rgb(36, 40, 59);
92    let overlay = Color::Rgb(52, 59, 88);
93    let muted = Color::Rgb(86, 95, 137);
94    let subtle = Color::Rgb(154, 165, 206);
95    let text = Color::Rgb(192, 202, 245);
96    let love = Color::Rgb(247, 118, 142);
97    let gold = Color::Rgb(224, 175, 104);
98    let rose = Color::Rgb(255, 158, 100);
99    let pine = Color::Rgb(158, 206, 106);
100    let foam = Color::Rgb(125, 207, 255);
101    let iris = Color::Rgb(187, 154, 247);
102    let hl_med = Color::Rgb(65, 72, 104);
103    let hl_high = Color::Rgb(86, 95, 137);
104    Theme {
105        id: "tokyo-night", name: "Tokyo Night",
106        base, surface, overlay, muted, subtle, text,
107        love, gold, rose, pine, foam, iris, hl_med, hl_high,
108        background: base, background_panel: surface, background_element: overlay,
109        background_menu: hl_med, text_muted: muted, border: hl_med, border_active: hl_high,
110        primary: iris, accent: foam, success: pine, warning: gold, error: love,
111    }
112}
113
114pub const fn gruvbox_dark() -> Theme {
115    let base = Color::Rgb(40, 40, 40);
116    let surface = Color::Rgb(50, 48, 47);
117    let overlay = Color::Rgb(60, 56, 54);
118    let muted = Color::Rgb(124, 111, 100);
119    let subtle = Color::Rgb(168, 153, 132);
120    let text = Color::Rgb(235, 219, 178);
121    let love = Color::Rgb(251, 73, 52);
122    let gold = Color::Rgb(250, 189, 47);
123    let rose = Color::Rgb(214, 93, 14);
124    let pine = Color::Rgb(184, 187, 38);
125    let foam = Color::Rgb(131, 165, 152);
126    let iris = Color::Rgb(211, 134, 155);
127    let hl_med = Color::Rgb(80, 73, 69);
128    let hl_high = Color::Rgb(102, 92, 84);
129    Theme {
130        id: "gruvbox-dark", name: "Gruvbox Dark",
131        base, surface, overlay, muted, subtle, text,
132        love, gold, rose, pine, foam, iris, hl_med, hl_high,
133        background: base, background_panel: surface, background_element: overlay,
134        background_menu: hl_med, text_muted: muted, border: hl_med, border_active: hl_high,
135        primary: iris, accent: foam, success: pine, warning: gold, error: love,
136    }
137}
138
139pub const THEMES: &[Theme] = &[
140    rose_pine(),
141    catppuccin_mocha(),
142    tokyo_night(),
143    gruvbox_dark(),
144];
145
146static CURRENT: LazyLock<RwLock<Theme>> = LazyLock::new(|| RwLock::new(rose_pine()));
147
148pub fn current() -> Theme { *CURRENT.read().unwrap() }
149
150pub fn set_theme(id: &str) -> bool {
151    for t in THEMES {
152        if t.id == id {
153            *CURRENT.write().unwrap() = *t;
154            return true;
155        }
156    }
157    false
158}
159
160#[allow(non_snake_case)] pub fn BASE()    -> Color { current().base }
161#[allow(non_snake_case)] pub fn SURFACE() -> Color { current().surface }
162#[allow(non_snake_case)] pub fn OVERLAY() -> Color { current().overlay }
163#[allow(non_snake_case)] pub fn MUTED()   -> Color { current().muted }
164#[allow(non_snake_case)] pub fn SUBTLE()  -> Color { current().subtle }
165#[allow(non_snake_case)] pub fn TEXT()    -> Color { current().text }
166#[allow(non_snake_case)] pub fn LOVE()    -> Color { current().love }
167#[allow(non_snake_case)] pub fn GOLD()    -> Color { current().gold }
168#[allow(non_snake_case)] pub fn ROSE()    -> Color { current().rose }
169#[allow(non_snake_case)] pub fn PINE()    -> Color { current().pine }
170#[allow(non_snake_case)] pub fn FOAM()    -> Color { current().foam }
171#[allow(non_snake_case)] pub fn IRIS()    -> Color { current().iris }
172#[allow(non_snake_case)] pub fn HL_MED()  -> Color { current().hl_med }
173#[allow(non_snake_case)] pub fn HL_HIGH() -> Color { current().hl_high }
174
175#[allow(non_snake_case)] pub fn BACKGROUND()         -> Color { current().background }
176#[allow(non_snake_case)] pub fn BACKGROUND_PANEL()   -> Color { current().background_panel }
177#[allow(non_snake_case)] pub fn BACKGROUND_ELEMENT() -> Color { current().background_element }
178#[allow(non_snake_case)] pub fn BACKGROUND_MENU()    -> Color { current().background_menu }
179#[allow(non_snake_case)] pub fn TEXT_MUTED()         -> Color { current().text_muted }
180#[allow(non_snake_case)] pub fn BORDER()             -> Color { current().border }
181#[allow(non_snake_case)] pub fn BORDER_ACTIVE()      -> Color { current().border_active }
182#[allow(non_snake_case)] pub fn PRIMARY()            -> Color { current().primary }
183#[allow(non_snake_case)] pub fn ACCENT()             -> Color { current().accent }
184#[allow(non_snake_case)] pub fn SUCCESS()            -> Color { current().success }
185#[allow(non_snake_case)] pub fn WARNING()            -> Color { current().warning }
186#[allow(non_snake_case)] pub fn ERROR()              -> Color { current().error }