use cm_dashboard_shared::Status; use ratatui::style::{Color, Modifier, Style}; use ratatui::widgets::{Block, Borders}; /// Complete terminal color palette matching your configuration #[allow(dead_code)] pub struct TerminalColors { // Primary colors pub foreground: Color, pub dim_foreground: Color, pub bright_foreground: Color, pub background: Color, // Normal colors pub normal_black: Color, pub normal_red: Color, pub normal_green: Color, pub normal_yellow: Color, pub normal_blue: Color, pub normal_magenta: Color, pub normal_cyan: Color, pub normal_white: Color, // Bright colors pub bright_black: Color, pub bright_red: Color, pub bright_green: Color, pub bright_yellow: Color, pub bright_blue: Color, pub bright_magenta: Color, pub bright_cyan: Color, pub bright_white: Color, // Dim colors pub dim_black: Color, pub dim_red: Color, pub dim_green: Color, pub dim_yellow: Color, pub dim_blue: Color, pub dim_magenta: Color, pub dim_cyan: Color, pub dim_white: Color, } impl Default for TerminalColors { fn default() -> Self { Self { // Primary colors foreground: Color::Rgb(198, 198, 198), // #c6c6c6 dim_foreground: Color::Rgb(112, 112, 112), // #707070 bright_foreground: Color::Rgb(255, 255, 255), // #ffffff background: Color::Rgb(38, 38, 38), // #262626 // Normal colors normal_black: Color::Rgb(0, 0, 0), // #000000 normal_red: Color::Rgb(215, 84, 0), // #d75400 normal_green: Color::Rgb(175, 215, 135), // #afd787 normal_yellow: Color::Rgb(215, 175, 95), // #d7af5f normal_blue: Color::Rgb(135, 175, 215), // #87afd7 normal_magenta: Color::Rgb(215, 215, 175), // #d7d7af normal_cyan: Color::Rgb(160, 160, 160), // #a0a0a0 normal_white: Color::Rgb(238, 238, 238), // #eeeeee // Bright colors bright_black: Color::Rgb(48, 48, 48), // #303030 bright_red: Color::Rgb(215, 84, 0), // #d75400 bright_green: Color::Rgb(175, 215, 135), // #afd787 bright_yellow: Color::Rgb(215, 175, 95), // #d7af5f bright_blue: Color::Rgb(135, 175, 215), // #87afd7 bright_magenta: Color::Rgb(215, 215, 175), // #d7d7af bright_cyan: Color::Rgb(160, 160, 160), // #a0a0a0 bright_white: Color::Rgb(255, 255, 255), // #ffffff // Dim colors dim_black: Color::Rgb(0, 0, 0), // #000000 dim_red: Color::Rgb(215, 84, 0), // #d75400 dim_green: Color::Rgb(175, 215, 135), // #afd787 dim_yellow: Color::Rgb(215, 175, 95), // #d7af5f dim_blue: Color::Rgb(135, 175, 215), // #87afd7 dim_magenta: Color::Rgb(215, 215, 175), // #d7d7af dim_cyan: Color::Rgb(160, 160, 160), // #a0a0a0 dim_white: Color::Rgb(221, 221, 221), // #dddddd } } } /// Comprehensive theming engine for dashboard consistency pub struct Theme; #[allow(dead_code)] impl Theme { fn colors() -> &'static TerminalColors { static COLORS: std::sync::OnceLock = std::sync::OnceLock::new(); COLORS.get_or_init(TerminalColors::default) } // Semantic color mapping using the terminal color struct pub fn primary_text() -> Color { Self::colors().normal_white } pub fn secondary_text() -> Color { Self::colors().foreground } pub fn muted_text() -> Color { Self::colors().dim_foreground } pub fn border() -> Color { Self::colors().dim_foreground } pub fn border_title() -> Color { Self::colors().bright_white } pub fn background() -> Color { Self::colors().background } pub fn success() -> Color { Self::colors().normal_green } pub fn warning() -> Color { Self::colors().normal_yellow } pub fn error() -> Color { Self::colors().normal_red } pub fn info() -> Color { Self::colors().normal_cyan } pub fn highlight() -> Color { Self::colors().normal_blue } /// Get color for status level pub fn status_color(status: Status) -> Color { match status { Status::Ok => Self::success(), Status::Inactive => Self::muted_text(), // Gray for inactive services in service list Status::Pending => Self::highlight(), // Blue for pending Status::Warning => Self::warning(), Status::Critical => Self::error(), Status::Unknown => Self::muted_text(), Status::Offline => Self::muted_text(), // Dark gray for offline } } /// Get style for status level pub fn status_style(status: Status) -> Style { Style::default().fg(Self::status_color(status)) } /// CPU usage colors using terminal color struct pub fn cpu_color(percentage: u16) -> Color { match percentage { 0..=25 => Self::colors().normal_green, // Low usage 26..=50 => Self::colors().normal_yellow, // Medium usage 51..=75 => Self::colors().normal_magenta, // High usage 76..=100 => Self::colors().normal_red, // Critical usage _ => Self::colors().normal_red, // Over 100% } } /// Memory usage colors using terminal color struct pub fn memory_color(percentage: u16) -> Color { match percentage { 0..=60 => Self::colors().normal_green, // Low usage 61..=80 => Self::colors().normal_yellow, // Medium usage 81..=95 => Self::colors().normal_magenta, // High usage 96..=100 => Self::colors().normal_red, // Critical usage _ => Self::colors().normal_red, // Over 100% } } /// Get gauge color based on percentage pub fn gauge_color(percentage: u16, warning_threshold: u16, critical_threshold: u16) -> Color { if percentage >= critical_threshold { Self::error() } else if percentage >= warning_threshold { Self::warning() } else { Self::success() } } /// Widget border style pub fn widget_border_style() -> Style { Style::default().fg(Self::border()).bg(Self::background()) } /// Inactive widget border style pub fn widget_border_inactive_style() -> Style { Style::default() .fg(Self::muted_text()) .bg(Self::background()) } /// Title style pub fn title_style() -> Style { Style::default() .fg(Self::border_title()) .bg(Self::background()) } /// Status bar style pub fn status_bar_style() -> Style { Style::default() .fg(Self::muted_text()) .bg(Self::background()) } } /// Layout and spacing constants pub struct Layout; impl Layout { /// Left panel percentage (system + backup) pub const LEFT_PANEL_WIDTH: u16 = 45; /// Right panel percentage (services) pub const RIGHT_PANEL_WIDTH: u16 = 55; } /// Typography system pub struct Typography; /// Component styling system pub struct Components; /// Status icons and styling pub struct StatusIcons; impl StatusIcons { /// Get status icon symbol pub fn get_icon(status: Status) -> &'static str { match status { Status::Ok => "●", Status::Inactive => "○", // Empty circle for inactive services Status::Pending => "◉", // Hollow circle for pending Status::Warning => "◐", Status::Critical => "!", Status::Unknown => "?", Status::Offline => "○", // Empty circle for offline } } /// Create spans with status icon colored and text in foreground color pub fn create_status_spans(status: Status, text: &str) -> Vec> { let icon = Self::get_icon(status); let status_color = match status { Status::Ok => Theme::success(), // Green Status::Inactive => Theme::muted_text(), // Gray for inactive services Status::Pending => Theme::highlight(), // Blue Status::Warning => Theme::warning(), // Yellow Status::Critical => Theme::error(), // Red Status::Unknown => Theme::muted_text(), // Gray Status::Offline => Theme::muted_text(), // Dark gray for offline }; vec![ ratatui::text::Span::styled( format!("{} ", icon), Style::default().fg(status_color).bg(Theme::background()), ), ratatui::text::Span::styled( text.to_string(), Style::default() .fg(Theme::secondary_text()) .bg(Theme::background()), ), ] } } impl Components { /// Standard widget block with title using bright foreground for title pub fn widget_block(title: &str) -> Block<'_> { Block::default() .title(title) .borders(Borders::ALL) .style(Style::default().fg(Theme::border()).bg(Theme::background())) .title_style( Style::default() .fg(Theme::border_title()) .bg(Theme::background()), ) } } impl Typography { /// Widget title style (panel headers) - bold bright white pub fn widget_title() -> Style { Style::default() .fg(Color::White) .bg(Theme::background()) .add_modifier(Modifier::BOLD) } /// Secondary content text pub fn secondary() -> Style { Style::default() .fg(Theme::secondary_text()) .bg(Theme::background()) } /// Muted text (inactive items, placeholders) - now bold bright white for headers pub fn muted() -> Style { Style::default() .fg(Color::White) .bg(Theme::background()) .add_modifier(Modifier::BOLD) } /// Tree symbols style (blue color) pub fn tree() -> Style { Style::default() .fg(Theme::highlight()) .bg(Theme::background()) } }