1use ratatui::style::{Color, Modifier, Style};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum ThemeVariant {
13 EverforestDark,
15 EverforestLight,
17}
18
19impl Default for ThemeVariant {
20 fn default() -> Self {
21 Self::EverforestDark
22 }
23}
24
25#[derive(Debug, Clone)]
27pub struct ColorPalette {
28 pub background: Color,
29 pub foreground: Color,
30 pub accent: Color,
31 pub secondary: Color,
32 pub info: Color,
33 pub border: Color,
34 pub selection: Color,
35 pub cursor: Color,
36 pub warning: Color, }
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum Element {
42 Text,
44 Title,
46 Border,
48 Highlight,
50 Accent,
52 Secondary,
54 Info,
56 Background,
58 Active,
60 Inactive,
62 Warning,
64}
65
66#[derive(Debug, Clone)]
68pub struct Theme {
69 variant: ThemeVariant,
70 colors: ColorPalette,
71}
72
73impl Default for Theme {
74 fn default() -> Self {
75 Self::new(ThemeVariant::default())
76 }
77}
78
79impl Theme {
80 pub fn new(variant: ThemeVariant) -> Self {
82 let colors = match variant {
83 ThemeVariant::EverforestDark => ColorPalette {
84 background: Color::Rgb(45, 53, 59), foreground: Color::Rgb(211, 198, 170), accent: Color::Rgb(167, 192, 128), secondary: Color::Rgb(230, 126, 128), info: Color::Rgb(127, 187, 179), border: Color::Rgb(130, 140, 150), selection: Color::Rgb(64, 72, 78), cursor: Color::Rgb(211, 198, 170), warning: Color::Rgb(219, 188, 127), },
94 ThemeVariant::EverforestLight => ColorPalette {
95 background: Color::Rgb(253, 246, 227), foreground: Color::Rgb(76, 86, 94), accent: Color::Rgb(141, 161, 1), secondary: Color::Rgb(248, 85, 82), info: Color::Rgb(53, 167, 124), border: Color::Rgb(150, 160, 170), selection: Color::Rgb(243, 236, 217), cursor: Color::Rgb(76, 86, 94), warning: Color::Rgb(207, 131, 44), },
105 };
106
107 Self { variant, colors }
108 }
109
110 pub fn variant(&self) -> ThemeVariant {
112 self.variant
113 }
114
115 pub fn colors(&self) -> &ColorPalette {
117 &self.colors
118 }
119
120 pub fn toggle(&mut self) {
122 self.variant = match self.variant {
123 ThemeVariant::EverforestDark => ThemeVariant::EverforestLight,
124 ThemeVariant::EverforestLight => ThemeVariant::EverforestDark,
125 };
126 *self = Self::new(self.variant);
127 }
128
129 pub fn set_variant(&mut self, variant: ThemeVariant) {
131 if self.variant != variant {
132 self.variant = variant;
133 *self = Self::new(self.variant);
134 }
135 }
136
137 pub fn ratatui_style(&self, element: Element) -> Style {
139 match element {
140 Element::Text => Style::default()
141 .fg(self.colors.foreground)
142 .bg(self.colors.background),
143
144 Element::Title => Style::default()
145 .fg(self.colors.accent)
146 .bg(self.colors.background)
147 .add_modifier(Modifier::BOLD),
148
149 Element::Border => Style::default()
150 .fg(self.colors.border)
151 .bg(self.colors.background),
152
153 Element::Highlight => Style::default()
154 .fg(self.colors.foreground)
155 .bg(self.colors.selection)
156 .add_modifier(Modifier::BOLD),
157
158 Element::Accent => Style::default()
159 .fg(self.colors.accent)
160 .bg(self.colors.background)
161 .add_modifier(Modifier::BOLD),
162
163 Element::Secondary => Style::default()
164 .fg(self.colors.secondary)
165 .bg(self.colors.background),
166
167 Element::Info => Style::default()
168 .fg(self.colors.info)
169 .bg(self.colors.background),
170
171 Element::Background => Style::default()
172 .fg(self.colors.foreground)
173 .bg(self.colors.background),
174
175 Element::Active => Style::default()
176 .fg(self.colors.accent)
177 .bg(self.colors.selection)
178 .add_modifier(Modifier::BOLD),
179
180 Element::Inactive => Style::default()
181 .fg(self.colors.border)
182 .bg(self.colors.background),
183
184 Element::Warning => Style::default()
185 .fg(self.colors.warning)
186 .bg(self.colors.background),
187 }
188 }
189
190 pub fn fg_color(&self, element: Element) -> Color {
192 match element {
193 Element::Text | Element::Background => self.colors.foreground,
194 Element::Title | Element::Accent | Element::Active => self.colors.accent,
195 Element::Border | Element::Inactive => self.colors.border,
196 Element::Highlight => self.colors.foreground,
197 Element::Secondary => self.colors.secondary,
198 Element::Info => self.colors.info,
199 Element::Warning => self.colors.warning,
200 }
201 }
202
203 pub fn bg_color(&self, element: Element) -> Color {
205 match element {
206 Element::Highlight | Element::Active => self.colors.selection,
207 _ => self.colors.background,
208 }
209 }
210
211 pub fn title_style(&self) -> Style {
213 self.ratatui_style(Element::Title)
214 }
215
216 pub fn border_style(&self) -> Style {
218 self.ratatui_style(Element::Border)
219 }
220
221 pub fn text_style(&self) -> Style {
223 self.ratatui_style(Element::Text)
224 }
225
226 pub fn highlight_style(&self) -> Style {
228 self.ratatui_style(Element::Highlight)
229 }
230
231 pub fn accent_style(&self) -> Style {
233 self.ratatui_style(Element::Accent)
234 }
235
236 pub fn secondary_style(&self) -> Style {
238 self.ratatui_style(Element::Secondary)
239 }
240
241 pub fn info_style(&self) -> Style {
243 self.ratatui_style(Element::Info)
244 }
245
246 pub fn warning_style(&self) -> Style {
248 self.ratatui_style(Element::Warning)
249 }
250}