1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub enum Color {
14 Reset,
16 Black,
18 Red,
20 Green,
22 Yellow,
24 Blue,
26 Magenta,
28 Cyan,
30 White,
32 Rgb(u8, u8, u8),
34 Indexed(u8),
36}
37
38impl Color {
39 fn to_rgb(self) -> (u8, u8, u8) {
44 match self {
45 Color::Rgb(r, g, b) => (r, g, b),
46 Color::Black => (0, 0, 0),
47 Color::Red => (205, 49, 49),
48 Color::Green => (13, 188, 121),
49 Color::Yellow => (229, 229, 16),
50 Color::Blue => (36, 114, 200),
51 Color::Magenta => (188, 63, 188),
52 Color::Cyan => (17, 168, 205),
53 Color::White => (229, 229, 229),
54 Color::Reset => (0, 0, 0),
55 Color::Indexed(idx) => xterm256_to_rgb(idx),
56 }
57 }
58
59 pub fn luminance(self) -> f32 {
77 let (r, g, b) = self.to_rgb();
78 let rf = r as f32 / 255.0;
79 let gf = g as f32 / 255.0;
80 let bf = b as f32 / 255.0;
81 0.2126 * rf + 0.7152 * gf + 0.0722 * bf
82 }
83
84 pub fn contrast_fg(bg: Color) -> Color {
100 if bg.luminance() > 0.5 {
101 Color::Rgb(0, 0, 0)
102 } else {
103 Color::Rgb(255, 255, 255)
104 }
105 }
106
107 pub fn blend(self, other: Color, alpha: f32) -> Color {
123 let alpha = alpha.clamp(0.0, 1.0);
124 let (r1, g1, b1) = self.to_rgb();
125 let (r2, g2, b2) = other.to_rgb();
126 let r = (r1 as f32 * alpha + r2 as f32 * (1.0 - alpha)) as u8;
127 let g = (g1 as f32 * alpha + g2 as f32 * (1.0 - alpha)) as u8;
128 let b = (b1 as f32 * alpha + b2 as f32 * (1.0 - alpha)) as u8;
129 Color::Rgb(r, g, b)
130 }
131
132 pub fn lighten(self, amount: f32) -> Color {
137 Color::Rgb(255, 255, 255).blend(self, 1.0 - amount.clamp(0.0, 1.0))
138 }
139
140 pub fn darken(self, amount: f32) -> Color {
145 Color::Rgb(0, 0, 0).blend(self, 1.0 - amount.clamp(0.0, 1.0))
146 }
147}
148
149fn xterm256_to_rgb(idx: u8) -> (u8, u8, u8) {
150 match idx {
151 0 => (0, 0, 0),
152 1 => (128, 0, 0),
153 2 => (0, 128, 0),
154 3 => (128, 128, 0),
155 4 => (0, 0, 128),
156 5 => (128, 0, 128),
157 6 => (0, 128, 128),
158 7 => (192, 192, 192),
159 8 => (128, 128, 128),
160 9 => (255, 0, 0),
161 10 => (0, 255, 0),
162 11 => (255, 255, 0),
163 12 => (0, 0, 255),
164 13 => (255, 0, 255),
165 14 => (0, 255, 255),
166 15 => (255, 255, 255),
167 16..=231 => {
168 let n = idx - 16;
169 let b_idx = n % 6;
170 let g_idx = (n / 6) % 6;
171 let r_idx = n / 36;
172 let to_val = |i: u8| if i == 0 { 0u8 } else { 55 + 40 * i };
173 (to_val(r_idx), to_val(g_idx), to_val(b_idx))
174 }
175 232..=255 => {
176 let v = 8 + 10 * (idx - 232);
177 (v, v, v)
178 }
179 }
180}
181
182#[derive(Debug, Clone, Copy)]
188#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
189pub struct Theme {
190 pub primary: Color,
192 pub secondary: Color,
194 pub accent: Color,
196 pub text: Color,
198 pub text_dim: Color,
200 pub border: Color,
202 pub bg: Color,
204 pub success: Color,
206 pub warning: Color,
208 pub error: Color,
210 pub selected_bg: Color,
212 pub selected_fg: Color,
214 pub surface: Color,
216 pub surface_hover: Color,
221 pub surface_text: Color,
227}
228
229impl Theme {
230 pub fn dark() -> Self {
232 Self {
233 primary: Color::Cyan,
234 secondary: Color::Blue,
235 accent: Color::Magenta,
236 text: Color::White,
237 text_dim: Color::Indexed(245),
238 border: Color::Indexed(240),
239 bg: Color::Reset,
240 success: Color::Green,
241 warning: Color::Yellow,
242 error: Color::Red,
243 selected_bg: Color::Cyan,
244 selected_fg: Color::Black,
245 surface: Color::Indexed(236),
246 surface_hover: Color::Indexed(238),
247 surface_text: Color::Indexed(250),
248 }
249 }
250
251 pub fn light() -> Self {
253 Self {
254 primary: Color::Blue,
255 secondary: Color::Cyan,
256 accent: Color::Magenta,
257 text: Color::Black,
258 text_dim: Color::Indexed(240),
259 border: Color::Indexed(245),
260 bg: Color::Reset,
261 success: Color::Green,
262 warning: Color::Yellow,
263 error: Color::Red,
264 selected_bg: Color::Blue,
265 selected_fg: Color::White,
266 surface: Color::Indexed(254),
267 surface_hover: Color::Indexed(252),
268 surface_text: Color::Indexed(238),
269 }
270 }
271
272 pub fn dracula() -> Self {
274 Self {
275 primary: Color::Rgb(189, 147, 249),
276 secondary: Color::Rgb(139, 233, 253),
277 accent: Color::Rgb(255, 121, 198),
278 text: Color::Rgb(248, 248, 242),
279 text_dim: Color::Rgb(98, 114, 164),
280 border: Color::Rgb(68, 71, 90),
281 bg: Color::Rgb(40, 42, 54),
282 success: Color::Rgb(80, 250, 123),
283 warning: Color::Rgb(241, 250, 140),
284 error: Color::Rgb(255, 85, 85),
285 selected_bg: Color::Rgb(189, 147, 249),
286 selected_fg: Color::Rgb(40, 42, 54),
287 surface: Color::Rgb(68, 71, 90),
288 surface_hover: Color::Rgb(98, 100, 120),
289 surface_text: Color::Rgb(191, 194, 210),
290 }
291 }
292
293 pub fn catppuccin() -> Self {
295 Self {
296 primary: Color::Rgb(180, 190, 254),
297 secondary: Color::Rgb(137, 180, 250),
298 accent: Color::Rgb(245, 194, 231),
299 text: Color::Rgb(205, 214, 244),
300 text_dim: Color::Rgb(127, 132, 156),
301 border: Color::Rgb(88, 91, 112),
302 bg: Color::Rgb(30, 30, 46),
303 success: Color::Rgb(166, 227, 161),
304 warning: Color::Rgb(249, 226, 175),
305 error: Color::Rgb(243, 139, 168),
306 selected_bg: Color::Rgb(180, 190, 254),
307 selected_fg: Color::Rgb(30, 30, 46),
308 surface: Color::Rgb(49, 50, 68),
309 surface_hover: Color::Rgb(69, 71, 90),
310 surface_text: Color::Rgb(166, 173, 200),
311 }
312 }
313
314 pub fn nord() -> Self {
316 Self {
317 primary: Color::Rgb(136, 192, 208),
318 secondary: Color::Rgb(129, 161, 193),
319 accent: Color::Rgb(180, 142, 173),
320 text: Color::Rgb(236, 239, 244),
321 text_dim: Color::Rgb(76, 86, 106),
322 border: Color::Rgb(76, 86, 106),
323 bg: Color::Rgb(46, 52, 64),
324 success: Color::Rgb(163, 190, 140),
325 warning: Color::Rgb(235, 203, 139),
326 error: Color::Rgb(191, 97, 106),
327 selected_bg: Color::Rgb(136, 192, 208),
328 selected_fg: Color::Rgb(46, 52, 64),
329 surface: Color::Rgb(59, 66, 82),
330 surface_hover: Color::Rgb(67, 76, 94),
331 surface_text: Color::Rgb(216, 222, 233),
332 }
333 }
334
335 pub fn solarized_dark() -> Self {
337 Self {
338 primary: Color::Rgb(38, 139, 210),
339 secondary: Color::Rgb(42, 161, 152),
340 accent: Color::Rgb(211, 54, 130),
341 text: Color::Rgb(131, 148, 150),
342 text_dim: Color::Rgb(88, 110, 117),
343 border: Color::Rgb(88, 110, 117),
344 bg: Color::Rgb(0, 43, 54),
345 success: Color::Rgb(133, 153, 0),
346 warning: Color::Rgb(181, 137, 0),
347 error: Color::Rgb(220, 50, 47),
348 selected_bg: Color::Rgb(38, 139, 210),
349 selected_fg: Color::Rgb(253, 246, 227),
350 surface: Color::Rgb(7, 54, 66),
351 surface_hover: Color::Rgb(23, 72, 85),
352 surface_text: Color::Rgb(147, 161, 161),
353 }
354 }
355
356 pub fn tokyo_night() -> Self {
358 Self {
359 primary: Color::Rgb(122, 162, 247),
360 secondary: Color::Rgb(125, 207, 255),
361 accent: Color::Rgb(187, 154, 247),
362 text: Color::Rgb(169, 177, 214),
363 text_dim: Color::Rgb(86, 95, 137),
364 border: Color::Rgb(54, 58, 79),
365 bg: Color::Rgb(26, 27, 38),
366 success: Color::Rgb(158, 206, 106),
367 warning: Color::Rgb(224, 175, 104),
368 error: Color::Rgb(247, 118, 142),
369 selected_bg: Color::Rgb(122, 162, 247),
370 selected_fg: Color::Rgb(26, 27, 38),
371 surface: Color::Rgb(36, 40, 59),
372 surface_hover: Color::Rgb(41, 46, 66),
373 surface_text: Color::Rgb(192, 202, 245),
374 }
375 }
376}
377
378impl Default for Theme {
379 fn default() -> Self {
380 Self::dark()
381 }
382}
383
384#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
389#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
390pub enum Border {
391 Single,
393 Double,
395 Rounded,
397 Thick,
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
406pub struct BorderChars {
407 pub tl: char,
409 pub tr: char,
411 pub bl: char,
413 pub br: char,
415 pub h: char,
417 pub v: char,
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
424pub struct BorderSides {
425 pub top: bool,
426 pub right: bool,
427 pub bottom: bool,
428 pub left: bool,
429}
430
431impl BorderSides {
432 pub const fn all() -> Self {
433 Self {
434 top: true,
435 right: true,
436 bottom: true,
437 left: true,
438 }
439 }
440
441 pub const fn none() -> Self {
442 Self {
443 top: false,
444 right: false,
445 bottom: false,
446 left: false,
447 }
448 }
449
450 pub const fn horizontal() -> Self {
451 Self {
452 top: true,
453 right: false,
454 bottom: true,
455 left: false,
456 }
457 }
458
459 pub const fn vertical() -> Self {
460 Self {
461 top: false,
462 right: true,
463 bottom: false,
464 left: true,
465 }
466 }
467
468 pub fn has_horizontal(&self) -> bool {
469 self.top || self.bottom
470 }
471
472 pub fn has_vertical(&self) -> bool {
473 self.left || self.right
474 }
475}
476
477impl Default for BorderSides {
478 fn default() -> Self {
479 Self::all()
480 }
481}
482
483impl Border {
484 pub const fn chars(self) -> BorderChars {
486 match self {
487 Self::Single => BorderChars {
488 tl: '┌',
489 tr: '┐',
490 bl: '└',
491 br: '┘',
492 h: '─',
493 v: '│',
494 },
495 Self::Double => BorderChars {
496 tl: '╔',
497 tr: '╗',
498 bl: '╚',
499 br: '╝',
500 h: '═',
501 v: '║',
502 },
503 Self::Rounded => BorderChars {
504 tl: '╭',
505 tr: '╮',
506 bl: '╰',
507 br: '╯',
508 h: '─',
509 v: '│',
510 },
511 Self::Thick => BorderChars {
512 tl: '┏',
513 tr: '┓',
514 bl: '┗',
515 br: '┛',
516 h: '━',
517 v: '┃',
518 },
519 }
520 }
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529pub struct Padding {
530 pub top: u32,
532 pub right: u32,
534 pub bottom: u32,
536 pub left: u32,
538}
539
540impl Padding {
541 pub const fn all(v: u32) -> Self {
543 Self::new(v, v, v, v)
544 }
545
546 pub const fn xy(x: u32, y: u32) -> Self {
548 Self::new(y, x, y, x)
549 }
550
551 pub const fn new(top: u32, right: u32, bottom: u32, left: u32) -> Self {
553 Self {
554 top,
555 right,
556 bottom,
557 left,
558 }
559 }
560
561 pub const fn horizontal(self) -> u32 {
563 self.left + self.right
564 }
565
566 pub const fn vertical(self) -> u32 {
568 self.top + self.bottom
569 }
570}
571
572#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
577#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
578pub struct Margin {
579 pub top: u32,
581 pub right: u32,
583 pub bottom: u32,
585 pub left: u32,
587}
588
589impl Margin {
590 pub const fn all(v: u32) -> Self {
592 Self::new(v, v, v, v)
593 }
594
595 pub const fn xy(x: u32, y: u32) -> Self {
597 Self::new(y, x, y, x)
598 }
599
600 pub const fn new(top: u32, right: u32, bottom: u32, left: u32) -> Self {
602 Self {
603 top,
604 right,
605 bottom,
606 left,
607 }
608 }
609
610 pub const fn horizontal(self) -> u32 {
612 self.left + self.right
613 }
614
615 pub const fn vertical(self) -> u32 {
617 self.top + self.bottom
618 }
619}
620
621#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
634#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
635#[must_use = "configure constraints using the returned value"]
636pub struct Constraints {
637 pub min_width: Option<u32>,
639 pub max_width: Option<u32>,
641 pub min_height: Option<u32>,
643 pub max_height: Option<u32>,
645 pub width_pct: Option<u8>,
647 pub height_pct: Option<u8>,
649}
650
651impl Constraints {
652 pub const fn min_w(mut self, min_width: u32) -> Self {
654 self.min_width = Some(min_width);
655 self
656 }
657
658 pub const fn max_w(mut self, max_width: u32) -> Self {
660 self.max_width = Some(max_width);
661 self
662 }
663
664 pub const fn min_h(mut self, min_height: u32) -> Self {
666 self.min_height = Some(min_height);
667 self
668 }
669
670 pub const fn max_h(mut self, max_height: u32) -> Self {
672 self.max_height = Some(max_height);
673 self
674 }
675
676 pub const fn w_pct(mut self, pct: u8) -> Self {
678 self.width_pct = Some(pct);
679 self
680 }
681
682 pub const fn h_pct(mut self, pct: u8) -> Self {
684 self.height_pct = Some(pct);
685 self
686 }
687}
688
689#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
695#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
696pub enum Align {
697 #[default]
699 Start,
700 Center,
702 End,
704}
705
706#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
715#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
716pub enum Justify {
717 #[default]
719 Start,
720 Center,
722 End,
724 SpaceBetween,
726 SpaceAround,
728 SpaceEvenly,
730}
731
732#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
737#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
738#[cfg_attr(feature = "serde", serde(transparent))]
739pub struct Modifiers(pub u8);
740
741impl Modifiers {
742 pub const NONE: Self = Self(0);
744 pub const BOLD: Self = Self(1 << 0);
746 pub const DIM: Self = Self(1 << 1);
748 pub const ITALIC: Self = Self(1 << 2);
750 pub const UNDERLINE: Self = Self(1 << 3);
752 pub const REVERSED: Self = Self(1 << 4);
754 pub const STRIKETHROUGH: Self = Self(1 << 5);
756
757 #[inline]
759 pub fn contains(self, other: Self) -> bool {
760 (self.0 & other.0) == other.0
761 }
762
763 #[inline]
765 pub fn insert(&mut self, other: Self) {
766 self.0 |= other.0;
767 }
768
769 #[inline]
771 pub fn is_empty(self) -> bool {
772 self.0 == 0
773 }
774}
775
776impl std::ops::BitOr for Modifiers {
777 type Output = Self;
778 #[inline]
779 fn bitor(self, rhs: Self) -> Self {
780 Self(self.0 | rhs.0)
781 }
782}
783
784impl std::ops::BitOrAssign for Modifiers {
785 #[inline]
786 fn bitor_assign(&mut self, rhs: Self) {
787 self.0 |= rhs.0;
788 }
789}
790
791#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
805#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
806#[must_use = "build and pass the returned Style value"]
807pub struct Style {
808 pub fg: Option<Color>,
810 pub bg: Option<Color>,
812 pub modifiers: Modifiers,
814}
815
816impl Style {
817 pub const fn new() -> Self {
819 Self {
820 fg: None,
821 bg: None,
822 modifiers: Modifiers::NONE,
823 }
824 }
825
826 pub const fn fg(mut self, color: Color) -> Self {
828 self.fg = Some(color);
829 self
830 }
831
832 pub const fn bg(mut self, color: Color) -> Self {
834 self.bg = Some(color);
835 self
836 }
837
838 pub fn bold(mut self) -> Self {
840 self.modifiers |= Modifiers::BOLD;
841 self
842 }
843
844 pub fn dim(mut self) -> Self {
846 self.modifiers |= Modifiers::DIM;
847 self
848 }
849
850 pub fn italic(mut self) -> Self {
852 self.modifiers |= Modifiers::ITALIC;
853 self
854 }
855
856 pub fn underline(mut self) -> Self {
858 self.modifiers |= Modifiers::UNDERLINE;
859 self
860 }
861
862 pub fn reversed(mut self) -> Self {
864 self.modifiers |= Modifiers::REVERSED;
865 self
866 }
867
868 pub fn strikethrough(mut self) -> Self {
870 self.modifiers |= Modifiers::STRIKETHROUGH;
871 self
872 }
873}