use ratatui::style::{Color, Modifier, Style};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThemeVariant {
EverforestDark,
EverforestLight,
}
impl Default for ThemeVariant {
fn default() -> Self {
Self::EverforestDark
}
}
#[derive(Debug, Clone)]
pub struct ColorPalette {
pub background: Color,
pub foreground: Color,
pub accent: Color,
pub secondary: Color,
pub info: Color,
pub border: Color,
pub selection: Color,
pub cursor: Color,
pub warning: Color, }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Element {
Text,
Title,
Border,
Highlight,
Accent,
Secondary,
Info,
Background,
Active,
Inactive,
Warning,
}
#[derive(Debug, Clone)]
pub struct Theme {
variant: ThemeVariant,
colors: ColorPalette,
}
impl Default for Theme {
fn default() -> Self {
Self::new(ThemeVariant::default())
}
}
impl Theme {
pub fn new(variant: ThemeVariant) -> Self {
let use_basic_colors = std::env::var("TERM_PROGRAM")
.map(|term| term == "Apple_Terminal")
.unwrap_or(false);
let colors = match (variant, use_basic_colors) {
(ThemeVariant::EverforestDark, false) => ColorPalette {
background: Color::Rgb(45, 53, 59), foreground: Color::Rgb(211, 198, 170), accent: Color::Rgb(167, 192, 128), secondary: Color::Rgb(230, 126, 128), info: Color::Rgb(127, 187, 179), border: Color::Rgb(130, 140, 150), selection: Color::Rgb(64, 72, 78), cursor: Color::Rgb(211, 198, 170), warning: Color::Rgb(219, 188, 127), },
(ThemeVariant::EverforestLight, false) => ColorPalette {
background: Color::Rgb(253, 246, 227), foreground: Color::Rgb(76, 86, 94), accent: Color::Rgb(141, 161, 1), secondary: Color::Rgb(248, 85, 82), info: Color::Rgb(53, 167, 124), border: Color::Rgb(150, 160, 170), selection: Color::Rgb(243, 236, 217), cursor: Color::Rgb(76, 86, 94), warning: Color::Rgb(207, 131, 44), },
(ThemeVariant::EverforestDark, true) => ColorPalette {
background: Color::Reset, foreground: Color::White,
accent: Color::Green,
secondary: Color::Red,
info: Color::Cyan,
border: Color::Gray, selection: Color::Blue, cursor: Color::White,
warning: Color::Yellow,
},
(ThemeVariant::EverforestLight, true) => ColorPalette {
background: Color::Reset, foreground: Color::Black,
accent: Color::Green,
secondary: Color::Red,
info: Color::Blue, border: Color::DarkGray, selection: Color::Yellow, cursor: Color::Black,
warning: Color::Magenta, },
};
Self { variant, colors }
}
pub fn variant(&self) -> ThemeVariant {
self.variant
}
pub fn colors(&self) -> &ColorPalette {
&self.colors
}
pub fn toggle(&mut self) {
self.variant = match self.variant {
ThemeVariant::EverforestDark => ThemeVariant::EverforestLight,
ThemeVariant::EverforestLight => ThemeVariant::EverforestDark,
};
*self = Self::new(self.variant);
}
pub fn set_variant(&mut self, variant: ThemeVariant) {
if self.variant != variant {
self.variant = variant;
*self = Self::new(self.variant);
}
}
pub fn ratatui_style(&self, element: Element) -> Style {
match element {
Element::Text => Style::default()
.fg(self.colors.foreground)
.bg(self.colors.background),
Element::Title => Style::default()
.fg(self.colors.accent)
.bg(self.colors.background)
.add_modifier(Modifier::BOLD),
Element::Border => Style::default()
.fg(self.colors.border)
.bg(self.colors.background),
Element::Highlight => Style::default()
.fg(self.colors.foreground)
.bg(self.colors.selection)
.add_modifier(Modifier::BOLD),
Element::Accent => Style::default()
.fg(self.colors.accent)
.bg(self.colors.background)
.add_modifier(Modifier::BOLD),
Element::Secondary => Style::default()
.fg(self.colors.secondary)
.bg(self.colors.background),
Element::Info => Style::default()
.fg(self.colors.info)
.bg(self.colors.background),
Element::Background => Style::default()
.fg(self.colors.foreground)
.bg(self.colors.background),
Element::Active => Style::default()
.fg(self.colors.accent)
.bg(self.colors.selection)
.add_modifier(Modifier::BOLD),
Element::Inactive => Style::default()
.fg(self.colors.border)
.bg(self.colors.background),
Element::Warning => Style::default()
.fg(self.colors.warning)
.bg(self.colors.background),
}
}
pub fn fg_color(&self, element: Element) -> Color {
match element {
Element::Text | Element::Background => self.colors.foreground,
Element::Title | Element::Accent | Element::Active => self.colors.accent,
Element::Border | Element::Inactive => self.colors.border,
Element::Highlight => self.colors.foreground,
Element::Secondary => self.colors.secondary,
Element::Info => self.colors.info,
Element::Warning => self.colors.warning,
}
}
pub fn bg_color(&self, element: Element) -> Color {
match element {
Element::Highlight | Element::Active => self.colors.selection,
_ => self.colors.background,
}
}
pub fn title_style(&self) -> Style {
self.ratatui_style(Element::Title)
}
pub fn border_style(&self) -> Style {
self.ratatui_style(Element::Border)
}
pub fn text_style(&self) -> Style {
self.ratatui_style(Element::Text)
}
pub fn highlight_style(&self) -> Style {
self.ratatui_style(Element::Highlight)
}
pub fn accent_style(&self) -> Style {
self.ratatui_style(Element::Accent)
}
pub fn secondary_style(&self) -> Style {
self.ratatui_style(Element::Secondary)
}
pub fn info_style(&self) -> Style {
self.ratatui_style(Element::Info)
}
pub fn warning_style(&self) -> Style {
self.ratatui_style(Element::Warning)
}
}