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 }