From 7aa12f1a31b800fa8712791dc5d2d6fc2a0c86ed Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Mon, 16 Mar 2026 17:47:01 +0100 Subject: [PATCH] added theme configuration --- src/config.rs | 48 +++++++++++++++++++++++++++++++++++++-- src/ui.rs | 62 ++++++++++++++++++++++----------------------------- 2 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/config.rs b/src/config.rs index 0773f72..8029cd2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,15 +1,59 @@ use serde::{Deserialize, Serialize}; use std::fs; +use ratatui::style::Color; #[derive(Debug, Deserialize, Serialize, Clone)] pub struct ThemeConfig { - pub name: String, + #[serde(default)] + pub transparent: bool, + pub crust: String, + pub surface0: String, + pub surface1: String, + pub text: String, + pub blue: String, + pub green: String, + pub lavender: String, + pub mauve: String, + pub peach: String, +} + +impl ThemeConfig { + fn parse_hex(hex: &str) -> Color { + let hex = hex.trim_start_matches('#'); + if hex.len() == 6 { + let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0); + let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0); + let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0); + Color::Rgb(r, g, b) + } else { + Color::Reset + } + } + + pub fn crust(&self) -> Color { Self::parse_hex(&self.crust) } + pub fn surface0(&self) -> Color { Self::parse_hex(&self.surface0) } + pub fn surface1(&self) -> Color { Self::parse_hex(&self.surface1) } + pub fn text(&self) -> Color { Self::parse_hex(&self.text) } + pub fn blue(&self) -> Color { Self::parse_hex(&self.blue) } + pub fn green(&self) -> Color { Self::parse_hex(&self.green) } + pub fn lavender(&self) -> Color { Self::parse_hex(&self.lavender) } + pub fn mauve(&self) -> Color { Self::parse_hex(&self.mauve) } + pub fn peach(&self) -> Color { Self::parse_hex(&self.peach) } } impl Default for ThemeConfig { fn default() -> Self { Self { - name: "catppuccin_mocha".to_string(), + transparent: false, + crust: "#11111b".to_string(), + surface0: "#313244".to_string(), + surface1: "#45475a".to_string(), + text: "#cdd6f4".to_string(), + blue: "#89b4fa".to_string(), + green: "#a6e3a1".to_string(), + lavender: "#b4befe".to_string(), + mauve: "#cba6f7".to_string(), + peach: "#fab387".to_string(), } } } diff --git a/src/ui.rs b/src/ui.rs index e40bba2..b35743e 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,28 +2,20 @@ use crate::app::{App, Mode}; use crate::config::Config; use ratatui::{ layout::{Constraint, Direction, Layout}, - style::{Color, Modifier, Style}, + style::{Modifier, Style}, text::{Line, Span}, widgets::{Block, BorderType, Borders, List, ListItem, ListState, Paragraph}, Frame, }; -// Catppuccin Mocha Palette -const CRUST: Color = Color::Rgb(17, 17, 27); -const SURFACE0: Color = Color::Rgb(49, 50, 68); -const SURFACE1: Color = Color::Rgb(69, 71, 90); -const TEXT: Color = Color::Rgb(205, 214, 244); -const BLUE: Color = Color::Rgb(137, 180, 250); -const GREEN: Color = Color::Rgb(166, 227, 161); -const LAVENDER: Color = Color::Rgb(180, 190, 254); -const MAUVE: Color = Color::Rgb(203, 166, 247); -const PEACH: Color = Color::Rgb(250, 179, 135); - -pub fn draw(f: &mut Frame, app: &mut App, _config: &Config) { +pub fn draw(f: &mut Frame, app: &mut App, config: &Config) { + let theme = &config.theme; let size = f.area(); // Background - f.render_widget(Block::default().style(Style::default().bg(CRUST)), size); + if !theme.transparent { + f.render_widget(Block::default().style(Style::default().bg(theme.crust())), size); + } // Main layout with horizontal padding let outer_layout = Layout::default() @@ -52,7 +44,7 @@ pub fn draw(f: &mut Frame, app: &mut App, _config: &Config) { .map(|v| v.key.len()) .max() .unwrap_or(20) - .min(40); // Cap at 40 to prevent long keys from hiding values + .min(40); // List let items: Vec = app @@ -69,27 +61,27 @@ pub fn draw(f: &mut Frame, app: &mut App, _config: &Config) { }; let key_style = if is_selected { - Style::default().fg(CRUST).add_modifier(Modifier::BOLD) + Style::default().fg(theme.crust()).add_modifier(Modifier::BOLD) } else { - Style::default().fg(LAVENDER) + Style::default().fg(theme.lavender()) }; let value_style = if is_selected { - Style::default().fg(CRUST) + Style::default().fg(theme.crust()) } else { - Style::default().fg(TEXT) + Style::default().fg(theme.text()) }; let line = Line::from(vec![ Span::styled(format!(" {: GREEN, - Mode::Normal => SURFACE1, + Mode::Insert => theme.green(), + Mode::Normal => theme.surface1(), }; let input_text = app.input.value(); let cursor_pos = app.input.visual_cursor(); let input = Paragraph::new(input_text) - .style(Style::default().fg(TEXT)) + .style(Style::default().fg(theme.text())) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) .title(input_title) - .title_style(Style::default().fg(PEACH).add_modifier(Modifier::BOLD)) + .title_style(Style::default().fg(theme.peach()).add_modifier(Modifier::BOLD)) .border_style(Style::default().fg(input_border_color)), ); f.render_widget(input, chunks[2]); @@ -149,20 +141,20 @@ pub fn draw(f: &mut Frame, app: &mut App, _config: &Config) { )); } - // Status bar (modern pill style at the bottom) + // Status bar let (mode_str, mode_style) = match app.mode { Mode::Normal => ( " NORMAL ", Style::default() - .bg(BLUE) - .fg(CRUST) + .bg(theme.blue()) + .fg(theme.crust()) .add_modifier(Modifier::BOLD), ), Mode::Insert => ( " INSERT ", Style::default() - .bg(GREEN) - .fg(CRUST) + .bg(theme.green()) + .fg(theme.crust()) .add_modifier(Modifier::BOLD), ), }; @@ -176,9 +168,9 @@ pub fn draw(f: &mut Frame, app: &mut App, _config: &Config) { let status_line = Line::from(vec![ Span::styled(mode_str, mode_style), - Span::styled(format!(" {} ", status_msg), Style::default().bg(SURFACE0).fg(TEXT)), + Span::styled(format!(" {} ", status_msg), Style::default().bg(theme.surface0()).fg(theme.text())), ]); - let status = Paragraph::new(status_line).style(Style::default().bg(SURFACE0)); + let status = Paragraph::new(status_line).style(Style::default().bg(theme.surface0())); f.render_widget(status, chunks[4]); }