agentic_core/
theme.rs

1// theme.rs
2//! Everforest Theme System for Agentic
3//!
4//! Implements a comprehensive theming architecture with Everforest Dark/Light variants.
5//! Provides clean separation of concerns and runtime theme switching capability.
6
7use ratatui::style::{Color, Modifier, Style};
8use serde::{Deserialize, Serialize};
9
10/// Theme variants supported by Agentic
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum ThemeVariant {
13    /// Everforest Dark theme (default)
14    EverforestDark,
15    /// Everforest Light theme
16    EverforestLight,
17}
18
19impl Default for ThemeVariant {
20    fn default() -> Self {
21        Self::EverforestDark
22    }
23}
24
25/// Color palette for a theme variant
26#[derive(Debug, Clone)]
27pub struct ColorPalette {
28    pub background: Color,
29    pub foreground: Color,
30    pub accent: Color,
31    pub secondary: Color,
32    pub info: Color,
33    pub border: Color,
34    pub selection: Color,
35    pub cursor: Color,
36    pub warning: Color, // Yellow/orange for settings
37}
38
39/// UI element types for styling
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum Element {
42    /// Normal text content
43    Text,
44    /// Titles and headers
45    Title,
46    /// Borders and frames
47    Border,
48    /// Highlighted/selected items
49    Highlight,
50    /// Accent elements (buttons, links)
51    Accent,
52    /// Secondary elements
53    Secondary,
54    /// Information/status elements
55    Info,
56    /// Background elements
57    Background,
58    /// Active/focused elements
59    Active,
60    /// Inactive/disabled elements
61    Inactive,
62    /// Warning/settings elements (yellow/orange)
63    Warning,
64}
65
66/// Main theme structure managing all UI styling
67#[derive(Debug, Clone)]
68pub struct Theme {
69    variant: ThemeVariant,
70    colors: ColorPalette,
71}
72
73impl Default for Theme {
74    fn default() -> Self {
75        Self::new(ThemeVariant::default())
76    }
77}
78
79impl Theme {
80    /// Create a new theme with the specified variant
81    pub fn new(variant: ThemeVariant) -> Self {
82        let colors = match variant {
83            ThemeVariant::EverforestDark => ColorPalette {
84                background: Color::Rgb(45, 53, 59),    // #2d353b
85                foreground: Color::Rgb(211, 198, 170), // #d3c6aa
86                accent: Color::Rgb(167, 192, 128),     // #a7c080 (green)
87                secondary: Color::Rgb(230, 126, 128),  // #e67e80 (red)
88                info: Color::Rgb(127, 187, 179),       // #7fbbb3 (aqua)
89                border: Color::Rgb(130, 140, 150),     // #828c96 (lighter gray for better contrast)
90                selection: Color::Rgb(64, 72, 78),     // #40484e (darker bg)
91                cursor: Color::Rgb(211, 198, 170),     // #d3c6aa (same as fg)
92                warning: Color::Rgb(219, 188, 127),    // #dbbc7f (yellow/orange)
93            },
94            ThemeVariant::EverforestLight => ColorPalette {
95                background: Color::Rgb(253, 246, 227), // #fdf6e3
96                foreground: Color::Rgb(76, 86, 94),    // #4c565e (darker for better readability)
97                accent: Color::Rgb(141, 161, 1),       // #8da101 (green)
98                secondary: Color::Rgb(248, 85, 82),    // #f85552 (red)
99                info: Color::Rgb(53, 167, 124),        // #35a77c (aqua)
100                border: Color::Rgb(150, 160, 170),     // #96a0aa (gray)
101                selection: Color::Rgb(243, 236, 217),  // #f3ecd9 (darker bg)
102                cursor: Color::Rgb(76, 86, 94),        // #4c565e (same as fg)
103                warning: Color::Rgb(207, 131, 44),     // #cf832c (yellow/orange)
104            },
105        };
106
107        Self { variant, colors }
108    }
109
110    /// Get the current theme variant
111    pub fn variant(&self) -> ThemeVariant {
112        self.variant
113    }
114
115    /// Get the color palette
116    pub fn colors(&self) -> &ColorPalette {
117        &self.colors
118    }
119
120    /// Toggle between dark and light variants
121    pub fn toggle(&mut self) {
122        self.variant = match self.variant {
123            ThemeVariant::EverforestDark => ThemeVariant::EverforestLight,
124            ThemeVariant::EverforestLight => ThemeVariant::EverforestDark,
125        };
126        *self = Self::new(self.variant);
127    }
128
129    /// Set specific theme variant
130    pub fn set_variant(&mut self, variant: ThemeVariant) {
131        if self.variant != variant {
132            self.variant = variant;
133            *self = Self::new(self.variant);
134        }
135    }
136
137    /// Get a ratatui Style for the specified UI element
138    pub fn ratatui_style(&self, element: Element) -> Style {
139        match element {
140            Element::Text => Style::default()
141                .fg(self.colors.foreground)
142                .bg(self.colors.background),
143
144            Element::Title => Style::default()
145                .fg(self.colors.accent)
146                .bg(self.colors.background)
147                .add_modifier(Modifier::BOLD),
148
149            Element::Border => Style::default()
150                .fg(self.colors.border)
151                .bg(self.colors.background),
152
153            Element::Highlight => Style::default()
154                .fg(self.colors.foreground)
155                .bg(self.colors.selection)
156                .add_modifier(Modifier::BOLD),
157
158            Element::Accent => Style::default()
159                .fg(self.colors.accent)
160                .bg(self.colors.background)
161                .add_modifier(Modifier::BOLD),
162
163            Element::Secondary => Style::default()
164                .fg(self.colors.secondary)
165                .bg(self.colors.background),
166
167            Element::Info => Style::default()
168                .fg(self.colors.info)
169                .bg(self.colors.background),
170
171            Element::Background => Style::default()
172                .fg(self.colors.foreground)
173                .bg(self.colors.background),
174
175            Element::Active => Style::default()
176                .fg(self.colors.accent)
177                .bg(self.colors.selection)
178                .add_modifier(Modifier::BOLD),
179
180            Element::Inactive => Style::default()
181                .fg(self.colors.border)
182                .bg(self.colors.background),
183
184            Element::Warning => Style::default()
185                .fg(self.colors.warning)
186                .bg(self.colors.background),
187        }
188    }
189
190    /// Get foreground color for an element
191    pub fn fg_color(&self, element: Element) -> Color {
192        match element {
193            Element::Text | Element::Background => self.colors.foreground,
194            Element::Title | Element::Accent | Element::Active => self.colors.accent,
195            Element::Border | Element::Inactive => self.colors.border,
196            Element::Highlight => self.colors.foreground,
197            Element::Secondary => self.colors.secondary,
198            Element::Info => self.colors.info,
199            Element::Warning => self.colors.warning,
200        }
201    }
202
203    /// Get background color for an element
204    pub fn bg_color(&self, element: Element) -> Color {
205        match element {
206            Element::Highlight | Element::Active => self.colors.selection,
207            _ => self.colors.background,
208        }
209    }
210
211    /// Get style for block titles
212    pub fn title_style(&self) -> Style {
213        self.ratatui_style(Element::Title)
214    }
215
216    /// Get style for block borders
217    pub fn border_style(&self) -> Style {
218        self.ratatui_style(Element::Border)
219    }
220
221    /// Get style for normal text
222    pub fn text_style(&self) -> Style {
223        self.ratatui_style(Element::Text)
224    }
225
226    /// Get style for highlighted/selected items
227    pub fn highlight_style(&self) -> Style {
228        self.ratatui_style(Element::Highlight)
229    }
230
231    /// Get style for accent elements
232    pub fn accent_style(&self) -> Style {
233        self.ratatui_style(Element::Accent)
234    }
235
236    /// Get style for secondary elements
237    pub fn secondary_style(&self) -> Style {
238        self.ratatui_style(Element::Secondary)
239    }
240
241    /// Get style for info elements
242    pub fn info_style(&self) -> Style {
243        self.ratatui_style(Element::Info)
244    }
245
246    /// Get style for warning/settings elements
247    pub fn warning_style(&self) -> Style {
248        self.ratatui_style(Element::Warning)
249    }
250}