debtmap 0.17.0

Code complexity and technical debt analyzer
Documentation
//! Color themes and styling for TUI components.

use ratatui::style::{Color, Modifier, Style};

/// Zen minimalist color scheme for debtmap TUI
pub struct Theme {
    /// Primary accent color (cyan for active elements)
    pub primary: Color,
    /// Success color (green for completed elements)
    pub success: Color,
    /// Muted color (dark gray for pending/inactive elements)
    pub muted: Color,
    /// Text color (white for normal text)
    pub text: Color,
    /// Background color (black/default)
    pub background: Color,
}

impl Theme {
    /// Create the default zen minimalist theme
    pub fn default_theme() -> Self {
        Self {
            primary: Color::Cyan,
            success: Color::Green,
            muted: Color::DarkGray,
            text: Color::White,
            background: Color::Reset,
        }
    }

    /// Accent color (alias for primary)
    pub fn accent(&self) -> Color {
        self.primary
    }

    /// Secondary color (alias for success)
    pub fn secondary(&self) -> Color {
        self.success
    }

    /// Success color (green for positive status messages)
    pub fn success(&self) -> Color {
        self.success
    }

    /// Warning color (yellow for warnings and errors)
    pub fn warning(&self) -> Color {
        Color::Yellow
    }

    /// Style for completed stage markers (✓)
    pub fn completed_style(&self) -> Style {
        Style::default().fg(self.success)
    }

    /// Style for active stage markers (▸)
    pub fn active_style(&self) -> Style {
        Style::default()
            .fg(self.primary)
            .add_modifier(Modifier::BOLD)
    }

    /// Style for pending stage markers (·)
    pub fn pending_style(&self) -> Style {
        Style::default().fg(self.muted)
    }

    /// Style for stage names (based on status)
    pub fn stage_name_style(&self, is_active: bool) -> Style {
        if is_active {
            Style::default()
                .fg(self.primary)
                .add_modifier(Modifier::BOLD)
        } else {
            Style::default().fg(self.text)
        }
    }

    /// Style for metrics and statistics
    pub fn metric_style(&self) -> Style {
        Style::default().fg(self.muted)
    }

    /// Style for progress bars
    pub fn progress_bar_style(&self) -> Style {
        Style::default().fg(self.primary)
    }

    /// Style for progress bar background
    pub fn progress_bar_bg_style(&self) -> Style {
        Style::default().fg(self.muted)
    }

    /// Style for dotted leaders
    pub fn dotted_leader_style(&self) -> Style {
        Style::default().fg(self.muted)
    }

    /// Style for animated arrows
    pub fn arrow_style(&self) -> Style {
        Style::default().fg(self.primary)
    }

    /// Style for elapsed time
    pub fn time_style(&self) -> Style {
        Style::default().fg(self.muted)
    }

    /// Get primary accent color (Spec 205)
    pub fn primary_color(&self) -> Color {
        self.primary
    }

    /// Get muted/dim color (Spec 205)
    pub fn muted_color(&self) -> Color {
        self.muted
    }

    /// Color for coupling classification badge (spec 203)
    ///
    /// Returns appropriate color based on classification type:
    /// - Stable Core: Green (healthy core module)
    /// - Utility Module: Cyan (normal utility)
    /// - Leaf Module: Yellow (attention - peripheral)
    /// - Isolated: Gray (neutral - low coupling)
    /// - Highly Coupled: Red (warning - needs refactoring)
    pub fn coupling_classification_color(&self, classification: &str) -> Color {
        match classification.to_lowercase().as_str() {
            "stable core" => Color::Green,
            "utility module" => Color::Cyan,
            "leaf module" => Color::Yellow,
            "isolated" => Color::DarkGray,
            "highly coupled" => Color::Red,
            _ => Color::White,
        }
    }

    /// Color for instability gradient (spec 203)
    ///
    /// Returns a color on the green-yellow-red gradient based on instability value:
    /// - 0.0 (stable): Green
    /// - 0.5 (balanced): Yellow
    /// - 1.0 (unstable): Red
    pub fn instability_color(&self, instability: f64) -> Color {
        if instability < 0.3 {
            Color::Green
        } else if instability < 0.7 {
            Color::Yellow
        } else {
            Color::Red
        }
    }

    /// Style for coupling classification badge (spec 203)
    pub fn coupling_badge_style(&self, classification: &str) -> Style {
        Style::default()
            .fg(self.coupling_classification_color(classification))
            .add_modifier(Modifier::BOLD)
    }
}

impl Default for Theme {
    fn default() -> Self {
        Self::default_theme()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_theme_creation() {
        let theme = Theme::default_theme();
        assert_eq!(theme.primary, Color::Cyan);
        assert_eq!(theme.success, Color::Green);
        assert_eq!(theme.muted, Color::DarkGray);
    }

    #[test]
    fn test_style_consistency() {
        let theme = Theme::default_theme();
        let completed = theme.completed_style();
        let active = theme.active_style();

        // Verify colors are distinct
        assert_ne!(completed.fg, active.fg);
    }

    // Tests for spec 203: Coupling visualization colors

    #[test]
    fn test_coupling_classification_colors() {
        let theme = Theme::default_theme();

        assert_eq!(
            theme.coupling_classification_color("stable core"),
            Color::Green
        );
        assert_eq!(
            theme.coupling_classification_color("Stable Core"),
            Color::Green
        );
        assert_eq!(
            theme.coupling_classification_color("utility module"),
            Color::Cyan
        );
        assert_eq!(
            theme.coupling_classification_color("leaf module"),
            Color::Yellow
        );
        assert_eq!(
            theme.coupling_classification_color("isolated"),
            Color::DarkGray
        );
        assert_eq!(
            theme.coupling_classification_color("highly coupled"),
            Color::Red
        );
        assert_eq!(theme.coupling_classification_color("unknown"), Color::White);
    }

    #[test]
    fn test_instability_color_gradient() {
        let theme = Theme::default_theme();

        // Stable (low instability) = green
        assert_eq!(theme.instability_color(0.0), Color::Green);
        assert_eq!(theme.instability_color(0.2), Color::Green);

        // Balanced (mid instability) = yellow
        assert_eq!(theme.instability_color(0.3), Color::Yellow);
        assert_eq!(theme.instability_color(0.5), Color::Yellow);
        assert_eq!(theme.instability_color(0.69), Color::Yellow);

        // Unstable (high instability) = red
        assert_eq!(theme.instability_color(0.7), Color::Red);
        assert_eq!(theme.instability_color(1.0), Color::Red);
    }

    #[test]
    fn test_coupling_badge_style_has_bold() {
        let theme = Theme::default_theme();
        let style = theme.coupling_badge_style("stable core");

        assert!(style.add_modifier.contains(Modifier::BOLD));
        assert_eq!(style.fg, Some(Color::Green));
    }
}