git_iris/theme/adapters/
ratatui.rs1use ratatui::style::{Color, Modifier, Style};
6
7use crate::theme::{Gradient, ThemeColor, ThemeStyle};
8
9pub trait ToRatatuiColor {
11 fn to_ratatui(&self) -> Color;
13}
14
15impl ToRatatuiColor for ThemeColor {
16 fn to_ratatui(&self) -> Color {
17 Color::Rgb(self.r, self.g, self.b)
18 }
19}
20
21pub trait ToRatatuiStyle {
23 fn to_ratatui(&self) -> Style;
25}
26
27impl ToRatatuiStyle for ThemeStyle {
28 fn to_ratatui(&self) -> Style {
29 let mut style = Style::default();
30
31 if let Some(fg) = self.fg {
32 style = style.fg(fg.to_ratatui());
33 }
34
35 if let Some(bg) = self.bg {
36 style = style.bg(bg.to_ratatui());
37 }
38
39 let mut modifiers = Modifier::empty();
40 if self.bold {
41 modifiers |= Modifier::BOLD;
42 }
43 if self.italic {
44 modifiers |= Modifier::ITALIC;
45 }
46 if self.underline {
47 modifiers |= Modifier::UNDERLINED;
48 }
49 if self.dim {
50 modifiers |= Modifier::DIM;
51 }
52
53 if !modifiers.is_empty() {
54 style = style.add_modifier(modifiers);
55 }
56
57 style
58 }
59}
60
61pub trait ThemeColorExt {
63 fn ratatui_color(&self, token: &str) -> Color;
65
66 fn ratatui_style(&self, name: &str) -> Style;
68
69 fn ratatui_gradient(&self, name: &str, t: f32) -> Color;
71}
72
73impl ThemeColorExt for crate::theme::Theme {
74 fn ratatui_color(&self, token: &str) -> Color {
75 self.color(token).to_ratatui()
76 }
77
78 fn ratatui_style(&self, name: &str) -> Style {
79 self.style(name).to_ratatui()
80 }
81
82 fn ratatui_gradient(&self, name: &str, t: f32) -> Color {
83 self.gradient(name, t).to_ratatui()
84 }
85}
86
87#[allow(clippy::cast_precision_loss, clippy::as_conversions)]
92pub fn gradient_spans(text: &str, gradient: &Gradient) -> Vec<ratatui::text::Span<'static>> {
93 let len = text.chars().count().max(1);
94 text.chars()
95 .enumerate()
96 .map(|(i, c)| {
97 let t = if len == 1 {
98 0.0
99 } else {
100 i as f32 / (len - 1) as f32
101 };
102 let color = gradient.at(t).to_ratatui();
103 ratatui::text::Span::styled(c.to_string(), Style::default().fg(color))
104 })
105 .collect()
106}
107
108#[allow(clippy::cast_precision_loss, clippy::as_conversions)]
110pub fn gradient_line(
111 width: usize,
112 ch: char,
113 gradient: &Gradient,
114) -> Vec<ratatui::text::Span<'static>> {
115 (0..width)
116 .map(|i| {
117 let t = if width <= 1 {
118 0.0
119 } else {
120 i as f32 / (width - 1) as f32
121 };
122 let color = gradient.at(t).to_ratatui();
123 ratatui::text::Span::styled(ch.to_string(), Style::default().fg(color))
124 })
125 .collect()
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131 use crate::theme::ThemeColor;
132
133 #[test]
134 fn test_color_conversion() {
135 let theme_color = ThemeColor::new(225, 53, 255);
136 let ratatui_color = theme_color.to_ratatui();
137 assert_eq!(ratatui_color, Color::Rgb(225, 53, 255));
138 }
139
140 #[test]
141 fn test_style_conversion() {
142 let theme_style = ThemeStyle::fg(ThemeColor::new(255, 0, 0))
143 .with_bg(ThemeColor::new(0, 0, 0))
144 .bold()
145 .italic();
146
147 let ratatui_style = theme_style.to_ratatui();
148
149 assert_eq!(ratatui_style.fg, Some(Color::Rgb(255, 0, 0)));
150 assert_eq!(ratatui_style.bg, Some(Color::Rgb(0, 0, 0)));
151 assert!(ratatui_style.add_modifier.contains(Modifier::BOLD));
152 assert!(ratatui_style.add_modifier.contains(Modifier::ITALIC));
153 }
154}