use ratatui::style::{Color, Style};
use std::env;
use syntect::highlighting::{Theme, ThemeSet};
#[derive(Debug, Clone)]
pub struct ThemeManager {
pub background: BackgroundType,
pub syntax_theme: String,
pub palette: ColorPalette,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BackgroundType {
Dark,
Light,
}
#[derive(Debug, Clone)]
pub struct ColorPalette {
pub success: Color,
pub warning: Color,
pub error: Color,
pub info: Color,
pub accent: Color,
pub highlight: Color,
pub muted: Color,
pub background: Color,
pub foreground: Color,
pub badge_active: Color,
pub badge_inactive: Color,
}
impl ThemeManager {
pub fn detect() -> Self {
let background = Self::detect_background();
let syntax_theme = Self::get_syntax_theme(&background);
let palette = ColorPalette::for_background(&background);
Self {
background,
syntax_theme,
palette,
}
}
fn detect_background() -> BackgroundType {
if let Ok(colorfgbg) = env::var("COLORFGBG") {
if let Some(bg) = colorfgbg.split(';').nth(1) {
if let Ok(bg_val) = bg.parse::<u8>() {
return if bg_val < 8 {
BackgroundType::Dark
} else {
BackgroundType::Light
};
}
}
}
BackgroundType::Dark
}
fn get_syntax_theme(background: &BackgroundType) -> String {
match background {
BackgroundType::Dark => "Monokai Extended".to_string(),
BackgroundType::Light => "InspiredGitHub".to_string(),
}
}
pub fn load_syntect_theme(&self) -> Theme {
let theme_set = ThemeSet::load_defaults();
if let Some(theme) = theme_set.themes.get(&self.syntax_theme) {
theme.clone()
} else {
match self.background {
BackgroundType::Dark => {
theme_set.themes.get("base16-ocean.dark")
.or_else(|| theme_set.themes.values().next())
.cloned()
.expect("No themes available")
}
BackgroundType::Light => {
theme_set.themes.get("base16-ocean.light")
.or_else(|| theme_set.themes.values().next())
.cloned()
.expect("No themes available")
}
}
}
}
}
impl ColorPalette {
pub fn for_background(bg: &BackgroundType) -> Self {
match bg {
BackgroundType::Dark => Self {
success: Color::Green,
warning: Color::Yellow,
error: Color::Red,
info: Color::Cyan,
accent: Color::Magenta,
highlight: Color::LightBlue,
muted: Color::DarkGray,
background: Color::Black,
foreground: Color::White,
badge_active: Color::Cyan,
badge_inactive: Color::DarkGray,
},
BackgroundType::Light => Self {
success: Color::Green,
warning: Color::Yellow,
error: Color::Red,
info: Color::Blue,
accent: Color::Magenta,
highlight: Color::Blue,
muted: Color::Gray,
background: Color::White,
foreground: Color::Black,
badge_active: Color::Blue,
badge_inactive: Color::Gray,
},
}
}
pub fn success_style(&self) -> Style {
Style::default().fg(self.success)
}
pub fn warning_style(&self) -> Style {
Style::default().fg(self.warning)
}
pub fn error_style(&self) -> Style {
Style::default().fg(self.error)
}
pub fn highlight_style(&self) -> Style {
Style::default().fg(self.highlight)
}
pub fn muted_style(&self) -> Style {
Style::default().fg(self.muted)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_theme_detection() {
let theme = ThemeManager::detect();
assert!(!theme.syntax_theme.is_empty());
}
#[test]
fn test_palette_creation() {
let palette = ColorPalette::for_background(&BackgroundType::Dark);
assert_eq!(palette.success, Color::Green);
let palette = ColorPalette::for_background(&BackgroundType::Light);
assert_eq!(palette.success, Color::Green);
}
}