1use crate::core::{ControlSpec, CursorSpec, StyleSpec};
2use once_cell::sync::Lazy;
3use std::convert::Into;
4use std::fmt;
5
6pub struct ThemePreset {
13 pub name: &'static str,
15
16 pub style: StyleSpec,
18
19 pub cursor: CursorSpec,
21
22 pub control: ControlSpec,
24}
25
26impl ThemePreset {
27 pub fn to_style(&self) -> StyleSpec {
29 self.style.clone()
30 }
31
32 pub fn to_cursor(&self) -> CursorSpec {
34 self.cursor.clone()
35 }
36
37 pub fn to_control(&self) -> ControlSpec {
39 self.control.clone()
40 }
41}
42
43#[derive(Debug)]
45pub enum ThemeError {
46 NotFound(String),
48}
49
50impl fmt::Display for ThemeError {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 ThemeError::NotFound(name) => write!(f, "Theme `{}` not found.", name),
54 }
55 }
56}
57
58impl std::error::Error for ThemeError {}
59
60pub fn resolve_theme(
66 name: Option<&str>,
67) -> Result<(StyleSpec, CursorSpec, ControlSpec), ThemeError> {
68 if let Some(name) = name {
69 match get_theme_by_name(name) {
70 Some(theme) => Ok((theme.to_style(), theme.to_cursor(), theme.to_control())),
71 None => Err(ThemeError::NotFound(name.to_string())),
72 }
73 } else {
74 Ok((
75 StyleSpec::default(),
76 CursorSpec::default(),
77 ControlSpec::default(),
78 ))
79 }
80}
81
82pub static RETRO_TTY: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
89 name: "Retro TTY",
90 style: StyleSpec {
91 font_size: 18,
92 font_family: "PxPlus IBM VGA8".into(),
93 text_color: Some("#00FF00".into()),
94 background_color: Some("#000000".into()),
95 },
96 cursor: CursorSpec {
97 char: "|".into(),
98 offset_x: 20,
99 color: Some("#00FF00".into()),
100 blink: true,
101 blink_ms: 800,
102 opacity: 1.0,
103 },
104 control: ControlSpec {
105 frame_delay: Some(60),
106 fade_duration: Some(100),
107 ..Default::default()
108 },
109});
110
111pub static DOS_CLASSIC: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
115 name: "DOS Classic",
116 style: StyleSpec {
117 font_size: 18,
118 font_family: "Courier New".into(),
119 text_color: Some("#C0C0C0".into()),
120 background_color: Some("#000000".into()),
121 },
122 cursor: CursorSpec {
123 char: "_".into(),
124 offset_x: 20,
125 color: None,
126 blink: true,
127 blink_ms: 700,
128 opacity: 1.0,
129 },
130 control: ControlSpec {
131 frame_delay: Some(70),
132 fade_duration: Some(100),
133 ..Default::default()
134 },
135});
136
137pub static DOS_BLUE: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
141 name: "DOS Blue",
142 style: StyleSpec {
143 font_size: 18,
144 font_family: "PxPlus IBM VGA8".into(),
145 text_color: Some("#FFFFFF".into()),
146 background_color: Some("#0000AA".into()), },
148 cursor: CursorSpec {
149 char: "β".into(),
150 offset_x: 20,
151 color: Some("#FFFFFF".into()),
152 blink: true,
153 blink_ms: 650,
154 opacity: 0.9,
155 },
156 control: ControlSpec {
157 frame_delay: Some(80),
158 fade_duration: Some(100),
159 ..Default::default()
160 },
161});
162
163pub static SOLARIZED_DARK: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
167 name: "Solarized Dark",
168 style: StyleSpec {
169 font_size: 18,
170 font_family: "Fira Code".into(),
171 text_color: Some("#839496".into()),
172 background_color: Some("#002b36".into()),
173 },
174 cursor: CursorSpec {
175 char: "β".into(),
176 offset_x: 20,
177 color: Some("#93a1a1".into()),
178 blink: true,
179 blink_ms: 850,
180 opacity: 0.9,
181 },
182 control: ControlSpec {
183 frame_delay: Some(60),
184 fade_duration: Some(100),
185 ..Default::default()
186 },
187});
188
189pub static GRUVBOX_DARK: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
193 name: "Gruvbox Dark",
194 style: StyleSpec {
195 font_size: 18,
196 font_family: "Source Code Pro".into(),
197 text_color: Some("#ebdbb2".into()),
198 background_color: Some("#282828".into()),
199 },
200 cursor: CursorSpec {
201 char: "β".into(),
202 offset_x: 20,
203 color: Some("#fe8019".into()),
204 blink: true,
205 blink_ms: 750,
206 opacity: 1.0,
207 },
208 control: ControlSpec {
209 frame_delay: Some(60),
210 fade_duration: Some(100),
211 ..Default::default()
212 },
213});
214
215pub static LIGHT_SHELL: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
219 name: "Light Shell",
220 style: StyleSpec {
221 font_size: 18,
222 font_family: "Monospace".into(),
223 text_color: Some("#444444".into()),
224 background_color: Some("#ffffff".into()),
225 },
226 cursor: CursorSpec {
227 char: "|".into(),
228 offset_x: 20,
229 color: Some("#888888".into()),
230 blink: true,
231 blink_ms: 1000,
232 opacity: 0.7,
233 },
234 control: ControlSpec {
235 frame_delay: Some(60),
236 fade_duration: Some(100),
237 ..Default::default()
238 },
239});
240
241pub static SOLARIZED_LIGHT: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
245 name: "Solarized Light",
246 style: StyleSpec {
247 font_size: 18,
248 font_family: "Monospace".into(),
249 text_color: Some("#586e75".into()),
250 background_color: Some("#fdf6e3".into()),
251 },
252 cursor: CursorSpec {
253 char: "|".into(),
254 offset_x: 20,
255 color: Some("#657b83".into()),
256 blink: true,
257 blink_ms: 850,
258 opacity: 0.9,
259 },
260 control: ControlSpec {
261 frame_delay: Some(60),
262 fade_duration: Some(100),
263 ..Default::default()
264 },
265});
266
267pub static NEON_NIGHT: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
271 name: "Neon Night",
272 style: StyleSpec {
273 font_size: 18,
274 font_family: "Cascadia Mono".into(),
275 text_color: Some("#FF00FF".into()),
276 background_color: Some("#0f0f1a".into()),
277 },
278 cursor: CursorSpec {
279 char: "οΉ".into(),
280 offset_x: 20,
281 color: Some("#FF00FF".into()),
282 blink: true,
283 blink_ms: 650,
284 opacity: 0.9,
285 },
286 control: ControlSpec {
287 frame_delay: Some(75),
288 fade_duration: Some(100),
289 ..Default::default()
290 },
291});
292
293pub static POKE_TERMINAL: Lazy<ThemePreset> = Lazy::new(|| ThemePreset {
297 name: "Poke Terminal",
298 style: StyleSpec {
299 font_size: 18,
300 font_family: "Monospace".into(),
301 text_color: Some("#FFD700".into()),
302 background_color: Some("#2b2b2b".into()),
303 },
304 cursor: CursorSpec {
305 char: "β‘".into(), offset_x: 30,
307 color: Some("#FFD700".into()),
308 blink: true,
309 blink_ms: 800,
310 opacity: 1.0,
311 },
312 control: ControlSpec {
313 frame_delay: Some(50),
314 fade_duration: Some(100),
315 ..Default::default()
316 },
317});
318
319pub fn all_presets() -> Vec<&'static ThemePreset> {
323 vec![
324 &RETRO_TTY,
325 &DOS_CLASSIC,
326 &DOS_BLUE,
327 &SOLARIZED_DARK,
328 &GRUVBOX_DARK,
329 &LIGHT_SHELL,
330 &SOLARIZED_LIGHT,
331 &NEON_NIGHT,
332 &POKE_TERMINAL,
333 ]
334}
335
336pub fn get_theme_by_name(name: &str) -> Option<&'static ThemePreset> {
341 let normalized = name.to_lowercase().replace('_', " ");
342 all_presets()
343 .into_iter()
344 .find(|preset| preset.name.to_lowercase() == normalized)
345}