Skip to main content

scrin/theme/
mod.rs

1use crate::core::color::Color;
2use crate::style::Style;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub struct ThemeTokens {
6    pub panel: Color,
7    pub text: Color,
8    pub dim: Color,
9    pub accent: Color,
10    pub success: Color,
11    pub warning: Color,
12    pub error: Color,
13}
14
15impl ThemeTokens {
16    pub const SCRIN: Self = Self {
17        panel: Color::rgb(22, 27, 34),
18        text: Color::rgb(201, 209, 217),
19        dim: Color::rgb(110, 118, 129),
20        accent: Color::rgb(88, 166, 255),
21        success: Color::rgb(63, 185, 80),
22        warning: Color::rgb(210, 153, 34),
23        error: Color::rgb(248, 81, 73),
24    };
25
26    pub const fn new(
27        panel: Color,
28        text: Color,
29        dim: Color,
30        accent: Color,
31        success: Color,
32        warning: Color,
33        error: Color,
34    ) -> Self {
35        Self {
36            panel,
37            text,
38            dim,
39            accent,
40            success,
41            warning,
42            error,
43        }
44    }
45
46    pub const fn panel_style(self) -> Style {
47        Style::new().fg(self.text).bg(self.panel)
48    }
49
50    pub const fn text_style(self) -> Style {
51        Style::new().fg(self.text)
52    }
53
54    pub const fn dim_style(self) -> Style {
55        Style::new().fg(self.dim)
56    }
57
58    pub const fn accent_style(self) -> Style {
59        Style::new().fg(self.accent)
60    }
61
62    pub const fn success_style(self) -> Style {
63        Style::new().fg(self.success)
64    }
65
66    pub const fn warning_style(self) -> Style {
67        Style::new().fg(self.warning)
68    }
69
70    pub const fn error_style(self) -> Style {
71        Style::new().fg(self.error)
72    }
73}
74
75impl Default for ThemeTokens {
76    fn default() -> Self {
77        Self::SCRIN
78    }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq)]
82pub struct Theme {
83    pub bg: Color,
84    pub fg: Color,
85    pub accent: Color,
86    pub accent_bright: Color,
87    pub muted: Color,
88    pub surface: Color,
89    pub surface_bright: Color,
90    pub error: Color,
91    pub warning: Color,
92    pub success: Color,
93    pub info: Color,
94    pub border: Color,
95    pub border_focus: Color,
96    pub text_primary: Color,
97    pub text_secondary: Color,
98    pub text_dim: Color,
99    pub highlight_bg: Color,
100    pub highlight_fg: Color,
101    pub glow: Color,
102}
103
104impl Theme {
105    pub const DARK: Theme = Theme {
106        bg: Color::rgb(13, 17, 23),
107        fg: Color::rgb(201, 209, 217),
108        accent: Color::rgb(88, 166, 255),
109        accent_bright: Color::rgb(121, 192, 255),
110        muted: Color::rgb(110, 118, 129),
111        surface: Color::rgb(22, 27, 34),
112        surface_bright: Color::rgb(33, 38, 45),
113        error: Color::rgb(248, 81, 73),
114        warning: Color::rgb(210, 153, 34),
115        success: Color::rgb(63, 185, 80),
116        info: Color::rgb(56, 139, 253),
117        border: Color::rgb(48, 54, 61),
118        border_focus: Color::rgb(88, 166, 255),
119        text_primary: Color::rgb(201, 209, 217),
120        text_secondary: Color::rgb(139, 148, 158),
121        text_dim: Color::rgb(110, 118, 129),
122        highlight_bg: Color::rgb(31, 111, 235),
123        highlight_fg: Color::rgb(255, 255, 255),
124        glow: Color::rgb(88, 166, 255),
125    };
126
127    pub const CYBERPUNK: Theme = Theme {
128        bg: Color::rgb(10, 4, 18),
129        fg: Color::rgb(200, 200, 255),
130        accent: Color::rgb(255, 0, 128),
131        accent_bright: Color::rgb(255, 80, 180),
132        muted: Color::rgb(100, 60, 140),
133        surface: Color::rgb(20, 10, 35),
134        surface_bright: Color::rgb(35, 18, 55),
135        error: Color::rgb(255, 30, 30),
136        warning: Color::rgb(255, 200, 0),
137        success: Color::rgb(0, 255, 128),
138        info: Color::rgb(0, 200, 255),
139        border: Color::rgb(60, 30, 90),
140        border_focus: Color::rgb(255, 0, 128),
141        text_primary: Color::rgb(220, 220, 255),
142        text_secondary: Color::rgb(150, 130, 180),
143        text_dim: Color::rgb(90, 70, 120),
144        highlight_bg: Color::rgb(255, 0, 128),
145        highlight_fg: Color::rgb(255, 255, 255),
146        glow: Color::rgb(255, 0, 128),
147    };
148
149    pub const MONOKAI: Theme = Theme {
150        bg: Color::rgb(39, 40, 34),
151        fg: Color::rgb(248, 248, 242),
152        accent: Color::rgb(166, 226, 46),
153        accent_bright: Color::rgb(171, 227, 56),
154        muted: Color::rgb(117, 113, 94),
155        surface: Color::rgb(49, 50, 44),
156        surface_bright: Color::rgb(69, 71, 66),
157        error: Color::rgb(249, 38, 114),
158        warning: Color::rgb(230, 219, 100),
159        success: Color::rgb(166, 226, 46),
160        info: Color::rgb(102, 217, 239),
161        border: Color::rgb(79, 82, 76),
162        border_focus: Color::rgb(166, 226, 46),
163        text_primary: Color::rgb(248, 248, 242),
164        text_secondary: Color::rgb(171, 174, 167),
165        text_dim: Color::rgb(117, 113, 94),
166        highlight_bg: Color::rgb(73, 72, 62),
167        highlight_fg: Color::rgb(255, 255, 255),
168        glow: Color::rgb(166, 226, 46),
169    };
170
171    pub const SOLARIZED: Theme = Theme {
172        bg: Color::rgb(0, 43, 54),
173        fg: Color::rgb(131, 148, 150),
174        accent: Color::rgb(38, 139, 210),
175        accent_bright: Color::rgb(42, 161, 152),
176        muted: Color::rgb(88, 110, 117),
177        surface: Color::rgb(7, 54, 66),
178        surface_bright: Color::rgb(0, 63, 77),
179        error: Color::rgb(220, 50, 47),
180        warning: Color::rgb(181, 137, 0),
181        success: Color::rgb(133, 153, 0),
182        info: Color::rgb(42, 161, 152),
183        border: Color::rgb(88, 110, 117),
184        border_focus: Color::rgb(38, 139, 210),
185        text_primary: Color::rgb(147, 161, 161),
186        text_secondary: Color::rgb(108, 113, 118),
187        text_dim: Color::rgb(88, 110, 117),
188        highlight_bg: Color::rgb(0, 63, 77),
189        highlight_fg: Color::rgb(253, 246, 227),
190        glow: Color::rgb(42, 161, 152),
191    };
192
193    pub fn accent_for(&self, index: usize) -> Color {
194        let colors = [
195            self.accent,
196            self.success,
197            self.warning,
198            self.error,
199            self.info,
200            Color::rgb(188, 140, 255),
201            Color::rgb(255, 160, 180),
202            Color::rgb(100, 220, 255),
203        ];
204        colors[index % colors.len()]
205    }
206
207    pub const fn tokens(&self) -> ThemeTokens {
208        ThemeTokens {
209            panel: self.surface,
210            text: self.text_primary,
211            dim: self.text_dim,
212            accent: self.accent,
213            success: self.success,
214            warning: self.warning,
215            error: self.error,
216        }
217    }
218}
219
220impl From<Theme> for ThemeTokens {
221    fn from(value: Theme) -> Self {
222        value.tokens()
223    }
224}
225
226impl From<&Theme> for ThemeTokens {
227    fn from(value: &Theme) -> Self {
228        value.tokens()
229    }
230}
231
232impl Default for Theme {
233    fn default() -> Self {
234        Self::DARK
235    }
236}