1use anstyle::{AnsiColor, Color, RgbColor, Style};
10
11#[derive(Debug, Clone, Hash, PartialEq, Eq)]
17pub struct Theme {
18 pub html_block_style: Style,
20 pub inline_html_style: Style,
22 pub code_style: Style,
24 pub link_style: Style,
26 pub image_link_style: Style,
28 pub rule_color: Color,
30 pub quote_border_style: Style,
32 pub h2_style: Style,
34 pub h3_style: Style,
36 pub h4_style: Style,
38 pub h5_style: Style,
40 pub h6_style: Style,
42 pub footnote_style: Style,
44 pub math_style: Style,
46 pub alert_note_style: Style,
48 pub alert_tip_style: Style,
50 pub alert_important_style: Style,
52 pub alert_warning_style: Style,
54 pub alert_caution_style: Style,
56 pub h1_prefix_style: Style,
58 pub h1_text_style: Style,
60}
61
62impl Theme {
63 pub fn with_h1(mut self, text_style: Style) -> Self {
65 let bg = text_style.get_bg_color();
66 self.h1_prefix_style = Style::new().bg_color(bg).fg_color(bg);
67 self.h1_text_style = text_style;
68 self
69 }
70}
71
72fn rgb(r: u8, g: u8, b: u8) -> Color {
73 RgbColor(r, g, b).into()
74}
75
76fn h1(bg: Color, fg: Color) -> (Style, Style) {
77 (
78 Style::new().bg_color(Some(bg)).fg_color(Some(bg)),
79 Style::new().bg_color(Some(bg)).fg_color(Some(fg)).bold(),
80 )
81}
82
83impl Theme {
84 pub fn dark() -> Self {
86 let (h1_prefix_style, h1_text_style) = (
87 Style::new()
88 .bg_color(Some(AnsiColor::BrightBlue.into()))
89 .fg_color(Some(AnsiColor::BrightBlue.into())),
90 Style::new()
91 .bg_color(Some(AnsiColor::BrightBlue.into()))
92 .fg_color(Some(AnsiColor::BrightWhite.into()))
93 .bold(),
94 );
95 Self {
96 html_block_style: Style::new().fg_color(Some(AnsiColor::Green.into())),
97 inline_html_style: Style::new().fg_color(Some(AnsiColor::Green.into())),
98 code_style: Style::new().fg_color(Some(AnsiColor::Yellow.into())),
99 link_style: Style::new().fg_color(Some(AnsiColor::Blue.into())),
100 image_link_style: Style::new().fg_color(Some(AnsiColor::Magenta.into())),
101 rule_color: AnsiColor::Green.into(),
102 quote_border_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())),
103 h2_style: Style::new().fg_color(Some(AnsiColor::Blue.into())).bold(),
104 h3_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())).bold(),
105 h4_style: Style::new().fg_color(Some(AnsiColor::Green.into())).bold(),
106 h5_style: Style::new().fg_color(Some(AnsiColor::Yellow.into())).bold(),
107 h6_style: Style::new()
108 .fg_color(Some(AnsiColor::Magenta.into()))
109 .bold(),
110 footnote_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())),
111 math_style: Style::new().fg_color(Some(AnsiColor::Yellow.into())),
112 alert_note_style: Style::new()
113 .fg_color(Some(AnsiColor::BrightBlue.into()))
114 .bold(),
115 alert_tip_style: Style::new()
116 .fg_color(Some(AnsiColor::BrightGreen.into()))
117 .bold(),
118 alert_important_style: Style::new()
119 .fg_color(Some(AnsiColor::BrightMagenta.into()))
120 .bold(),
121 alert_warning_style: Style::new()
122 .fg_color(Some(AnsiColor::BrightYellow.into()))
123 .bold(),
124 alert_caution_style: Style::new()
125 .fg_color(Some(AnsiColor::BrightRed.into()))
126 .bold(),
127 h1_prefix_style,
128 h1_text_style,
129 }
130 }
131
132 pub fn light() -> Self {
134 let (h1_prefix_style, h1_text_style) = (
135 Style::new()
136 .bg_color(Some(AnsiColor::BrightCyan.into()))
137 .fg_color(Some(AnsiColor::BrightCyan.into())),
138 Style::new()
139 .bg_color(Some(AnsiColor::BrightCyan.into()))
140 .fg_color(Some(AnsiColor::Black.into()))
141 .bold(),
142 );
143 Self {
144 html_block_style: Style::new().fg_color(Some(AnsiColor::Green.into())),
145 inline_html_style: Style::new().fg_color(Some(AnsiColor::Green.into())),
146 code_style: Style::new().fg_color(Some(AnsiColor::Blue.into())),
147 link_style: Style::new().fg_color(Some(AnsiColor::Blue.into())),
148 image_link_style: Style::new().fg_color(Some(AnsiColor::Magenta.into())),
149 rule_color: AnsiColor::Green.into(),
150 quote_border_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())),
151 h2_style: Style::new()
152 .fg_color(Some(AnsiColor::Magenta.into()))
153 .bold(),
154 h3_style: Style::new().fg_color(Some(AnsiColor::Blue.into())).bold(),
155 h4_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())).bold(),
156 h5_style: Style::new().fg_color(Some(AnsiColor::Green.into())).bold(),
157 h6_style: Style::new().fg_color(Some(AnsiColor::Red.into())).bold(),
158 footnote_style: Style::new().fg_color(Some(AnsiColor::Cyan.into())),
159 math_style: Style::new().fg_color(Some(AnsiColor::Blue.into())),
160 alert_note_style: Style::new().fg_color(Some(AnsiColor::Blue.into())).bold(),
161 alert_tip_style: Style::new().fg_color(Some(AnsiColor::Green.into())).bold(),
162 alert_important_style: Style::new()
163 .fg_color(Some(AnsiColor::Magenta.into()))
164 .bold(),
165 alert_warning_style: Style::new().fg_color(Some(AnsiColor::Red.into())).bold(),
166 alert_caution_style: Style::new()
167 .fg_color(Some(AnsiColor::BrightRed.into()))
168 .bold(),
169 h1_prefix_style,
170 h1_text_style,
171 }
172 }
173
174 pub fn catppuccin_mocha() -> Self {
176 let (h1_prefix_style, h1_text_style) = h1(rgb(203, 166, 247), rgb(17, 17, 27));
177 Self {
178 html_block_style: Style::new().fg_color(Some(rgb(250, 179, 135))),
179 inline_html_style: Style::new().fg_color(Some(rgb(250, 179, 135))),
180 code_style: Style::new().fg_color(Some(rgb(137, 220, 235))),
181 link_style: Style::new().fg_color(Some(rgb(137, 180, 250))),
182 image_link_style: Style::new().fg_color(Some(rgb(245, 194, 231))),
183 rule_color: rgb(148, 226, 213),
184 quote_border_style: Style::new().fg_color(Some(rgb(108, 112, 134))),
185 h2_style: Style::new().fg_color(Some(rgb(203, 166, 247))).bold(),
186 h3_style: Style::new().fg_color(Some(rgb(137, 180, 250))).bold(),
187 h4_style: Style::new().fg_color(Some(rgb(137, 220, 235))).bold(),
188 h5_style: Style::new().fg_color(Some(rgb(166, 227, 161))).bold(),
189 h6_style: Style::new().fg_color(Some(rgb(250, 179, 135))).bold(),
190 footnote_style: Style::new().fg_color(Some(rgb(108, 112, 134))),
191 math_style: Style::new().fg_color(Some(rgb(137, 220, 235))),
192 alert_note_style: Style::new().fg_color(Some(rgb(116, 199, 236))).bold(),
193 alert_tip_style: Style::new().fg_color(Some(rgb(166, 227, 161))).bold(),
194 alert_important_style: Style::new().fg_color(Some(rgb(203, 166, 247))).bold(),
195 alert_warning_style: Style::new().fg_color(Some(rgb(249, 226, 175))).bold(),
196 alert_caution_style: Style::new().fg_color(Some(rgb(243, 139, 168))).bold(),
197 h1_prefix_style,
198 h1_text_style,
199 }
200 }
201
202 pub fn catppuccin_latte() -> Self {
204 let (h1_prefix_style, h1_text_style) = h1(rgb(136, 57, 239), rgb(239, 241, 245));
205 Self {
206 html_block_style: Style::new().fg_color(Some(rgb(254, 100, 11))),
207 inline_html_style: Style::new().fg_color(Some(rgb(254, 100, 11))),
208 code_style: Style::new().fg_color(Some(rgb(4, 165, 229))),
209 link_style: Style::new().fg_color(Some(rgb(30, 102, 245))),
210 image_link_style: Style::new().fg_color(Some(rgb(234, 118, 203))),
211 rule_color: rgb(23, 146, 153),
212 quote_border_style: Style::new().fg_color(Some(rgb(124, 127, 147))),
213 h2_style: Style::new().fg_color(Some(rgb(136, 57, 239))).bold(),
214 h3_style: Style::new().fg_color(Some(rgb(30, 102, 245))).bold(),
215 h4_style: Style::new().fg_color(Some(rgb(23, 146, 153))).bold(),
216 h5_style: Style::new().fg_color(Some(rgb(64, 160, 43))).bold(),
217 h6_style: Style::new().fg_color(Some(rgb(254, 100, 11))).bold(),
218 footnote_style: Style::new().fg_color(Some(rgb(124, 127, 147))),
219 math_style: Style::new().fg_color(Some(rgb(4, 165, 229))),
220 alert_note_style: Style::new().fg_color(Some(rgb(32, 159, 181))).bold(),
221 alert_tip_style: Style::new().fg_color(Some(rgb(64, 160, 43))).bold(),
222 alert_important_style: Style::new().fg_color(Some(rgb(136, 57, 239))).bold(),
223 alert_warning_style: Style::new().fg_color(Some(rgb(223, 142, 29))).bold(),
224 alert_caution_style: Style::new().fg_color(Some(rgb(210, 15, 57))).bold(),
225 h1_prefix_style,
226 h1_text_style,
227 }
228 }
229
230 pub fn gruvbox_dark() -> Self {
232 let (h1_prefix_style, h1_text_style) = h1(rgb(250, 189, 47), rgb(40, 40, 40));
233 Self {
234 html_block_style: Style::new().fg_color(Some(rgb(214, 93, 14))),
235 inline_html_style: Style::new().fg_color(Some(rgb(214, 93, 14))),
236 code_style: Style::new().fg_color(Some(rgb(131, 165, 152))),
237 link_style: Style::new().fg_color(Some(rgb(131, 165, 152))),
238 image_link_style: Style::new().fg_color(Some(rgb(211, 134, 155))),
239 rule_color: rgb(184, 187, 38),
240 quote_border_style: Style::new().fg_color(Some(rgb(142, 192, 124))),
241 h2_style: Style::new().fg_color(Some(rgb(250, 189, 47))).bold(),
242 h3_style: Style::new().fg_color(Some(rgb(131, 165, 152))).bold(),
243 h4_style: Style::new().fg_color(Some(rgb(142, 192, 124))).bold(),
244 h5_style: Style::new().fg_color(Some(rgb(211, 134, 155))).bold(),
245 h6_style: Style::new().fg_color(Some(rgb(254, 128, 25))).bold(),
246 footnote_style: Style::new().fg_color(Some(rgb(146, 131, 116))),
247 math_style: Style::new().fg_color(Some(rgb(131, 165, 152))),
248 alert_note_style: Style::new().fg_color(Some(rgb(131, 165, 152))).bold(),
249 alert_tip_style: Style::new().fg_color(Some(rgb(184, 187, 38))).bold(),
250 alert_important_style: Style::new().fg_color(Some(rgb(211, 134, 155))).bold(),
251 alert_warning_style: Style::new().fg_color(Some(rgb(254, 128, 25))).bold(),
252 alert_caution_style: Style::new().fg_color(Some(rgb(251, 73, 52))).bold(),
253 h1_prefix_style,
254 h1_text_style,
255 }
256 }
257
258 pub fn gruvbox_light() -> Self {
260 let (h1_prefix_style, h1_text_style) = h1(rgb(181, 118, 20), rgb(251, 241, 199));
261 Self {
262 html_block_style: Style::new().fg_color(Some(rgb(175, 58, 3))),
263 inline_html_style: Style::new().fg_color(Some(rgb(175, 58, 3))),
264 code_style: Style::new().fg_color(Some(rgb(7, 102, 120))),
265 link_style: Style::new().fg_color(Some(rgb(7, 102, 120))),
266 image_link_style: Style::new().fg_color(Some(rgb(143, 63, 113))),
267 rule_color: rgb(66, 123, 88),
268 quote_border_style: Style::new().fg_color(Some(rgb(66, 123, 88))),
269 h2_style: Style::new().fg_color(Some(rgb(181, 118, 20))).bold(),
270 h3_style: Style::new().fg_color(Some(rgb(7, 102, 120))).bold(),
271 h4_style: Style::new().fg_color(Some(rgb(66, 123, 88))).bold(),
272 h5_style: Style::new().fg_color(Some(rgb(143, 63, 113))).bold(),
273 h6_style: Style::new().fg_color(Some(rgb(175, 58, 3))).bold(),
274 footnote_style: Style::new().fg_color(Some(rgb(124, 111, 100))),
275 math_style: Style::new().fg_color(Some(rgb(7, 102, 120))),
276 alert_note_style: Style::new().fg_color(Some(rgb(7, 102, 120))).bold(),
277 alert_tip_style: Style::new().fg_color(Some(rgb(121, 116, 14))).bold(),
278 alert_important_style: Style::new().fg_color(Some(rgb(143, 63, 113))).bold(),
279 alert_warning_style: Style::new().fg_color(Some(rgb(181, 118, 20))).bold(),
280 alert_caution_style: Style::new().fg_color(Some(rgb(157, 0, 6))).bold(),
281 h1_prefix_style,
282 h1_text_style,
283 }
284 }
285
286 pub fn dracula() -> Self {
288 let (h1_prefix_style, h1_text_style) = h1(rgb(189, 147, 249), rgb(40, 42, 54));
289 Self {
290 html_block_style: Style::new().fg_color(Some(rgb(255, 184, 108))),
291 inline_html_style: Style::new().fg_color(Some(rgb(255, 184, 108))),
292 code_style: Style::new().fg_color(Some(rgb(241, 250, 140))),
293 link_style: Style::new().fg_color(Some(rgb(139, 233, 253))),
294 image_link_style: Style::new().fg_color(Some(rgb(255, 121, 198))),
295 rule_color: rgb(80, 250, 123),
296 quote_border_style: Style::new().fg_color(Some(rgb(98, 114, 164))),
297 h2_style: Style::new().fg_color(Some(rgb(189, 147, 249))).bold(),
298 h3_style: Style::new().fg_color(Some(rgb(139, 233, 253))).bold(),
299 h4_style: Style::new().fg_color(Some(rgb(80, 250, 123))).bold(),
300 h5_style: Style::new().fg_color(Some(rgb(255, 121, 198))).bold(),
301 h6_style: Style::new().fg_color(Some(rgb(255, 184, 108))).bold(),
302 footnote_style: Style::new().fg_color(Some(rgb(98, 114, 164))),
303 math_style: Style::new().fg_color(Some(rgb(241, 250, 140))),
304 alert_note_style: Style::new().fg_color(Some(rgb(139, 233, 253))).bold(),
305 alert_tip_style: Style::new().fg_color(Some(rgb(80, 250, 123))).bold(),
306 alert_important_style: Style::new().fg_color(Some(rgb(255, 121, 198))).bold(),
307 alert_warning_style: Style::new().fg_color(Some(rgb(255, 184, 108))).bold(),
308 alert_caution_style: Style::new().fg_color(Some(rgb(255, 85, 85))).bold(),
309 h1_prefix_style,
310 h1_text_style,
311 }
312 }
313
314 pub fn nord() -> Self {
316 let (h1_prefix_style, h1_text_style) = h1(rgb(94, 129, 172), rgb(236, 239, 244));
317 Self {
318 html_block_style: Style::new().fg_color(Some(rgb(208, 135, 112))),
319 inline_html_style: Style::new().fg_color(Some(rgb(208, 135, 112))),
320 code_style: Style::new().fg_color(Some(rgb(143, 188, 187))),
321 link_style: Style::new().fg_color(Some(rgb(136, 192, 208))),
322 image_link_style: Style::new().fg_color(Some(rgb(180, 142, 173))),
323 rule_color: rgb(136, 192, 208),
324 quote_border_style: Style::new().fg_color(Some(rgb(76, 86, 106))),
325 h2_style: Style::new().fg_color(Some(rgb(94, 129, 172))).bold(),
326 h3_style: Style::new().fg_color(Some(rgb(129, 161, 193))).bold(),
327 h4_style: Style::new().fg_color(Some(rgb(136, 192, 208))).bold(),
328 h5_style: Style::new().fg_color(Some(rgb(163, 190, 140))).bold(),
329 h6_style: Style::new().fg_color(Some(rgb(235, 203, 139))).bold(),
330 footnote_style: Style::new().fg_color(Some(rgb(76, 86, 106))),
331 math_style: Style::new().fg_color(Some(rgb(143, 188, 187))),
332 alert_note_style: Style::new().fg_color(Some(rgb(143, 188, 187))).bold(),
333 alert_tip_style: Style::new().fg_color(Some(rgb(163, 190, 140))).bold(),
334 alert_important_style: Style::new().fg_color(Some(rgb(180, 142, 173))).bold(),
335 alert_warning_style: Style::new().fg_color(Some(rgb(235, 203, 139))).bold(),
336 alert_caution_style: Style::new().fg_color(Some(rgb(191, 97, 106))).bold(),
337 h1_prefix_style,
338 h1_text_style,
339 }
340 }
341
342 pub fn solarized_dark() -> Self {
344 let (h1_prefix_style, h1_text_style) = h1(rgb(38, 139, 210), rgb(0, 43, 54));
345 Self {
346 html_block_style: Style::new().fg_color(Some(rgb(203, 75, 22))),
347 inline_html_style: Style::new().fg_color(Some(rgb(203, 75, 22))),
348 code_style: Style::new().fg_color(Some(rgb(42, 161, 152))),
349 link_style: Style::new().fg_color(Some(rgb(38, 139, 210))),
350 image_link_style: Style::new().fg_color(Some(rgb(211, 54, 130))),
351 rule_color: rgb(42, 161, 152),
352 quote_border_style: Style::new().fg_color(Some(rgb(88, 110, 117))),
353 h2_style: Style::new().fg_color(Some(rgb(38, 139, 210))).bold(),
354 h3_style: Style::new().fg_color(Some(rgb(42, 161, 152))).bold(),
355 h4_style: Style::new().fg_color(Some(rgb(133, 153, 0))).bold(),
356 h5_style: Style::new().fg_color(Some(rgb(211, 54, 130))).bold(),
357 h6_style: Style::new().fg_color(Some(rgb(203, 75, 22))).bold(),
358 footnote_style: Style::new().fg_color(Some(rgb(88, 110, 117))),
359 math_style: Style::new().fg_color(Some(rgb(42, 161, 152))),
360 alert_note_style: Style::new().fg_color(Some(rgb(108, 113, 196))).bold(),
361 alert_tip_style: Style::new().fg_color(Some(rgb(133, 153, 0))).bold(),
362 alert_important_style: Style::new().fg_color(Some(rgb(211, 54, 130))).bold(),
363 alert_warning_style: Style::new().fg_color(Some(rgb(181, 137, 0))).bold(),
364 alert_caution_style: Style::new().fg_color(Some(rgb(220, 50, 47))).bold(),
365 h1_prefix_style,
366 h1_text_style,
367 }
368 }
369
370 pub fn solarized_light() -> Self {
372 let (h1_prefix_style, h1_text_style) = h1(rgb(38, 139, 210), rgb(253, 246, 227));
373 Self {
374 h1_prefix_style,
375 h1_text_style,
376 ..Self::solarized_dark()
377 }
378 }
379}
380
381impl Default for Theme {
382 fn default() -> Self {
383 Self::dark()
384 }
385}
386
387pub trait CombineStyle {
389 fn on_top_of(self, other: &Self) -> Self;
394}
395
396impl CombineStyle for Style {
397 fn on_top_of(self, other: &Style) -> Style {
399 Style::new()
400 .fg_color(self.get_fg_color().or(other.get_fg_color()))
401 .bg_color(self.get_bg_color().or(other.get_bg_color()))
402 .effects(other.get_effects() | self.get_effects())
403 .underline_color(self.get_underline_color().or(other.get_underline_color()))
404 }
405}