use ratatui::style::Color;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::Path;
pub fn color_to_rgb(color: Color) -> Option<(u8, u8, u8)> {
match color {
Color::Rgb(r, g, b) => Some((r, g, b)),
Color::White => Some((255, 255, 255)),
Color::Black => Some((0, 0, 0)),
Color::Red => Some((205, 0, 0)),
Color::Green => Some((0, 205, 0)),
Color::Blue => Some((0, 0, 238)),
Color::Yellow => Some((205, 205, 0)),
Color::Magenta => Some((205, 0, 205)),
Color::Cyan => Some((0, 205, 205)),
Color::Gray => Some((229, 229, 229)),
Color::DarkGray => Some((127, 127, 127)),
Color::LightRed => Some((255, 0, 0)),
Color::LightGreen => Some((0, 255, 0)),
Color::LightBlue => Some((92, 92, 255)),
Color::LightYellow => Some((255, 255, 0)),
Color::LightMagenta => Some((255, 0, 255)),
Color::LightCyan => Some((0, 255, 255)),
Color::Reset | Color::Indexed(_) => None,
}
}
fn brighten_color(color: Color, amount: u8) -> Color {
if let Some((r, g, b)) = color_to_rgb(color) {
Color::Rgb(
r.saturating_add(amount),
g.saturating_add(amount),
b.saturating_add(amount),
)
} else {
color
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum ColorDef {
Rgb(u8, u8, u8),
Named(String),
}
impl From<ColorDef> for Color {
fn from(def: ColorDef) -> Self {
match def {
ColorDef::Rgb(r, g, b) => Color::Rgb(r, g, b),
ColorDef::Named(name) => match name.as_str() {
"Black" => Color::Black,
"Red" => Color::Red,
"Green" => Color::Green,
"Yellow" => Color::Yellow,
"Blue" => Color::Blue,
"Magenta" => Color::Magenta,
"Cyan" => Color::Cyan,
"Gray" => Color::Gray,
"DarkGray" => Color::DarkGray,
"LightRed" => Color::LightRed,
"LightGreen" => Color::LightGreen,
"LightYellow" => Color::LightYellow,
"LightBlue" => Color::LightBlue,
"LightMagenta" => Color::LightMagenta,
"LightCyan" => Color::LightCyan,
"White" => Color::White,
"Default" | "Reset" => Color::Reset,
_ => Color::White, },
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ThemeFile {
pub name: String,
pub editor: EditorColors,
pub ui: UiColors,
pub search: SearchColors,
pub diagnostic: DiagnosticColors,
pub syntax: SyntaxColors,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct EditorColors {
#[serde(default = "default_editor_bg")]
pub bg: ColorDef,
#[serde(default = "default_editor_fg")]
pub fg: ColorDef,
#[serde(default = "default_cursor")]
pub cursor: ColorDef,
#[serde(default = "default_inactive_cursor")]
pub inactive_cursor: ColorDef,
#[serde(default = "default_selection_bg")]
pub selection_bg: ColorDef,
#[serde(default = "default_current_line_bg")]
pub current_line_bg: ColorDef,
#[serde(default = "default_line_number_fg")]
pub line_number_fg: ColorDef,
#[serde(default = "default_line_number_bg")]
pub line_number_bg: ColorDef,
#[serde(default = "default_diff_add_bg")]
pub diff_add_bg: ColorDef,
#[serde(default = "default_diff_remove_bg")]
pub diff_remove_bg: ColorDef,
#[serde(default = "default_diff_modify_bg")]
pub diff_modify_bg: ColorDef,
}
fn default_editor_bg() -> ColorDef {
ColorDef::Rgb(30, 30, 30)
}
fn default_editor_fg() -> ColorDef {
ColorDef::Rgb(212, 212, 212)
}
fn default_cursor() -> ColorDef {
ColorDef::Rgb(255, 255, 255)
}
fn default_inactive_cursor() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_selection_bg() -> ColorDef {
ColorDef::Rgb(38, 79, 120)
}
fn default_current_line_bg() -> ColorDef {
ColorDef::Rgb(40, 40, 40)
}
fn default_line_number_fg() -> ColorDef {
ColorDef::Rgb(100, 100, 100)
}
fn default_line_number_bg() -> ColorDef {
ColorDef::Rgb(30, 30, 30)
}
fn default_diff_add_bg() -> ColorDef {
ColorDef::Rgb(35, 60, 35) }
fn default_diff_remove_bg() -> ColorDef {
ColorDef::Rgb(70, 35, 35) }
fn default_diff_modify_bg() -> ColorDef {
ColorDef::Rgb(40, 38, 30) }
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct UiColors {
#[serde(default = "default_tab_active_fg")]
pub tab_active_fg: ColorDef,
#[serde(default = "default_tab_active_bg")]
pub tab_active_bg: ColorDef,
#[serde(default = "default_tab_inactive_fg")]
pub tab_inactive_fg: ColorDef,
#[serde(default = "default_tab_inactive_bg")]
pub tab_inactive_bg: ColorDef,
#[serde(default = "default_tab_separator_bg")]
pub tab_separator_bg: ColorDef,
#[serde(default = "default_tab_close_hover_fg")]
pub tab_close_hover_fg: ColorDef,
#[serde(default = "default_tab_hover_bg")]
pub tab_hover_bg: ColorDef,
#[serde(default = "default_menu_bg")]
pub menu_bg: ColorDef,
#[serde(default = "default_menu_fg")]
pub menu_fg: ColorDef,
#[serde(default = "default_menu_active_bg")]
pub menu_active_bg: ColorDef,
#[serde(default = "default_menu_active_fg")]
pub menu_active_fg: ColorDef,
#[serde(default = "default_menu_dropdown_bg")]
pub menu_dropdown_bg: ColorDef,
#[serde(default = "default_menu_dropdown_fg")]
pub menu_dropdown_fg: ColorDef,
#[serde(default = "default_menu_highlight_bg")]
pub menu_highlight_bg: ColorDef,
#[serde(default = "default_menu_highlight_fg")]
pub menu_highlight_fg: ColorDef,
#[serde(default = "default_menu_border_fg")]
pub menu_border_fg: ColorDef,
#[serde(default = "default_menu_separator_fg")]
pub menu_separator_fg: ColorDef,
#[serde(default = "default_menu_hover_bg")]
pub menu_hover_bg: ColorDef,
#[serde(default = "default_menu_hover_fg")]
pub menu_hover_fg: ColorDef,
#[serde(default = "default_menu_disabled_fg")]
pub menu_disabled_fg: ColorDef,
#[serde(default = "default_menu_disabled_bg")]
pub menu_disabled_bg: ColorDef,
#[serde(default = "default_status_bar_fg")]
pub status_bar_fg: ColorDef,
#[serde(default = "default_status_bar_bg")]
pub status_bar_bg: ColorDef,
#[serde(default = "default_prompt_fg")]
pub prompt_fg: ColorDef,
#[serde(default = "default_prompt_bg")]
pub prompt_bg: ColorDef,
#[serde(default = "default_prompt_selection_fg")]
pub prompt_selection_fg: ColorDef,
#[serde(default = "default_prompt_selection_bg")]
pub prompt_selection_bg: ColorDef,
#[serde(default = "default_popup_border_fg")]
pub popup_border_fg: ColorDef,
#[serde(default = "default_popup_bg")]
pub popup_bg: ColorDef,
#[serde(default = "default_popup_selection_bg")]
pub popup_selection_bg: ColorDef,
#[serde(default = "default_popup_text_fg")]
pub popup_text_fg: ColorDef,
#[serde(default = "default_suggestion_bg")]
pub suggestion_bg: ColorDef,
#[serde(default = "default_suggestion_selected_bg")]
pub suggestion_selected_bg: ColorDef,
#[serde(default = "default_help_bg")]
pub help_bg: ColorDef,
#[serde(default = "default_help_fg")]
pub help_fg: ColorDef,
#[serde(default = "default_help_key_fg")]
pub help_key_fg: ColorDef,
#[serde(default = "default_help_separator_fg")]
pub help_separator_fg: ColorDef,
#[serde(default = "default_help_indicator_fg")]
pub help_indicator_fg: ColorDef,
#[serde(default = "default_help_indicator_bg")]
pub help_indicator_bg: ColorDef,
#[serde(default = "default_inline_code_bg")]
pub inline_code_bg: ColorDef,
#[serde(default = "default_split_separator_fg")]
pub split_separator_fg: ColorDef,
#[serde(default = "default_split_separator_hover_fg")]
pub split_separator_hover_fg: ColorDef,
#[serde(default = "default_scrollbar_track_fg")]
pub scrollbar_track_fg: ColorDef,
#[serde(default = "default_scrollbar_thumb_fg")]
pub scrollbar_thumb_fg: ColorDef,
#[serde(default = "default_scrollbar_track_hover_fg")]
pub scrollbar_track_hover_fg: ColorDef,
#[serde(default = "default_scrollbar_thumb_hover_fg")]
pub scrollbar_thumb_hover_fg: ColorDef,
#[serde(default = "default_compose_margin_bg")]
pub compose_margin_bg: ColorDef,
#[serde(default = "default_semantic_highlight_bg")]
pub semantic_highlight_bg: ColorDef,
#[serde(default = "default_terminal_bg")]
pub terminal_bg: ColorDef,
#[serde(default = "default_terminal_fg")]
pub terminal_fg: ColorDef,
#[serde(default = "default_status_warning_indicator_bg")]
pub status_warning_indicator_bg: ColorDef,
#[serde(default = "default_status_warning_indicator_fg")]
pub status_warning_indicator_fg: ColorDef,
#[serde(default = "default_status_error_indicator_bg")]
pub status_error_indicator_bg: ColorDef,
#[serde(default = "default_status_error_indicator_fg")]
pub status_error_indicator_fg: ColorDef,
#[serde(default = "default_status_warning_indicator_hover_bg")]
pub status_warning_indicator_hover_bg: ColorDef,
#[serde(default = "default_status_warning_indicator_hover_fg")]
pub status_warning_indicator_hover_fg: ColorDef,
#[serde(default = "default_status_error_indicator_hover_bg")]
pub status_error_indicator_hover_bg: ColorDef,
#[serde(default = "default_status_error_indicator_hover_fg")]
pub status_error_indicator_hover_fg: ColorDef,
#[serde(default = "default_tab_drop_zone_bg")]
pub tab_drop_zone_bg: ColorDef,
#[serde(default = "default_tab_drop_zone_border")]
pub tab_drop_zone_border: ColorDef,
}
fn default_tab_active_fg() -> ColorDef {
ColorDef::Named("Yellow".to_string())
}
fn default_tab_active_bg() -> ColorDef {
ColorDef::Named("Blue".to_string())
}
fn default_tab_inactive_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_tab_inactive_bg() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_tab_separator_bg() -> ColorDef {
ColorDef::Named("Black".to_string())
}
fn default_tab_close_hover_fg() -> ColorDef {
ColorDef::Rgb(255, 100, 100) }
fn default_tab_hover_bg() -> ColorDef {
ColorDef::Rgb(70, 70, 75) }
fn default_menu_bg() -> ColorDef {
ColorDef::Rgb(60, 60, 65)
}
fn default_menu_fg() -> ColorDef {
ColorDef::Rgb(220, 220, 220)
}
fn default_menu_active_bg() -> ColorDef {
ColorDef::Rgb(60, 60, 60)
}
fn default_menu_active_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255)
}
fn default_menu_dropdown_bg() -> ColorDef {
ColorDef::Rgb(50, 50, 50)
}
fn default_menu_dropdown_fg() -> ColorDef {
ColorDef::Rgb(220, 220, 220)
}
fn default_menu_highlight_bg() -> ColorDef {
ColorDef::Rgb(70, 130, 180)
}
fn default_menu_highlight_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255)
}
fn default_menu_border_fg() -> ColorDef {
ColorDef::Rgb(100, 100, 100)
}
fn default_menu_separator_fg() -> ColorDef {
ColorDef::Rgb(80, 80, 80)
}
fn default_menu_hover_bg() -> ColorDef {
ColorDef::Rgb(55, 55, 55)
}
fn default_menu_hover_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255)
}
fn default_menu_disabled_fg() -> ColorDef {
ColorDef::Rgb(100, 100, 100) }
fn default_menu_disabled_bg() -> ColorDef {
ColorDef::Rgb(50, 50, 50) }
fn default_status_bar_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_status_bar_bg() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_prompt_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_prompt_bg() -> ColorDef {
ColorDef::Named("Black".to_string())
}
fn default_prompt_selection_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_prompt_selection_bg() -> ColorDef {
ColorDef::Rgb(58, 79, 120)
}
fn default_popup_border_fg() -> ColorDef {
ColorDef::Named("Gray".to_string())
}
fn default_popup_bg() -> ColorDef {
ColorDef::Rgb(30, 30, 30)
}
fn default_popup_selection_bg() -> ColorDef {
ColorDef::Rgb(58, 79, 120)
}
fn default_popup_text_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_suggestion_bg() -> ColorDef {
ColorDef::Rgb(30, 30, 30)
}
fn default_suggestion_selected_bg() -> ColorDef {
ColorDef::Rgb(58, 79, 120)
}
fn default_help_bg() -> ColorDef {
ColorDef::Named("Black".to_string())
}
fn default_help_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_help_key_fg() -> ColorDef {
ColorDef::Named("Cyan".to_string())
}
fn default_help_separator_fg() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_help_indicator_fg() -> ColorDef {
ColorDef::Named("Red".to_string())
}
fn default_help_indicator_bg() -> ColorDef {
ColorDef::Named("Black".to_string())
}
fn default_inline_code_bg() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_split_separator_fg() -> ColorDef {
ColorDef::Rgb(100, 100, 100)
}
fn default_split_separator_hover_fg() -> ColorDef {
ColorDef::Rgb(100, 149, 237) }
fn default_scrollbar_track_fg() -> ColorDef {
ColorDef::Named("DarkGray".to_string())
}
fn default_scrollbar_thumb_fg() -> ColorDef {
ColorDef::Named("Gray".to_string())
}
fn default_scrollbar_track_hover_fg() -> ColorDef {
ColorDef::Named("Gray".to_string())
}
fn default_scrollbar_thumb_hover_fg() -> ColorDef {
ColorDef::Named("White".to_string())
}
fn default_compose_margin_bg() -> ColorDef {
ColorDef::Rgb(18, 18, 18) }
fn default_semantic_highlight_bg() -> ColorDef {
ColorDef::Rgb(60, 60, 80) }
fn default_terminal_bg() -> ColorDef {
ColorDef::Named("Default".to_string()) }
fn default_terminal_fg() -> ColorDef {
ColorDef::Named("Default".to_string()) }
fn default_status_warning_indicator_bg() -> ColorDef {
ColorDef::Rgb(181, 137, 0) }
fn default_status_warning_indicator_fg() -> ColorDef {
ColorDef::Rgb(0, 0, 0) }
fn default_status_error_indicator_bg() -> ColorDef {
ColorDef::Rgb(220, 50, 47) }
fn default_status_error_indicator_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255) }
fn default_status_warning_indicator_hover_bg() -> ColorDef {
ColorDef::Rgb(211, 167, 30) }
fn default_status_warning_indicator_hover_fg() -> ColorDef {
ColorDef::Rgb(0, 0, 0) }
fn default_status_error_indicator_hover_bg() -> ColorDef {
ColorDef::Rgb(250, 80, 77) }
fn default_status_error_indicator_hover_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255) }
fn default_tab_drop_zone_bg() -> ColorDef {
ColorDef::Rgb(70, 130, 180) }
fn default_tab_drop_zone_border() -> ColorDef {
ColorDef::Rgb(100, 149, 237) }
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SearchColors {
#[serde(default = "default_search_match_bg")]
pub match_bg: ColorDef,
#[serde(default = "default_search_match_fg")]
pub match_fg: ColorDef,
}
fn default_search_match_bg() -> ColorDef {
ColorDef::Rgb(100, 100, 20)
}
fn default_search_match_fg() -> ColorDef {
ColorDef::Rgb(255, 255, 255)
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct DiagnosticColors {
#[serde(default = "default_diagnostic_error_fg")]
pub error_fg: ColorDef,
#[serde(default = "default_diagnostic_error_bg")]
pub error_bg: ColorDef,
#[serde(default = "default_diagnostic_warning_fg")]
pub warning_fg: ColorDef,
#[serde(default = "default_diagnostic_warning_bg")]
pub warning_bg: ColorDef,
#[serde(default = "default_diagnostic_info_fg")]
pub info_fg: ColorDef,
#[serde(default = "default_diagnostic_info_bg")]
pub info_bg: ColorDef,
#[serde(default = "default_diagnostic_hint_fg")]
pub hint_fg: ColorDef,
#[serde(default = "default_diagnostic_hint_bg")]
pub hint_bg: ColorDef,
}
fn default_diagnostic_error_fg() -> ColorDef {
ColorDef::Named("Red".to_string())
}
fn default_diagnostic_error_bg() -> ColorDef {
ColorDef::Rgb(60, 20, 20)
}
fn default_diagnostic_warning_fg() -> ColorDef {
ColorDef::Named("Yellow".to_string())
}
fn default_diagnostic_warning_bg() -> ColorDef {
ColorDef::Rgb(60, 50, 0)
}
fn default_diagnostic_info_fg() -> ColorDef {
ColorDef::Named("Blue".to_string())
}
fn default_diagnostic_info_bg() -> ColorDef {
ColorDef::Rgb(0, 30, 60)
}
fn default_diagnostic_hint_fg() -> ColorDef {
ColorDef::Named("Gray".to_string())
}
fn default_diagnostic_hint_bg() -> ColorDef {
ColorDef::Rgb(30, 30, 30)
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SyntaxColors {
#[serde(default = "default_syntax_keyword")]
pub keyword: ColorDef,
#[serde(default = "default_syntax_string")]
pub string: ColorDef,
#[serde(default = "default_syntax_comment")]
pub comment: ColorDef,
#[serde(default = "default_syntax_function")]
pub function: ColorDef,
#[serde(rename = "type", default = "default_syntax_type")]
pub type_: ColorDef,
#[serde(default = "default_syntax_variable")]
pub variable: ColorDef,
#[serde(default = "default_syntax_constant")]
pub constant: ColorDef,
#[serde(default = "default_syntax_operator")]
pub operator: ColorDef,
}
fn default_syntax_keyword() -> ColorDef {
ColorDef::Rgb(86, 156, 214)
}
fn default_syntax_string() -> ColorDef {
ColorDef::Rgb(206, 145, 120)
}
fn default_syntax_comment() -> ColorDef {
ColorDef::Rgb(106, 153, 85)
}
fn default_syntax_function() -> ColorDef {
ColorDef::Rgb(220, 220, 170)
}
fn default_syntax_type() -> ColorDef {
ColorDef::Rgb(78, 201, 176)
}
fn default_syntax_variable() -> ColorDef {
ColorDef::Rgb(156, 220, 254)
}
fn default_syntax_constant() -> ColorDef {
ColorDef::Rgb(79, 193, 255)
}
fn default_syntax_operator() -> ColorDef {
ColorDef::Rgb(212, 212, 212)
}
#[derive(Debug, Clone)]
pub struct Theme {
pub name: String,
pub editor_bg: Color,
pub editor_fg: Color,
pub cursor: Color,
pub inactive_cursor: Color,
pub selection_bg: Color,
pub current_line_bg: Color,
pub line_number_fg: Color,
pub line_number_bg: Color,
pub diff_add_bg: Color,
pub diff_remove_bg: Color,
pub diff_modify_bg: Color,
pub diff_add_highlight_bg: Color,
pub diff_remove_highlight_bg: Color,
pub tab_active_fg: Color,
pub tab_active_bg: Color,
pub tab_inactive_fg: Color,
pub tab_inactive_bg: Color,
pub tab_separator_bg: Color,
pub tab_close_hover_fg: Color,
pub tab_hover_bg: Color,
pub menu_bg: Color,
pub menu_fg: Color,
pub menu_active_bg: Color,
pub menu_active_fg: Color,
pub menu_dropdown_bg: Color,
pub menu_dropdown_fg: Color,
pub menu_highlight_bg: Color,
pub menu_highlight_fg: Color,
pub menu_border_fg: Color,
pub menu_separator_fg: Color,
pub menu_hover_bg: Color,
pub menu_hover_fg: Color,
pub menu_disabled_fg: Color,
pub menu_disabled_bg: Color,
pub status_bar_fg: Color,
pub status_bar_bg: Color,
pub prompt_fg: Color,
pub prompt_bg: Color,
pub prompt_selection_fg: Color,
pub prompt_selection_bg: Color,
pub popup_border_fg: Color,
pub popup_bg: Color,
pub popup_selection_bg: Color,
pub popup_text_fg: Color,
pub suggestion_bg: Color,
pub suggestion_selected_bg: Color,
pub help_bg: Color,
pub help_fg: Color,
pub help_key_fg: Color,
pub help_separator_fg: Color,
pub help_indicator_fg: Color,
pub help_indicator_bg: Color,
pub inline_code_bg: Color,
pub split_separator_fg: Color,
pub split_separator_hover_fg: Color,
pub scrollbar_track_fg: Color,
pub scrollbar_thumb_fg: Color,
pub scrollbar_track_hover_fg: Color,
pub scrollbar_thumb_hover_fg: Color,
pub compose_margin_bg: Color,
pub semantic_highlight_bg: Color,
pub terminal_bg: Color,
pub terminal_fg: Color,
pub status_warning_indicator_bg: Color,
pub status_warning_indicator_fg: Color,
pub status_error_indicator_bg: Color,
pub status_error_indicator_fg: Color,
pub status_warning_indicator_hover_bg: Color,
pub status_warning_indicator_hover_fg: Color,
pub status_error_indicator_hover_bg: Color,
pub status_error_indicator_hover_fg: Color,
pub tab_drop_zone_bg: Color,
pub tab_drop_zone_border: Color,
pub search_match_bg: Color,
pub search_match_fg: Color,
pub diagnostic_error_fg: Color,
pub diagnostic_error_bg: Color,
pub diagnostic_warning_fg: Color,
pub diagnostic_warning_bg: Color,
pub diagnostic_info_fg: Color,
pub diagnostic_info_bg: Color,
pub diagnostic_hint_fg: Color,
pub diagnostic_hint_bg: Color,
pub syntax_keyword: Color,
pub syntax_string: Color,
pub syntax_comment: Color,
pub syntax_function: Color,
pub syntax_type: Color,
pub syntax_variable: Color,
pub syntax_constant: Color,
pub syntax_operator: Color,
}
impl From<ThemeFile> for Theme {
fn from(file: ThemeFile) -> Self {
Self {
name: file.name,
editor_bg: file.editor.bg.into(),
editor_fg: file.editor.fg.into(),
cursor: file.editor.cursor.into(),
inactive_cursor: file.editor.inactive_cursor.into(),
selection_bg: file.editor.selection_bg.into(),
current_line_bg: file.editor.current_line_bg.into(),
line_number_fg: file.editor.line_number_fg.into(),
line_number_bg: file.editor.line_number_bg.into(),
diff_add_bg: file.editor.diff_add_bg.clone().into(),
diff_remove_bg: file.editor.diff_remove_bg.clone().into(),
diff_modify_bg: file.editor.diff_modify_bg.into(),
diff_add_highlight_bg: brighten_color(file.editor.diff_add_bg.into(), 40),
diff_remove_highlight_bg: brighten_color(file.editor.diff_remove_bg.into(), 40),
tab_active_fg: file.ui.tab_active_fg.into(),
tab_active_bg: file.ui.tab_active_bg.into(),
tab_inactive_fg: file.ui.tab_inactive_fg.into(),
tab_inactive_bg: file.ui.tab_inactive_bg.into(),
tab_separator_bg: file.ui.tab_separator_bg.into(),
tab_close_hover_fg: file.ui.tab_close_hover_fg.into(),
tab_hover_bg: file.ui.tab_hover_bg.into(),
menu_bg: file.ui.menu_bg.into(),
menu_fg: file.ui.menu_fg.into(),
menu_active_bg: file.ui.menu_active_bg.into(),
menu_active_fg: file.ui.menu_active_fg.into(),
menu_dropdown_bg: file.ui.menu_dropdown_bg.into(),
menu_dropdown_fg: file.ui.menu_dropdown_fg.into(),
menu_highlight_bg: file.ui.menu_highlight_bg.into(),
menu_highlight_fg: file.ui.menu_highlight_fg.into(),
menu_border_fg: file.ui.menu_border_fg.into(),
menu_separator_fg: file.ui.menu_separator_fg.into(),
menu_hover_bg: file.ui.menu_hover_bg.into(),
menu_hover_fg: file.ui.menu_hover_fg.into(),
menu_disabled_fg: file.ui.menu_disabled_fg.into(),
menu_disabled_bg: file.ui.menu_disabled_bg.into(),
status_bar_fg: file.ui.status_bar_fg.into(),
status_bar_bg: file.ui.status_bar_bg.into(),
prompt_fg: file.ui.prompt_fg.into(),
prompt_bg: file.ui.prompt_bg.into(),
prompt_selection_fg: file.ui.prompt_selection_fg.into(),
prompt_selection_bg: file.ui.prompt_selection_bg.into(),
popup_border_fg: file.ui.popup_border_fg.into(),
popup_bg: file.ui.popup_bg.into(),
popup_selection_bg: file.ui.popup_selection_bg.into(),
popup_text_fg: file.ui.popup_text_fg.into(),
suggestion_bg: file.ui.suggestion_bg.into(),
suggestion_selected_bg: file.ui.suggestion_selected_bg.into(),
help_bg: file.ui.help_bg.into(),
help_fg: file.ui.help_fg.into(),
help_key_fg: file.ui.help_key_fg.into(),
help_separator_fg: file.ui.help_separator_fg.into(),
help_indicator_fg: file.ui.help_indicator_fg.into(),
help_indicator_bg: file.ui.help_indicator_bg.into(),
inline_code_bg: file.ui.inline_code_bg.into(),
split_separator_fg: file.ui.split_separator_fg.into(),
split_separator_hover_fg: file.ui.split_separator_hover_fg.into(),
scrollbar_track_fg: file.ui.scrollbar_track_fg.into(),
scrollbar_thumb_fg: file.ui.scrollbar_thumb_fg.into(),
scrollbar_track_hover_fg: file.ui.scrollbar_track_hover_fg.into(),
scrollbar_thumb_hover_fg: file.ui.scrollbar_thumb_hover_fg.into(),
compose_margin_bg: file.ui.compose_margin_bg.into(),
semantic_highlight_bg: file.ui.semantic_highlight_bg.into(),
terminal_bg: file.ui.terminal_bg.into(),
terminal_fg: file.ui.terminal_fg.into(),
status_warning_indicator_bg: file.ui.status_warning_indicator_bg.into(),
status_warning_indicator_fg: file.ui.status_warning_indicator_fg.into(),
status_error_indicator_bg: file.ui.status_error_indicator_bg.into(),
status_error_indicator_fg: file.ui.status_error_indicator_fg.into(),
status_warning_indicator_hover_bg: file.ui.status_warning_indicator_hover_bg.into(),
status_warning_indicator_hover_fg: file.ui.status_warning_indicator_hover_fg.into(),
status_error_indicator_hover_bg: file.ui.status_error_indicator_hover_bg.into(),
status_error_indicator_hover_fg: file.ui.status_error_indicator_hover_fg.into(),
tab_drop_zone_bg: file.ui.tab_drop_zone_bg.into(),
tab_drop_zone_border: file.ui.tab_drop_zone_border.into(),
search_match_bg: file.search.match_bg.into(),
search_match_fg: file.search.match_fg.into(),
diagnostic_error_fg: file.diagnostic.error_fg.into(),
diagnostic_error_bg: file.diagnostic.error_bg.into(),
diagnostic_warning_fg: file.diagnostic.warning_fg.into(),
diagnostic_warning_bg: file.diagnostic.warning_bg.into(),
diagnostic_info_fg: file.diagnostic.info_fg.into(),
diagnostic_info_bg: file.diagnostic.info_bg.into(),
diagnostic_hint_fg: file.diagnostic.hint_fg.into(),
diagnostic_hint_bg: file.diagnostic.hint_bg.into(),
syntax_keyword: file.syntax.keyword.into(),
syntax_string: file.syntax.string.into(),
syntax_comment: file.syntax.comment.into(),
syntax_function: file.syntax.function.into(),
syntax_type: file.syntax.type_.into(),
syntax_variable: file.syntax.variable.into(),
syntax_constant: file.syntax.constant.into(),
syntax_operator: file.syntax.operator.into(),
}
}
}
impl Theme {
fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
let content = std::fs::read_to_string(path)
.map_err(|e| format!("Failed to read theme file: {}", e))?;
let theme_file: ThemeFile = serde_json::from_str(&content)
.map_err(|e| format!("Failed to parse theme file: {}", e))?;
Ok(theme_file.into())
}
fn load_builtin_theme(name: &str) -> Option<Self> {
let mut theme_paths = vec![
format!("themes/{}.json", name),
format!("../themes/{}.json", name),
format!("../../themes/{}.json", name),
];
if let Some(config_dir) = dirs::config_dir() {
let user_theme_path = config_dir
.join("fresh")
.join("themes")
.join(format!("{}.json", name));
theme_paths.insert(0, user_theme_path.to_string_lossy().to_string());
}
for path in &theme_paths {
if let Ok(theme) = Self::from_file(path) {
return Some(theme);
}
}
None
}
pub fn dark() -> Self {
Self {
name: "dark".to_string(),
editor_bg: Color::Rgb(30, 30, 30),
editor_fg: Color::Rgb(212, 212, 212),
cursor: Color::Rgb(255, 255, 255),
inactive_cursor: Color::Rgb(100, 100, 100),
selection_bg: Color::Rgb(38, 79, 120),
current_line_bg: Color::Rgb(40, 40, 40),
line_number_fg: Color::Rgb(100, 100, 100),
line_number_bg: Color::Rgb(30, 30, 30),
diff_add_bg: Color::Rgb(35, 60, 35), diff_remove_bg: Color::Rgb(70, 35, 35), diff_modify_bg: Color::Rgb(40, 38, 30), diff_add_highlight_bg: Color::Rgb(60, 110, 60), diff_remove_highlight_bg: Color::Rgb(120, 50, 50),
tab_active_fg: Color::Yellow,
tab_active_bg: Color::Blue,
tab_inactive_fg: Color::White,
tab_inactive_bg: Color::DarkGray,
tab_separator_bg: Color::Rgb(45, 45, 48),
tab_close_hover_fg: Color::Rgb(255, 100, 100),
tab_hover_bg: Color::Rgb(70, 70, 75),
menu_bg: Color::Rgb(60, 60, 65),
menu_fg: Color::Rgb(220, 220, 220),
menu_active_bg: Color::Rgb(60, 60, 60),
menu_active_fg: Color::Rgb(255, 255, 255),
menu_dropdown_bg: Color::Rgb(50, 50, 50),
menu_dropdown_fg: Color::Rgb(220, 220, 220),
menu_highlight_bg: Color::Rgb(70, 130, 180),
menu_highlight_fg: Color::Rgb(255, 255, 255),
menu_border_fg: Color::Rgb(100, 100, 100),
menu_separator_fg: Color::Rgb(80, 80, 80),
menu_hover_bg: Color::Rgb(55, 55, 55),
menu_hover_fg: Color::Rgb(255, 255, 255),
menu_disabled_fg: Color::Rgb(100, 100, 100), menu_disabled_bg: Color::Rgb(50, 50, 50),
status_bar_fg: Color::White,
status_bar_bg: Color::Rgb(30, 30, 30), prompt_fg: Color::White,
prompt_bg: Color::Rgb(20, 20, 20), prompt_selection_fg: Color::White,
prompt_selection_bg: Color::Rgb(58, 79, 120),
popup_border_fg: Color::Gray,
popup_bg: Color::Rgb(30, 30, 30),
popup_selection_bg: Color::Rgb(58, 79, 120),
popup_text_fg: Color::White,
suggestion_bg: Color::Rgb(30, 30, 30),
suggestion_selected_bg: Color::Rgb(58, 79, 120),
help_bg: Color::Black,
help_fg: Color::White,
help_key_fg: Color::Cyan,
help_separator_fg: Color::DarkGray,
help_indicator_fg: Color::Red,
help_indicator_bg: Color::Black,
inline_code_bg: Color::Rgb(60, 60, 60),
split_separator_fg: Color::Rgb(100, 100, 100),
split_separator_hover_fg: Color::Rgb(100, 149, 237),
scrollbar_track_fg: Color::DarkGray,
scrollbar_thumb_fg: Color::Gray,
scrollbar_track_hover_fg: Color::Gray,
scrollbar_thumb_hover_fg: Color::White,
compose_margin_bg: Color::Rgb(18, 18, 18),
semantic_highlight_bg: Color::Rgb(60, 60, 80),
terminal_bg: Color::Reset,
terminal_fg: Color::Reset,
status_warning_indicator_bg: Color::Rgb(181, 137, 0), status_warning_indicator_fg: Color::Rgb(0, 0, 0), status_error_indicator_bg: Color::Rgb(220, 50, 47), status_error_indicator_fg: Color::Rgb(255, 255, 255), status_warning_indicator_hover_bg: Color::Rgb(211, 167, 30), status_warning_indicator_hover_fg: Color::Rgb(0, 0, 0),
status_error_indicator_hover_bg: Color::Rgb(250, 80, 77), status_error_indicator_hover_fg: Color::Rgb(255, 255, 255),
tab_drop_zone_bg: Color::Rgb(70, 130, 180), tab_drop_zone_border: Color::Rgb(100, 149, 237),
search_match_bg: Color::Rgb(100, 100, 20), search_match_fg: Color::Rgb(255, 255, 255),
diagnostic_error_fg: Color::Red,
diagnostic_error_bg: Color::Rgb(60, 20, 20),
diagnostic_warning_fg: Color::Yellow,
diagnostic_warning_bg: Color::Rgb(60, 50, 0),
diagnostic_info_fg: Color::Blue,
diagnostic_info_bg: Color::Rgb(0, 30, 60),
diagnostic_hint_fg: Color::Gray,
diagnostic_hint_bg: Color::Rgb(30, 30, 30),
syntax_keyword: Color::Rgb(86, 156, 214),
syntax_string: Color::Rgb(206, 145, 120),
syntax_comment: Color::Rgb(106, 153, 85),
syntax_function: Color::Rgb(220, 220, 170),
syntax_type: Color::Rgb(78, 201, 176),
syntax_variable: Color::Rgb(156, 220, 254),
syntax_constant: Color::Rgb(79, 193, 255),
syntax_operator: Color::Rgb(212, 212, 212),
}
}
pub fn light() -> Self {
Self {
name: "light".to_string(),
editor_bg: Color::Rgb(255, 255, 255),
editor_fg: Color::Rgb(0, 0, 0),
cursor: Color::Rgb(0, 0, 0),
inactive_cursor: Color::Rgb(180, 180, 180),
selection_bg: Color::Rgb(173, 214, 255),
current_line_bg: Color::Rgb(245, 245, 245),
line_number_fg: Color::Rgb(140, 140, 140),
line_number_bg: Color::Rgb(255, 255, 255),
diff_add_bg: Color::Rgb(200, 255, 200), diff_remove_bg: Color::Rgb(255, 200, 200), diff_modify_bg: Color::Rgb(255, 252, 240), diff_add_highlight_bg: Color::Rgb(140, 220, 140), diff_remove_highlight_bg: Color::Rgb(220, 140, 140),
tab_active_fg: Color::Rgb(40, 40, 40),
tab_active_bg: Color::Rgb(255, 255, 255),
tab_inactive_fg: Color::Rgb(100, 100, 100),
tab_inactive_bg: Color::Rgb(230, 230, 230),
tab_separator_bg: Color::Rgb(200, 200, 200),
tab_close_hover_fg: Color::Rgb(220, 50, 50),
tab_hover_bg: Color::Rgb(210, 210, 210),
menu_bg: Color::Rgb(245, 245, 245),
menu_fg: Color::Rgb(30, 30, 30),
menu_active_bg: Color::Rgb(225, 225, 225),
menu_active_fg: Color::Rgb(0, 0, 0),
menu_dropdown_bg: Color::Rgb(248, 248, 248),
menu_dropdown_fg: Color::Rgb(30, 30, 30),
menu_highlight_bg: Color::Rgb(209, 226, 243), menu_highlight_fg: Color::Rgb(0, 0, 0), menu_border_fg: Color::Rgb(180, 180, 180),
menu_separator_fg: Color::Rgb(210, 210, 210),
menu_hover_bg: Color::Rgb(230, 235, 240),
menu_hover_fg: Color::Rgb(0, 0, 0),
menu_disabled_fg: Color::Rgb(160, 160, 160), menu_disabled_bg: Color::Rgb(248, 248, 248),
status_bar_fg: Color::Black,
status_bar_bg: Color::Rgb(220, 220, 220), prompt_fg: Color::Black,
prompt_bg: Color::Rgb(230, 240, 250), prompt_selection_fg: Color::Black,
prompt_selection_bg: Color::Rgb(173, 214, 255),
popup_border_fg: Color::Rgb(180, 180, 180),
popup_bg: Color::Rgb(232, 238, 245), popup_selection_bg: Color::Rgb(209, 226, 243),
popup_text_fg: Color::Rgb(30, 30, 30),
suggestion_bg: Color::Rgb(232, 238, 245), suggestion_selected_bg: Color::Rgb(209, 226, 243),
help_bg: Color::White,
help_fg: Color::Black,
help_key_fg: Color::Blue,
help_separator_fg: Color::Gray,
help_indicator_fg: Color::Red,
help_indicator_bg: Color::White,
inline_code_bg: Color::Rgb(230, 230, 235),
split_separator_fg: Color::Rgb(140, 140, 140),
split_separator_hover_fg: Color::Rgb(70, 130, 180),
scrollbar_track_fg: Color::Rgb(220, 220, 220),
scrollbar_thumb_fg: Color::Rgb(180, 180, 180),
scrollbar_track_hover_fg: Color::Rgb(200, 200, 200),
scrollbar_thumb_hover_fg: Color::Rgb(140, 140, 140),
compose_margin_bg: Color::Rgb(220, 220, 225),
semantic_highlight_bg: Color::Rgb(220, 230, 240),
terminal_bg: Color::Reset,
terminal_fg: Color::Reset,
status_warning_indicator_bg: Color::Rgb(202, 145, 0), status_warning_indicator_fg: Color::Rgb(0, 0, 0), status_error_indicator_bg: Color::Rgb(200, 40, 40), status_error_indicator_fg: Color::Rgb(255, 255, 255), status_warning_indicator_hover_bg: Color::Rgb(232, 175, 30), status_warning_indicator_hover_fg: Color::Rgb(0, 0, 0),
status_error_indicator_hover_bg: Color::Rgb(230, 70, 70), status_error_indicator_hover_fg: Color::Rgb(255, 255, 255),
tab_drop_zone_bg: Color::Rgb(173, 214, 255), tab_drop_zone_border: Color::Rgb(70, 130, 180),
search_match_bg: Color::Rgb(255, 255, 150), search_match_fg: Color::Rgb(0, 0, 0),
diagnostic_error_fg: Color::Red,
diagnostic_error_bg: Color::Rgb(255, 220, 220),
diagnostic_warning_fg: Color::Rgb(128, 128, 0),
diagnostic_warning_bg: Color::Rgb(255, 255, 200),
diagnostic_info_fg: Color::Blue,
diagnostic_info_bg: Color::Rgb(220, 240, 255),
diagnostic_hint_fg: Color::DarkGray,
diagnostic_hint_bg: Color::Rgb(240, 240, 240),
syntax_keyword: Color::Rgb(175, 0, 219), syntax_string: Color::Rgb(163, 21, 21), syntax_comment: Color::Rgb(0, 128, 0), syntax_function: Color::Rgb(121, 94, 38), syntax_type: Color::Rgb(0, 128, 128), syntax_variable: Color::Rgb(0, 16, 128), syntax_constant: Color::Rgb(0, 112, 193), syntax_operator: Color::Rgb(0, 0, 0), }
}
pub fn high_contrast() -> Self {
Self {
name: "high-contrast".to_string(),
editor_bg: Color::Black,
editor_fg: Color::White,
cursor: Color::White,
inactive_cursor: Color::DarkGray,
selection_bg: Color::Rgb(0, 100, 200),
current_line_bg: Color::Rgb(20, 20, 20),
line_number_fg: Color::Rgb(140, 140, 140),
line_number_bg: Color::Black,
diff_add_bg: Color::Rgb(0, 80, 0), diff_remove_bg: Color::Rgb(100, 0, 0), diff_modify_bg: Color::Rgb(25, 22, 0), diff_add_highlight_bg: Color::Rgb(0, 140, 0), diff_remove_highlight_bg: Color::Rgb(180, 0, 0),
tab_active_fg: Color::Black,
tab_active_bg: Color::Yellow,
tab_inactive_fg: Color::White,
tab_inactive_bg: Color::Black,
tab_separator_bg: Color::Rgb(30, 30, 35),
tab_close_hover_fg: Color::Rgb(249, 38, 114), tab_hover_bg: Color::Rgb(50, 50, 55),
menu_bg: Color::Rgb(50, 50, 55),
menu_fg: Color::White,
menu_active_bg: Color::Yellow,
menu_active_fg: Color::Black,
menu_dropdown_bg: Color::Rgb(20, 20, 20),
menu_dropdown_fg: Color::White,
menu_highlight_bg: Color::Rgb(0, 100, 200),
menu_highlight_fg: Color::White,
menu_border_fg: Color::Yellow,
menu_separator_fg: Color::White,
menu_hover_bg: Color::Rgb(50, 50, 50),
menu_hover_fg: Color::Yellow,
menu_disabled_fg: Color::DarkGray, menu_disabled_bg: Color::Rgb(20, 20, 20),
status_bar_fg: Color::White,
status_bar_bg: Color::Rgb(20, 20, 20), prompt_fg: Color::White,
prompt_bg: Color::Rgb(10, 10, 10), prompt_selection_fg: Color::White,
prompt_selection_bg: Color::Rgb(0, 100, 200),
popup_border_fg: Color::LightCyan,
popup_bg: Color::Black,
popup_selection_bg: Color::Rgb(0, 100, 200),
popup_text_fg: Color::White,
suggestion_bg: Color::Black,
suggestion_selected_bg: Color::Rgb(0, 100, 200),
help_bg: Color::Black,
help_fg: Color::White,
help_key_fg: Color::LightCyan,
help_separator_fg: Color::White,
help_indicator_fg: Color::Red,
help_indicator_bg: Color::Black,
inline_code_bg: Color::Rgb(40, 40, 40),
split_separator_fg: Color::Rgb(140, 140, 140),
split_separator_hover_fg: Color::Yellow,
scrollbar_track_fg: Color::White,
scrollbar_thumb_fg: Color::Yellow,
scrollbar_track_hover_fg: Color::Yellow,
scrollbar_thumb_hover_fg: Color::Cyan,
compose_margin_bg: Color::Rgb(10, 10, 10),
semantic_highlight_bg: Color::Rgb(0, 60, 100),
terminal_bg: Color::Reset,
terminal_fg: Color::Reset,
status_warning_indicator_bg: Color::Yellow, status_warning_indicator_fg: Color::Black, status_error_indicator_bg: Color::Red, status_error_indicator_fg: Color::White, status_warning_indicator_hover_bg: Color::LightYellow, status_warning_indicator_hover_fg: Color::Black,
status_error_indicator_hover_bg: Color::LightRed, status_error_indicator_hover_fg: Color::White,
tab_drop_zone_bg: Color::Rgb(0, 100, 200), tab_drop_zone_border: Color::Yellow,
search_match_bg: Color::Yellow,
search_match_fg: Color::Black,
diagnostic_error_fg: Color::Red,
diagnostic_error_bg: Color::Rgb(100, 0, 0),
diagnostic_warning_fg: Color::Yellow,
diagnostic_warning_bg: Color::Rgb(100, 100, 0),
diagnostic_info_fg: Color::Cyan,
diagnostic_info_bg: Color::Rgb(0, 50, 100),
diagnostic_hint_fg: Color::White,
diagnostic_hint_bg: Color::Rgb(50, 50, 50),
syntax_keyword: Color::Cyan,
syntax_string: Color::Green,
syntax_comment: Color::Gray,
syntax_function: Color::Yellow,
syntax_type: Color::Magenta,
syntax_variable: Color::White,
syntax_constant: Color::LightBlue,
syntax_operator: Color::White,
}
}
pub fn from_name(name: &str) -> Self {
let normalized_name = name.to_lowercase().replace('_', "-");
if let Some(theme) = Self::load_builtin_theme(&normalized_name) {
return theme;
}
match normalized_name.as_str() {
"light" => Self::light(),
"high-contrast" => Self::high_contrast(),
"nostalgia" => Self::nostalgia(),
_ => Self::dark(),
}
}
pub fn available_themes() -> Vec<String> {
let mut themes: Vec<String> = vec![
"dark".to_string(),
"light".to_string(),
"high-contrast".to_string(),
"nostalgia".to_string(),
];
if let Ok(entries) = std::fs::read_dir("themes") {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "json") {
if let Some(stem) = path.file_stem() {
let name = stem.to_string_lossy().to_string();
if !themes.iter().any(|t| t == &name) {
themes.push(name);
}
}
}
}
}
if let Some(config_dir) = dirs::config_dir() {
let user_themes_dir = config_dir.join("fresh").join("themes");
if let Ok(entries) = std::fs::read_dir(&user_themes_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "json") {
if let Some(stem) = path.file_stem() {
let name = stem.to_string_lossy().to_string();
if !themes.iter().any(|t| t == &name) {
themes.push(name);
}
}
}
}
}
}
themes
}
pub fn nostalgia() -> Self {
Self {
name: "nostalgia".to_string(),
editor_bg: Color::Rgb(0, 0, 170), editor_fg: Color::Rgb(255, 255, 85), cursor: Color::Rgb(255, 255, 255), inactive_cursor: Color::Rgb(170, 170, 170),
selection_bg: Color::Rgb(170, 170, 170), current_line_bg: Color::Rgb(0, 0, 128), line_number_fg: Color::Rgb(85, 255, 255), line_number_bg: Color::Rgb(0, 0, 170),
diff_add_bg: Color::Rgb(0, 100, 0), diff_remove_bg: Color::Rgb(170, 0, 0), diff_modify_bg: Color::Rgb(20, 20, 140), diff_add_highlight_bg: Color::Rgb(0, 170, 0), diff_remove_highlight_bg: Color::Rgb(255, 0, 0),
tab_active_fg: Color::Rgb(0, 0, 0),
tab_active_bg: Color::Rgb(170, 170, 170),
tab_inactive_fg: Color::Rgb(255, 255, 85),
tab_inactive_bg: Color::Rgb(0, 0, 128),
tab_separator_bg: Color::Rgb(0, 0, 170),
tab_close_hover_fg: Color::Rgb(255, 85, 85), tab_hover_bg: Color::Rgb(0, 0, 200),
menu_bg: Color::Rgb(170, 170, 170),
menu_fg: Color::Rgb(0, 0, 0),
menu_active_bg: Color::Rgb(0, 170, 0),
menu_active_fg: Color::Rgb(255, 255, 255),
menu_dropdown_bg: Color::Rgb(170, 170, 170),
menu_dropdown_fg: Color::Rgb(0, 0, 0),
menu_highlight_bg: Color::Rgb(0, 170, 0), menu_highlight_fg: Color::Rgb(255, 255, 255),
menu_border_fg: Color::Rgb(0, 0, 0),
menu_separator_fg: Color::Rgb(85, 85, 85),
menu_hover_bg: Color::Rgb(0, 170, 0),
menu_hover_fg: Color::Rgb(255, 255, 255),
menu_disabled_fg: Color::Rgb(85, 85, 85), menu_disabled_bg: Color::Rgb(170, 170, 170),
status_bar_fg: Color::Rgb(0, 0, 0),
status_bar_bg: Color::Rgb(0, 170, 170), prompt_fg: Color::Rgb(255, 255, 85), prompt_bg: Color::Rgb(0, 0, 170), prompt_selection_fg: Color::Rgb(0, 0, 0),
prompt_selection_bg: Color::Rgb(170, 170, 170),
popup_border_fg: Color::Rgb(255, 255, 255),
popup_bg: Color::Rgb(0, 0, 170),
popup_selection_bg: Color::Rgb(0, 170, 0),
popup_text_fg: Color::Rgb(255, 255, 85),
suggestion_bg: Color::Rgb(0, 0, 170),
suggestion_selected_bg: Color::Rgb(0, 170, 0),
help_bg: Color::Rgb(0, 0, 170),
help_fg: Color::Rgb(255, 255, 85),
help_key_fg: Color::Rgb(85, 255, 255),
help_separator_fg: Color::Rgb(170, 170, 170),
help_indicator_fg: Color::Rgb(255, 85, 85),
help_indicator_bg: Color::Rgb(0, 0, 170),
inline_code_bg: Color::Rgb(0, 0, 85),
split_separator_fg: Color::Rgb(85, 255, 255),
split_separator_hover_fg: Color::Rgb(255, 255, 255),
scrollbar_track_fg: Color::Rgb(0, 0, 128),
scrollbar_thumb_fg: Color::Rgb(170, 170, 170),
scrollbar_track_hover_fg: Color::Rgb(0, 0, 128),
scrollbar_thumb_hover_fg: Color::Rgb(255, 255, 255),
compose_margin_bg: Color::Rgb(0, 0, 128),
semantic_highlight_bg: Color::Rgb(0, 85, 170),
terminal_bg: Color::Rgb(0, 0, 170), terminal_fg: Color::Rgb(255, 255, 85),
status_warning_indicator_bg: Color::Rgb(170, 85, 0), status_warning_indicator_fg: Color::Rgb(255, 255, 255), status_error_indicator_bg: Color::Rgb(170, 0, 0), status_error_indicator_fg: Color::Rgb(255, 255, 255), status_warning_indicator_hover_bg: Color::Rgb(200, 115, 30), status_warning_indicator_hover_fg: Color::Rgb(255, 255, 255),
status_error_indicator_hover_bg: Color::Rgb(200, 30, 30), status_error_indicator_hover_fg: Color::Rgb(255, 255, 255),
tab_drop_zone_bg: Color::Rgb(0, 170, 170), tab_drop_zone_border: Color::Rgb(255, 255, 255),
search_match_bg: Color::Rgb(170, 85, 0), search_match_fg: Color::Rgb(255, 255, 255),
diagnostic_error_fg: Color::Rgb(255, 85, 85),
diagnostic_error_bg: Color::Rgb(128, 0, 0),
diagnostic_warning_fg: Color::Rgb(255, 255, 85),
diagnostic_warning_bg: Color::Rgb(128, 128, 0),
diagnostic_info_fg: Color::Rgb(85, 255, 255),
diagnostic_info_bg: Color::Rgb(0, 85, 128),
diagnostic_hint_fg: Color::Rgb(170, 170, 170),
diagnostic_hint_bg: Color::Rgb(0, 0, 128),
syntax_keyword: Color::Rgb(255, 255, 255), syntax_string: Color::Rgb(0, 255, 255), syntax_comment: Color::Rgb(128, 128, 128), syntax_function: Color::Rgb(255, 255, 0), syntax_type: Color::Rgb(0, 255, 0), syntax_variable: Color::Rgb(255, 255, 85), syntax_constant: Color::Rgb(255, 0, 255), syntax_operator: Color::Rgb(170, 170, 170), }
}
pub fn set_terminal_cursor_color(&self) {
use std::io::Write;
if let Some((r, g, b)) = color_to_rgb(self.cursor) {
let _ = write!(
std::io::stdout(),
"\x1b]12;#{:02x}{:02x}{:02x}\x07",
r,
g,
b
);
let _ = std::io::stdout().flush();
}
}
pub fn reset_terminal_cursor_color() {
use std::io::Write;
let _ = write!(std::io::stdout(), "\x1b]112\x07");
let _ = std::io::stdout().flush();
}
}
impl Default for Theme {
fn default() -> Self {
Self::high_contrast()
}
}
pub fn get_theme_schema() -> serde_json::Value {
use schemars::schema_for;
let schema = schema_for!(ThemeFile);
serde_json::to_value(&schema).unwrap_or_default()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_theme_creation() {
let dark = Theme::dark();
assert_eq!(dark.name, "dark");
let light = Theme::light();
assert_eq!(light.name, "light");
let high_contrast = Theme::high_contrast();
assert_eq!(high_contrast.name, "high-contrast");
}
#[test]
fn test_theme_from_name() {
let theme = Theme::from_name("light");
assert_eq!(theme.name, "light");
let theme = Theme::from_name("high-contrast");
assert_eq!(theme.name, "high-contrast");
let theme = Theme::from_name("unknown");
assert_eq!(theme.name, "dark");
}
#[test]
fn test_available_themes() {
let themes = Theme::available_themes();
assert!(themes.len() >= 4);
assert!(themes.contains(&"dark".to_string()));
assert!(themes.contains(&"light".to_string()));
assert!(themes.contains(&"high-contrast".to_string()));
assert!(themes.contains(&"nostalgia".to_string()));
}
#[test]
fn test_default_theme() {
let theme = Theme::default();
assert_eq!(theme.name, "high-contrast");
}
#[test]
fn test_default_reset_color() {
let color: Color = ColorDef::Named("Default".to_string()).into();
assert_eq!(color, Color::Reset);
let color: Color = ColorDef::Named("Reset".to_string()).into();
assert_eq!(color, Color::Reset);
}
}