1#[derive(Clone)]
2pub struct Theme {
3 pub colors: [String; 4],
4}
5
6#[derive(Copy, Clone, PartialEq)]
7pub enum ColorMode {
8 Theme,
9 Rainbow,
10}
11
12impl Theme {
13
14 pub const CHARS: [char; 10] = [' ', '.', ':', '^', '*', 'x', 's', 'S', '#', '$'];
15
16 pub fn ash() -> Self {
17 Theme { colors: [
18 "38;2;30;30;30".into(),
19 "38;2;80;80;80".into(),
20 "38;2;160;160;160".into(),
21 "38;2;240;240;240".into(),
22 ]}
23 }
24
25 pub fn aurora() -> Self {
26 Theme { colors: [
27 "38;2;0;20;40".into(),
28 "38;2;0;128;100".into(),
29 "38;2;0;255;180".into(),
30 "38;2;180;255;240".into(),
31 ]}
32 }
33
34 pub fn blue() -> Self {
35 Theme { colors: [
36 "38;2;10;15;28".into(),
37 "38;2;0;95;135".into(),
38 "38;2;0;175;175".into(),
39 "38;2;51;225;255".into(),
40 ]}
41 }
42
43 pub fn classic() -> Self {
44 Theme { colors: [
45 "38;2;20;20;20".into(),
46 "38;2;220;50;47".into(),
47 "38;2;255;200;40".into(),
48 "38;2;70;130;180".into(),
49 ]}
50 }
51
52 pub fn copper() -> Self {
53 Theme { colors: [
54 "38;2;0;40;40".into(),
55 "38;2;0;128;128".into(),
56 "38;2;64;224;208".into(),
57 "38;2;224;255;255".into(),
58 ]}
59 }
60
61 pub fn crimson() -> Self {
62 Theme { colors: [
63 "38;2;60;0;0".into(),
64 "38;2;178;34;34".into(),
65 "38;2;220;20;60".into(),
66 "38;2;255;100;100".into(),
67 ]}
68 }
69
70 pub fn dusk() -> Self {
71 Theme { colors: [
72 "38;2;60;10;40".into(),
73 "38;2;180;50;80".into(),
74 "38;2;255;120;50".into(),
75 "38;2;255;200;120".into(),
76 ]}
77 }
78
79 pub fn ember() -> Self {
80 Theme { colors: [
81 "38;2;60;20;0".into(),
82 "38;2;139;0;0".into(),
83 "38;2;204;85;0".into(),
84 "38;2;255;140;0".into(),
85 ]}
86 }
87
88 pub fn emerald() -> Self {
89 Theme { colors: [
90 "38;2;0;30;10".into(),
91 "38;2;0;128;0".into(),
92 "38;2;0;255;127".into(),
93 "38;2;152;251;152".into(),
94 ]}
95 }
96
97 pub fn forest() -> Self {
98 Theme { colors: [
99 "38;2;0;40;0".into(),
100 "38;2;34;139;34".into(),
101 "38;2;50;205;50".into(),
102 "38;2;172;255;47".into(),
103 ]}
104 }
105
106 pub fn frost() -> Self {
107 Theme { colors: [
108 "38;2;20;40;80".into(),
109 "38;2;70;130;200".into(),
110 "38;2;180;210;255".into(),
111 "38;2;240;248;255".into(),
112 ]}
113 }
114
115 pub fn ghost() -> Self {
116 Theme { colors: [
117 "38;2;80;0;80".into(),
118 "38;2;150;50;250".into(),
119 "38;2;200;130;255".into(),
120 "38;2;240;220;255".into(),
121 ]}
122 }
123
124 pub fn gold() -> Self {
125 Theme { colors: [
126 "38;2;101;67;33".into(),
127 "38;2;184;134;11".into(),
128 "38;2;218;165;32".into(),
129 "38;2;255;255;0".into(),
130 ]}
131 }
132
133 pub fn ice() -> Self {
134 Theme { colors: [
135 "38;2;173;216;230".into(),
136 "38;2;135;206;250".into(),
137 "38;2;0;191;255".into(),
138 "38;2;240;248;255".into(),
139 ]}
140 }
141
142 pub fn magma() -> Self {
143 Theme { colors: [
144 "38;2;40;0;0".into(),
145 "38;2;120;0;0".into(),
146 "38;2;255;69;0".into(),
147 "38;2;255;140;0".into(),
148 ]}
149 }
150
151 pub fn nebula() -> Self {
152 Theme { colors: [
153 "38;2;25;25;112".into(),
154 "38;2;75;0;130".into(),
155 "38;2;219;112;147".into(),
156 "38;2;255;182;193".into(),
157 ]}
158 }
159
160 pub fn pink() -> Self {
161 Theme { colors: [
162 "38;2;255;105;180".into(),
163 "38;2;255;182;193".into(),
164 "38;2;255;240;245".into(),
165 "38;2;255;255;255".into(),
166 ]}
167 }
168
169 pub fn plasma() -> Self {
170 Theme { colors: [
171 "38;2;10;0;30".into(),
172 "38;2;75;0;130".into(),
173 "38;2;0;0;255".into(),
174 "38;2;100;149;237".into(),
175 ]}
176 }
177
178 pub fn sakura() -> Self {
179 Theme { colors: [
180 "38;2;80;20;40".into(),
181 "38;2;180;80;120".into(),
182 "38;2;255;150;180".into(),
183 "38;2;255;220;230".into(),
184 ]}
185 }
186
187 pub fn solar() -> Self {
188 Theme { colors: [
189 "38;2;139;69;19".into(),
190 "38;2;255;165;0".into(),
191 "38;2;255;215;0".into(),
192 "38;2;255;255;224".into(),
193 ]}
194 }
195
196 pub fn std() -> Self {
197 Theme { colors: [
198 "38;2;255;0;0".into(),
199 "38;2;255;85;0".into(),
200 "38;2;255;170;0".into(),
201 "38;2;255;255;0".into(),
202 ]}
203 }
204
205 pub fn sulfur() -> Self {
206 Theme { colors: [
207 "38;2;0;20;50".into(),
208 "38;2;65;105;225".into(),
209 "38;2;0;191;255".into(),
210 "38;2;175;238;238".into(),
211 ]}
212 }
213
214}
215
216fn rgb_to_ansi_str(rgb: (u8, u8, u8)) -> String {
217 format!("38;2;{};{};{}", rgb.0, rgb.1, rgb.2)
218}
219
220pub fn parse_custom_theme(input: &str) -> Option<Theme> {
221 let parts: Vec<&str> = input.split('.').collect();
223 if parts.is_empty() || parts.len() > 4 {
224 return None;
225 }
226
227 let mut rgb_list: Vec<(u8, u8, u8)> = Vec::new();
228 for part in &parts {
229 match parse_hex_color(part.trim()) {
230 Some(c) => rgb_list.push(c),
231 None => return None,
232 }
233 }
234
235 while rgb_list.len() < 4 {
236 let last = *rgb_list.last().unwrap();
237 rgb_list.push(last);
238 }
239
240 Some(Theme {
241 colors: [
242 rgb_to_ansi_str(rgb_list[0]),
243 rgb_to_ansi_str(rgb_list[1]),
244 rgb_to_ansi_str(rgb_list[2]),
245 rgb_to_ansi_str(rgb_list[3]),
246 ],
247 })
248}
249
250fn parse_hex_color(s: &str) -> Option<(u8, u8, u8)> {
251 let s = s.trim_start_matches('#');
252 if s.len() != 6 {
253 return None;
254 }
255 let r = u8::from_str_radix(&s[0..2], 16).ok()?;
256 let g = u8::from_str_radix(&s[2..4], 16).ok()?;
257 let b = u8::from_str_radix(&s[4..6], 16).ok()?;
258 Some((r, g, b))
259}
260
261pub fn hue_to_color_bytes(hue: f32, heat: usize) -> [u8; 3] {
262 let v = match heat {
263 0..=4 => 0.4,
264 5..=9 => 0.6,
265 10..=15 => 0.85,
266 _ => 1.0,
267 };
268
269 let hue = (hue / 4.0).round() * 4.0;
271
272 let h = hue / 60.0;
273 let i = h.floor() as u32;
274 let f = h - h.floor();
275 let p = 0.0f32;
276 let q = v * (1.0 - f);
277 let t = v * f;
278
279 let (r, g, b) = match i % 6 {
280 0 => (v, t, p),
281 1 => (q, v, p),
282 2 => (p, v, t),
283 3 => (p, q, v),
284 4 => (t, p, v),
285 _ => (v, p, q),
286 };
287
288 [(r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8]
289}