0xSerofix stuff47e64ac11 months agoHistory
386 lines · 11.8 KB · Rust
RawDownload
//! Dynamic theme system for Azul browser//!//! Provides 7 built-in themes with the ability to cycle through them at runtime. use ratatui::style::Color; /// Available theme presets#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum ThemePreset {    AzulDark,    // Default - Tokyo Night based    Monokai,     // Classic Monokai    Gruvbox,     // Gruvbox Dark    Nord,        // Nord theme    Dracula,     // Dracula theme    Catppuccin,  // Catppuccin Mocha    Solarized,   // Solarized Dark} impl ThemePreset {    pub fn name(&self) -> &'static str {        match self {            ThemePreset::AzulDark => "Azul Dark",            ThemePreset::Monokai => "Monokai",            ThemePreset::Gruvbox => "Gruvbox",            ThemePreset::Nord => "Nord",            ThemePreset::Dracula => "Dracula",            ThemePreset::Catppuccin => "Catppuccin",            ThemePreset::Solarized => "Solarized",        }    }     pub fn all() -> &'static [ThemePreset] {        &[            ThemePreset::AzulDark,            ThemePreset::Monokai,            ThemePreset::Gruvbox,            ThemePreset::Nord,            ThemePreset::Dracula,            ThemePreset::Catppuccin,            ThemePreset::Solarized,        ]    }     pub fn next(&self) -> ThemePreset {        let all = Self::all();        let idx = all.iter().position(|t| t == self).unwrap_or(0);        all[(idx + 1) % all.len()]    }     pub fn prev(&self) -> ThemePreset {        let all = Self::all();        let idx = all.iter().position(|t| t == self).unwrap_or(0);        all[(idx + all.len() - 1) % all.len()]    }} /// Theme color configuration#[derive(Debug, Clone)]pub struct ThemeColors {    // Primary accent colors    pub primary: Color,    pub primary_bright: Color,    pub primary_dim: Color,     // Secondary colors    pub secondary: Color,    pub tertiary: Color,     // Semantic colors    pub success: Color,    pub warning: Color,    pub error: Color,    pub info: Color,     // Text colors    pub text: Color,    pub text_dim: Color,    pub text_muted: Color,     // Background colors    pub bg: Color,    pub bg_secondary: Color,    pub bg_highlight: Color,     // Border colors    pub border: Color,    pub border_active: Color,} impl ThemeColors {    /// Get colors for a theme preset    pub fn from_preset(preset: ThemePreset) -> Self {        match preset {            ThemePreset::AzulDark => Self::azul_dark(),            ThemePreset::Monokai => Self::monokai(),            ThemePreset::Gruvbox => Self::gruvbox(),            ThemePreset::Nord => Self::nord(),            ThemePreset::Dracula => Self::dracula(),            ThemePreset::Catppuccin => Self::catppuccin(),            ThemePreset::Solarized => Self::solarized(),        }    }     /// Azul Dark - Tokyo Night inspired (default)    fn azul_dark() -> Self {        Self {            primary: Color::Rgb(0, 191, 255),      // Deep sky blue            primary_bright: Color::Rgb(125, 207, 255),            primary_dim: Color::Rgb(0, 140, 200),             secondary: Color::Rgb(187, 154, 247),  // Purple            tertiary: Color::Rgb(255, 169, 0),     // Orange             success: Color::Rgb(158, 206, 106),    // Green            warning: Color::Rgb(255, 169, 0),      // Orange            error: Color::Rgb(247, 118, 142),      // Red            info: Color::Rgb(122, 162, 247),       // Blue             text: Color::Rgb(192, 202, 245),            text_dim: Color::Rgb(165, 175, 215),            text_muted: Color::Rgb(125, 137, 168),             bg: Color::Rgb(26, 27, 38),            bg_secondary: Color::Rgb(36, 40, 59),            bg_highlight: Color::Rgb(41, 46, 66),             border: Color::Rgb(122, 162, 247),            border_active: Color::Rgb(0, 191, 255),        }    }     /// Monokai theme    fn monokai() -> Self {        Self {            primary: Color::Rgb(102, 217, 239),    // Cyan            primary_bright: Color::Rgb(166, 226, 46),            primary_dim: Color::Rgb(82, 190, 200),             secondary: Color::Rgb(174, 129, 255),  // Purple            tertiary: Color::Rgb(253, 151, 31),    // Orange             success: Color::Rgb(166, 226, 46),     // Green            warning: Color::Rgb(253, 151, 31),     // Orange            error: Color::Rgb(249, 38, 114),       // Pink/Red            info: Color::Rgb(102, 217, 239),       // Cyan             text: Color::Rgb(248, 248, 242),            text_dim: Color::Rgb(200, 200, 195),            text_muted: Color::Rgb(117, 113, 94),             bg: Color::Rgb(39, 40, 34),            bg_secondary: Color::Rgb(49, 50, 44),            bg_highlight: Color::Rgb(59, 60, 54),             border: Color::Rgb(102, 217, 239),            border_active: Color::Rgb(166, 226, 46),        }    }     /// Gruvbox Dark theme    fn gruvbox() -> Self {        Self {            primary: Color::Rgb(215, 153, 33),     // Yellow            primary_bright: Color::Rgb(250, 189, 47),            primary_dim: Color::Rgb(181, 118, 20),             secondary: Color::Rgb(211, 134, 155),  // Purple            tertiary: Color::Rgb(254, 128, 25),    // Orange             success: Color::Rgb(152, 151, 26),     // Green            warning: Color::Rgb(254, 128, 25),     // Orange            error: Color::Rgb(204, 36, 29),        // Red            info: Color::Rgb(69, 133, 136),        // Aqua             text: Color::Rgb(235, 219, 178),            text_dim: Color::Rgb(213, 196, 161),            text_muted: Color::Rgb(168, 153, 132),             bg: Color::Rgb(40, 40, 40),            bg_secondary: Color::Rgb(50, 48, 47),            bg_highlight: Color::Rgb(60, 56, 54),             border: Color::Rgb(215, 153, 33),            border_active: Color::Rgb(250, 189, 47),        }    }     /// Nord theme    fn nord() -> Self {        Self {            primary: Color::Rgb(136, 192, 208),    // Frost cyan            primary_bright: Color::Rgb(143, 188, 187),            primary_dim: Color::Rgb(94, 129, 172),             secondary: Color::Rgb(180, 142, 173),  // Purple            tertiary: Color::Rgb(208, 135, 112),   // Orange             success: Color::Rgb(163, 190, 140),    // Green            warning: Color::Rgb(235, 203, 139),    // Yellow            error: Color::Rgb(191, 97, 106),       // Red            info: Color::Rgb(129, 161, 193),       // Blue             text: Color::Rgb(236, 239, 244),            text_dim: Color::Rgb(216, 222, 233),            text_muted: Color::Rgb(76, 86, 106),             bg: Color::Rgb(46, 52, 64),            bg_secondary: Color::Rgb(59, 66, 82),            bg_highlight: Color::Rgb(67, 76, 94),             border: Color::Rgb(136, 192, 208),            border_active: Color::Rgb(143, 188, 187),        }    }     /// Dracula theme    fn dracula() -> Self {        Self {            primary: Color::Rgb(189, 147, 249),    // Purple            primary_bright: Color::Rgb(255, 121, 198),            primary_dim: Color::Rgb(139, 233, 253),             secondary: Color::Rgb(255, 121, 198),  // Pink            tertiary: Color::Rgb(255, 184, 108),   // Orange             success: Color::Rgb(80, 250, 123),     // Green            warning: Color::Rgb(241, 250, 140),    // Yellow            error: Color::Rgb(255, 85, 85),        // Red            info: Color::Rgb(139, 233, 253),       // Cyan             text: Color::Rgb(248, 248, 242),            text_dim: Color::Rgb(226, 226, 220),            text_muted: Color::Rgb(98, 114, 164),             bg: Color::Rgb(40, 42, 54),            bg_secondary: Color::Rgb(68, 71, 90),            bg_highlight: Color::Rgb(55, 58, 76),             border: Color::Rgb(189, 147, 249),            border_active: Color::Rgb(255, 121, 198),        }    }     /// Catppuccin Mocha theme    fn catppuccin() -> Self {        Self {            primary: Color::Rgb(137, 180, 250),    // Blue            primary_bright: Color::Rgb(148, 226, 213),            primary_dim: Color::Rgb(116, 199, 236),             secondary: Color::Rgb(203, 166, 247),  // Mauve            tertiary: Color::Rgb(250, 179, 135),   // Peach             success: Color::Rgb(166, 227, 161),    // Green            warning: Color::Rgb(249, 226, 175),    // Yellow            error: Color::Rgb(243, 139, 168),      // Red            info: Color::Rgb(137, 220, 235),       // Sky             text: Color::Rgb(205, 214, 244),            text_dim: Color::Rgb(186, 194, 222),            text_muted: Color::Rgb(108, 112, 134),             bg: Color::Rgb(30, 30, 46),            bg_secondary: Color::Rgb(49, 50, 68),            bg_highlight: Color::Rgb(69, 71, 90),             border: Color::Rgb(137, 180, 250),            border_active: Color::Rgb(203, 166, 247),        }    }     /// Solarized Dark theme    fn solarized() -> Self {        Self {            primary: Color::Rgb(38, 139, 210),     // Blue            primary_bright: Color::Rgb(42, 161, 152),            primary_dim: Color::Rgb(108, 113, 196),             secondary: Color::Rgb(211, 54, 130),   // Magenta            tertiary: Color::Rgb(203, 75, 22),     // Orange             success: Color::Rgb(133, 153, 0),      // Green            warning: Color::Rgb(181, 137, 0),      // Yellow            error: Color::Rgb(220, 50, 47),        // Red            info: Color::Rgb(42, 161, 152),        // Cyan             text: Color::Rgb(131, 148, 150),            text_dim: Color::Rgb(147, 161, 161),            text_muted: Color::Rgb(88, 110, 117),             bg: Color::Rgb(0, 43, 54),            bg_secondary: Color::Rgb(7, 54, 66),            bg_highlight: Color::Rgb(0, 57, 71),             border: Color::Rgb(38, 139, 210),            border_active: Color::Rgb(42, 161, 152),        }    }} /// Theme manager for runtime theme switchingpub struct ThemeManager {    current_preset: ThemePreset,    colors: ThemeColors,} impl ThemeManager {    pub fn new() -> Self {        let preset = ThemePreset::AzulDark;        Self {            current_preset: preset,            colors: ThemeColors::from_preset(preset),        }    }     pub fn with_preset(preset: ThemePreset) -> Self {        Self {            current_preset: preset,            colors: ThemeColors::from_preset(preset),        }    }     /// Get current theme preset    pub fn preset(&self) -> ThemePreset {        self.current_preset    }     /// Get current theme colors    pub fn colors(&self) -> &ThemeColors {        &self.colors    }     /// Cycle to next theme, returns the new theme name    pub fn cycle_next(&mut self) -> &'static str {        self.current_preset = self.current_preset.next();        self.colors = ThemeColors::from_preset(self.current_preset);        self.current_preset.name()    }     /// Cycle to previous theme, returns the new theme name    pub fn cycle_prev(&mut self) -> &'static str {        self.current_preset = self.current_preset.prev();        self.colors = ThemeColors::from_preset(self.current_preset);        self.current_preset.name()    }     /// Set a specific theme    pub fn set_theme(&mut self, preset: ThemePreset) {        self.current_preset = preset;        self.colors = ThemeColors::from_preset(preset);    }} impl Default for ThemeManager {    fn default() -> Self {        Self::new()    }} #[cfg(test)]mod tests {    use super::*;     #[test]    fn test_theme_cycling() {        let mut tm = ThemeManager::new();        assert_eq!(tm.preset(), ThemePreset::AzulDark);         tm.cycle_next();        assert_eq!(tm.preset(), ThemePreset::Monokai);         tm.cycle_prev();        assert_eq!(tm.preset(), ThemePreset::AzulDark);    }     #[test]    fn test_all_themes_have_colors() {        for preset in ThemePreset::all() {            let colors = ThemeColors::from_preset(*preset);            // Just ensure we can access all color fields            let _ = colors.primary;            let _ = colors.text;            let _ = colors.bg;        }    }}