use ratatui::style::Color;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum ThemeName {
#[default]
Dark,
Light,
Ansi,
Dracula,
Nord,
Catppuccin,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Theme {
pub fg: Color,
pub bg: Color,
pub dim: Color,
pub bold: Color,
pub success: Color,
pub error: Color,
pub warning: Color,
pub info: Color,
pub assistant: Color,
pub assistant_bold: Color,
pub user: Color,
pub user_bold: Color,
pub user_message_bg: Color, pub user_message_fg: Color,
pub tool_name: Color,
pub tool_summary: Color,
pub tool_success: Color,
pub tool_error: Color,
pub diff_added: Color,
pub diff_removed: Color,
pub diff_added_dim: Color,
pub diff_removed_dim: Color,
pub status_bg: Color,
pub status_fg: Color,
pub thinking: Color,
pub border: Color,
pub border_bold: Color,
pub divider: Color,
}
impl Theme {
pub fn dark() -> Self {
Self {
fg: Color::Rgb(213, 196, 161), bg: Color::Rgb(40, 40, 40), dim: Color::Rgb(146, 131, 116), bold: Color::Rgb(250, 233, 213),
success: Color::Rgb(184, 187, 38), error: Color::Rgb(251, 73, 52), warning: Color::Rgb(250, 189, 47), info: Color::Rgb(131, 165, 152),
assistant: Color::Rgb(131, 165, 152), assistant_bold: Color::Rgb(211, 134, 155), user: Color::Rgb(184, 187, 38), user_bold: Color::Rgb(250, 233, 213), user_message_bg: Color::Rgb(60, 60, 60), user_message_fg: Color::Rgb(213, 196, 161),
tool_name: Color::Rgb(250, 189, 47), tool_summary: Color::Rgb(146, 131, 116), tool_success: Color::Rgb(184, 187, 38), tool_error: Color::Rgb(251, 73, 52),
diff_added: Color::Rgb(152, 195, 121), diff_removed: Color::Rgb(224, 108, 117), diff_added_dim: Color::Rgb(99, 166, 71), diff_removed_dim: Color::Rgb(184, 68, 77),
status_bg: Color::Rgb(60, 60, 60),
status_fg: Color::Rgb(213, 196, 161),
thinking: Color::Rgb(184, 187, 38),
border: Color::Rgb(100, 100, 100),
border_bold: Color::Rgb(180, 180, 180),
divider: Color::Rgb(80, 80, 80),
}
}
pub fn light() -> Self {
Self {
fg: Color::Rgb(60, 60, 60), bg: Color::Rgb(250, 250, 250), dim: Color::Rgb(140, 140, 140), bold: Color::Rgb(30, 30, 30),
success: Color::Rgb(84, 128, 0), error: Color::Rgb(200, 60, 60), warning: Color::Rgb(200, 150, 0), info: Color::Rgb(40, 80, 180),
assistant: Color::Rgb(80, 120, 180), assistant_bold: Color::Rgb(140, 80, 160), user: Color::Rgb(84, 128, 0), user_bold: Color::Rgb(30, 30, 30), user_message_bg: Color::Rgb(220, 220, 220), user_message_fg: Color::Rgb(60, 60, 60),
tool_name: Color::Rgb(200, 150, 0), tool_summary: Color::Rgb(140, 140, 140), tool_success: Color::Rgb(84, 128, 0), tool_error: Color::Rgb(200, 60, 60),
diff_added: Color::Rgb(120, 180, 60),
diff_removed: Color::Rgb(200, 100, 100),
diff_added_dim: Color::Rgb(180, 220, 150),
diff_removed_dim: Color::Rgb(240, 200, 200),
status_bg: Color::Rgb(220, 220, 220),
status_fg: Color::Rgb(60, 60, 60),
thinking: Color::Rgb(84, 128, 0),
border: Color::Rgb(180, 180, 180),
border_bold: Color::Rgb(100, 100, 100),
divider: Color::Rgb(200, 200, 200),
}
}
pub fn ansi() -> Self {
Self {
fg: Color::White,
bg: Color::Black,
dim: Color::DarkGray,
bold: Color::White,
success: Color::Green,
error: Color::Red,
warning: Color::Yellow,
info: Color::Blue,
assistant: Color::Blue,
assistant_bold: Color::Magenta,
user: Color::Green,
user_bold: Color::White,
user_message_bg: Color::DarkGray, user_message_fg: Color::White,
tool_name: Color::Yellow,
tool_summary: Color::DarkGray,
tool_success: Color::Green,
tool_error: Color::Red,
diff_added: Color::Green,
diff_removed: Color::Red,
diff_added_dim: Color::Rgb(99, 166, 71), diff_removed_dim: Color::Rgb(184, 68, 77),
status_bg: Color::DarkGray,
status_fg: Color::White,
thinking: Color::Green,
border: Color::White,
border_bold: Color::White,
divider: Color::DarkGray,
}
}
pub fn dracula() -> Self {
Self {
fg: Color::Rgb(248, 248, 242), bg: Color::Rgb(40, 42, 54), dim: Color::Rgb(98, 114, 164), bold: Color::Rgb(255, 255, 255),
success: Color::Rgb(139, 233, 253), error: Color::Rgb(255, 85, 85), warning: Color::Rgb(255, 184, 108), info: Color::Rgb(139, 233, 253),
assistant: Color::Rgb(189, 147, 249), assistant_bold: Color::Rgb(255, 121, 198), user: Color::Rgb(80, 250, 123), user_bold: Color::Rgb(248, 248, 242),
user_message_bg: Color::Rgb(68, 71, 90), user_message_fg: Color::Rgb(248, 248, 242),
tool_name: Color::Rgb(255, 184, 108), tool_summary: Color::Rgb(98, 114, 164), tool_success: Color::Rgb(80, 250, 123), tool_error: Color::Rgb(255, 85, 85),
diff_added: Color::Rgb(139, 233, 253),
diff_removed: Color::Rgb(255, 184, 108),
diff_added_dim: Color::Rgb(60, 180, 200),
diff_removed_dim: Color::Rgb(220, 150, 80),
status_bg: Color::Rgb(68, 71, 90),
status_fg: Color::Rgb(248, 248, 242),
thinking: Color::Rgb(80, 250, 123),
border: Color::Rgb(98, 114, 164),
border_bold: Color::Rgb(189, 147, 249),
divider: Color::Rgb(68, 71, 90),
}
}
pub fn nord() -> Self {
Self {
fg: Color::Rgb(236, 239, 244), bg: Color::Rgb(46, 52, 64), dim: Color::Rgb(94, 120, 156), bold: Color::Rgb(248, 250, 252),
success: Color::Rgb(163, 190, 140), error: Color::Rgb(191, 97, 106), warning: Color::Rgb(235, 203, 139), info: Color::Rgb(88, 166, 224),
assistant: Color::Rgb(143, 188, 187), assistant_bold: Color::Rgb(180, 142, 173), user: Color::Rgb(163, 190, 140), user_bold: Color::Rgb(236, 239, 244),
user_message_bg: Color::Rgb(59, 66, 82), user_message_fg: Color::Rgb(236, 239, 244),
tool_name: Color::Rgb(235, 203, 139), tool_summary: Color::Rgb(94, 120, 156), tool_success: Color::Rgb(163, 190, 140), tool_error: Color::Rgb(191, 97, 106),
diff_added: Color::Rgb(163, 190, 140),
diff_removed: Color::Rgb(191, 97, 106),
diff_added_dim: Color::Rgb(130, 160, 110),
diff_removed_dim: Color::Rgb(160, 70, 80),
status_bg: Color::Rgb(59, 66, 82),
status_fg: Color::Rgb(236, 239, 244),
thinking: Color::Rgb(163, 190, 140),
border: Color::Rgb(94, 120, 156),
border_bold: Color::Rgb(143, 188, 187),
divider: Color::Rgb(59, 66, 82),
}
}
pub fn catppuccin() -> Self {
Self {
fg: Color::Rgb(205, 214, 244), bg: Color::Rgb(30, 34, 44), dim: Color::Rgb(148, 157, 180), bold: Color::Rgb(249, 250, 255),
success: Color::Rgb(166, 227, 161), error: Color::Rgb(243, 139, 168), warning: Color::Rgb(250, 179, 135), info: Color::Rgb(137, 180, 250),
assistant: Color::Rgb(180, 194, 255), assistant_bold: Color::Rgb(231, 130, 198), user: Color::Rgb(166, 227, 161), user_bold: Color::Rgb(205, 214, 244),
user_message_bg: Color::Rgb(41, 45, 60), user_message_fg: Color::Rgb(205, 214, 244),
tool_name: Color::Rgb(250, 179, 135), tool_summary: Color::Rgb(148, 157, 180), tool_success: Color::Rgb(166, 227, 161), tool_error: Color::Rgb(243, 139, 168),
diff_added: Color::Rgb(166, 227, 161),
diff_removed: Color::Rgb(243, 139, 168),
diff_added_dim: Color::Rgb(130, 190, 125),
diff_removed_dim: Color::Rgb(210, 100, 135),
status_bg: Color::Rgb(41, 45, 60),
status_fg: Color::Rgb(205, 214, 244),
thinking: Color::Rgb(166, 227, 161),
border: Color::Rgb(148, 157, 180),
border_bold: Color::Rgb(180, 194, 255),
divider: Color::Rgb(41, 45, 60),
}
}
pub fn from_name(name: ThemeName) -> Self {
match name {
ThemeName::Dark => Self::dark(),
ThemeName::Light => Self::light(),
ThemeName::Ansi => Self::ansi(),
ThemeName::Dracula => Self::dracula(),
ThemeName::Nord => Self::nord(),
ThemeName::Catppuccin => Self::catppuccin(),
}
}
}
pub fn default_theme() -> Theme {
Theme::dark()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dark_theme_colors() {
let theme = Theme::dark();
assert_eq!(theme.fg, Color::Rgb(213, 196, 161));
assert_eq!(theme.bg, Color::Rgb(40, 40, 40));
assert_eq!(theme.success, Color::Rgb(184, 187, 38));
}
#[test]
fn test_light_theme_colors() {
let theme = Theme::light();
assert_eq!(theme.fg, Color::Rgb(60, 60, 60));
assert_eq!(theme.bg, Color::Rgb(250, 250, 250));
assert_eq!(theme.success, Color::Rgb(84, 128, 0));
}
#[test]
fn test_theme_from_name() {
assert_eq!(Theme::from_name(ThemeName::Dark), Theme::dark());
assert_eq!(Theme::from_name(ThemeName::Light), Theme::light());
assert_eq!(Theme::from_name(ThemeName::Ansi), Theme::ansi());
}
}