1pub mod palette;
3
4pub use palette::Palette;
5
6use crate::Color;
7
8use std::borrow::Cow;
9use std::fmt;
10use std::sync::Arc;
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum Theme {
15 Light,
17 Dark,
19 Dracula,
21 Nord,
23 SolarizedLight,
25 SolarizedDark,
27 GruvboxLight,
29 GruvboxDark,
31 CatppuccinLatte,
33 CatppuccinFrappe,
35 CatppuccinMacchiato,
37 CatppuccinMocha,
39 TokyoNight,
41 TokyoNightStorm,
43 TokyoNightLight,
45 KanagawaWave,
47 KanagawaDragon,
49 KanagawaLotus,
51 Moonfly,
53 Nightfly,
55 Oxocarbon,
57 Ferra,
59 Custom(Arc<Custom>),
61}
62
63impl Theme {
64 pub const ALL: &'static [Self] = &[
66 Self::Light,
67 Self::Dark,
68 Self::Dracula,
69 Self::Nord,
70 Self::SolarizedLight,
71 Self::SolarizedDark,
72 Self::GruvboxLight,
73 Self::GruvboxDark,
74 Self::CatppuccinLatte,
75 Self::CatppuccinFrappe,
76 Self::CatppuccinMacchiato,
77 Self::CatppuccinMocha,
78 Self::TokyoNight,
79 Self::TokyoNightStorm,
80 Self::TokyoNightLight,
81 Self::KanagawaWave,
82 Self::KanagawaDragon,
83 Self::KanagawaLotus,
84 Self::Moonfly,
85 Self::Nightfly,
86 Self::Oxocarbon,
87 Self::Ferra,
88 ];
89
90 pub fn custom(name: impl Into<Cow<'static, str>>, seed: palette::Seed) -> Self {
92 Self::custom_with_fn(name, seed, Palette::generate)
93 }
94
95 pub fn custom_with_fn(
98 name: impl Into<Cow<'static, str>>,
99 palette: palette::Seed,
100 generate: impl FnOnce(palette::Seed) -> Palette,
101 ) -> Self {
102 Self::Custom(Arc::new(Custom::with_fn(name, palette, generate)))
103 }
104
105 pub fn palette(&self) -> &palette::Palette {
107 match self {
108 Self::Light => &palette::LIGHT,
109 Self::Dark => &palette::DARK,
110 Self::Dracula => &palette::DRACULA,
111 Self::Nord => &palette::NORD,
112 Self::SolarizedLight => &palette::SOLARIZED_LIGHT,
113 Self::SolarizedDark => &palette::SOLARIZED_DARK,
114 Self::GruvboxLight => &palette::GRUVBOX_LIGHT,
115 Self::GruvboxDark => &palette::GRUVBOX_DARK,
116 Self::CatppuccinLatte => &palette::CATPPUCCIN_LATTE,
117 Self::CatppuccinFrappe => &palette::CATPPUCCIN_FRAPPE,
118 Self::CatppuccinMacchiato => &palette::CATPPUCCIN_MACCHIATO,
119 Self::CatppuccinMocha => &palette::CATPPUCCIN_MOCHA,
120 Self::TokyoNight => &palette::TOKYO_NIGHT,
121 Self::TokyoNightStorm => &palette::TOKYO_NIGHT_STORM,
122 Self::TokyoNightLight => &palette::TOKYO_NIGHT_LIGHT,
123 Self::KanagawaWave => &palette::KANAGAWA_WAVE,
124 Self::KanagawaDragon => &palette::KANAGAWA_DRAGON,
125 Self::KanagawaLotus => &palette::KANAGAWA_LOTUS,
126 Self::Moonfly => &palette::MOONFLY,
127 Self::Nightfly => &palette::NIGHTFLY,
128 Self::Oxocarbon => &palette::OXOCARBON,
129 Self::Ferra => &palette::FERRA,
130 Self::Custom(custom) => &custom.palette,
131 }
132 }
133
134 pub fn seed(&self) -> palette::Seed {
136 match self {
137 Self::Light => palette::Seed::LIGHT,
138 Self::Dark => palette::Seed::DARK,
139 Self::Dracula => palette::Seed::DRACULA,
140 Self::Nord => palette::Seed::NORD,
141 Self::SolarizedLight => palette::Seed::SOLARIZED_LIGHT,
142 Self::SolarizedDark => palette::Seed::SOLARIZED_DARK,
143 Self::GruvboxLight => palette::Seed::GRUVBOX_LIGHT,
144 Self::GruvboxDark => palette::Seed::GRUVBOX_DARK,
145 Self::CatppuccinLatte => palette::Seed::CATPPUCCIN_LATTE,
146 Self::CatppuccinFrappe => palette::Seed::CATPPUCCIN_FRAPPE,
147 Self::CatppuccinMacchiato => palette::Seed::CATPPUCCIN_MACCHIATO,
148 Self::CatppuccinMocha => palette::Seed::CATPPUCCIN_MOCHA,
149 Self::TokyoNight => palette::Seed::TOKYO_NIGHT,
150 Self::TokyoNightStorm => palette::Seed::TOKYO_NIGHT_STORM,
151 Self::TokyoNightLight => palette::Seed::TOKYO_NIGHT_LIGHT,
152 Self::KanagawaWave => palette::Seed::KANAGAWA_WAVE,
153 Self::KanagawaDragon => palette::Seed::KANAGAWA_DRAGON,
154 Self::KanagawaLotus => palette::Seed::KANAGAWA_LOTUS,
155 Self::Moonfly => palette::Seed::MOONFLY,
156 Self::Nightfly => palette::Seed::NIGHTFLY,
157 Self::Oxocarbon => palette::Seed::OXOCARBON,
158 Self::Ferra => palette::Seed::FERRA,
159 Self::Custom(custom) => custom.seed,
160 }
161 }
162}
163
164impl fmt::Display for Theme {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str(self.name())
167 }
168}
169
170#[derive(Debug, Clone, PartialEq)]
172pub struct Custom {
173 name: Cow<'static, str>,
174 seed: palette::Seed,
175 palette: Palette,
176}
177
178impl Custom {
179 pub fn new(name: String, seed: palette::Seed) -> Self {
181 Self::with_fn(name, seed, Palette::generate)
182 }
183
184 pub fn with_fn(
187 name: impl Into<Cow<'static, str>>,
188 seed: palette::Seed,
189 generate: impl FnOnce(palette::Seed) -> Palette,
190 ) -> Self {
191 Self {
192 name: name.into(),
193 seed,
194 palette: generate(seed),
195 }
196 }
197}
198
199impl fmt::Display for Custom {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 write!(f, "{}", self.name)
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
207pub enum Mode {
208 #[default]
210 None,
211 Light,
213 Dark,
215}
216
217#[derive(Debug, Clone, Copy, PartialEq)]
219pub struct Style {
220 pub background_color: Color,
222
223 pub text_color: Color,
225}
226
227pub trait Base {
229 fn default(preference: Mode) -> Self;
231
232 fn mode(&self) -> Mode;
234
235 fn base(&self) -> Style;
237
238 fn seed(&self) -> Option<palette::Seed>;
243
244 fn name(&self) -> &str;
249}
250
251impl Base for Theme {
252 fn default(preference: Mode) -> Self {
253 use std::env;
254 use std::sync::OnceLock;
255
256 static SYSTEM: OnceLock<Option<Theme>> = OnceLock::new();
257
258 let system = SYSTEM.get_or_init(|| {
259 let name = env::var("ICED_THEME").ok()?;
260
261 Theme::ALL
262 .iter()
263 .find(|theme| theme.to_string() == name)
264 .cloned()
265 });
266
267 if let Some(system) = system {
268 return system.clone();
269 }
270
271 match preference {
272 Mode::None | Mode::Light => Self::Light,
273 Mode::Dark => Self::Dark,
274 }
275 }
276
277 fn mode(&self) -> Mode {
278 if self.palette().is_dark {
279 Mode::Dark
280 } else {
281 Mode::Light
282 }
283 }
284
285 fn base(&self) -> Style {
286 default(self)
287 }
288
289 fn seed(&self) -> Option<palette::Seed> {
290 Some(self.seed())
291 }
292
293 fn name(&self) -> &str {
294 match self {
295 Self::Light => "Light",
296 Self::Dark => "Dark",
297 Self::Dracula => "Dracula",
298 Self::Nord => "Nord",
299 Self::SolarizedLight => "Solarized Light",
300 Self::SolarizedDark => "Solarized Dark",
301 Self::GruvboxLight => "Gruvbox Light",
302 Self::GruvboxDark => "Gruvbox Dark",
303 Self::CatppuccinLatte => "Catppuccin Latte",
304 Self::CatppuccinFrappe => "Catppuccin Frappé",
305 Self::CatppuccinMacchiato => "Catppuccin Macchiato",
306 Self::CatppuccinMocha => "Catppuccin Mocha",
307 Self::TokyoNight => "Tokyo Night",
308 Self::TokyoNightStorm => "Tokyo Night Storm",
309 Self::TokyoNightLight => "Tokyo Night Light",
310 Self::KanagawaWave => "Kanagawa Wave",
311 Self::KanagawaDragon => "Kanagawa Dragon",
312 Self::KanagawaLotus => "Kanagawa Lotus",
313 Self::Moonfly => "Moonfly",
314 Self::Nightfly => "Nightfly",
315 Self::Oxocarbon => "Oxocarbon",
316 Self::Ferra => "Ferra",
317 Self::Custom(custom) => &custom.name,
318 }
319 }
320}
321
322pub fn default(theme: &Theme) -> Style {
324 let palette = theme.palette();
325
326 Style {
327 background_color: palette.background.base.color,
328 text_color: palette.background.base.text,
329 }
330}