crabcode 0.0.1

(WIP) Rust AI CLI Coding Agent with a beautiful terminal UI
use ratatui::{
    layout::Rect,
    style::{Color, Modifier, Style},
    text::{Line, Span},
    Frame,
};

pub struct StatusBar {
    pub version: String,
    pub cwd: String,
    pub branch: Option<String>,
    pub agent: String,
    pub model: String,
}

impl StatusBar {
    pub fn new(
        version: String,
        cwd: String,
        branch: Option<String>,
        agent: String,
        model: String,
    ) -> Self {
        Self {
            version,
            cwd,
            branch,
            agent,
            model,
        }
    }

    pub fn render(&self, f: &mut Frame, area: Rect) {
        let cwd_with_tilde = if let Some(home) = std::env::var_os("HOME") {
            let home_str = home.to_string_lossy();
            if self.cwd.starts_with(&*home_str) {
                format!("~{}", &self.cwd[home_str.len()..])
            } else {
                self.cwd.clone()
            }
        } else {
            self.cwd.clone()
        };
        let cwd_display = if cwd_with_tilde.len() > 30 {
            format!("...{}", &cwd_with_tilde[cwd_with_tilde.len() - 27..])
        } else {
            cwd_with_tilde
        };
        let mut left_spans = vec![Span::raw(cwd_display)];

        if let Some(ref branch) = self.branch {
            left_spans.push(Span::raw(" ("));
            left_spans.push(Span::styled(
                branch,
                Style::default().fg(Color::Rgb(255, 140, 0)),
            ));
            left_spans.push(Span::raw(")"));
        }

        let right_spans = vec![Span::styled(
            &self.version,
            Style::default().add_modifier(Modifier::DIM),
        )];

        let line = Line::from(left_spans);
        f.render_widget(line, area);

        let version_width = self.version.len() as u16;
        let right_area = Rect {
            x: area.x + area.width.saturating_sub(version_width + 1),
            y: area.y,
            width: version_width,
            height: 1,
        };
        let version_line = Line::from(right_spans);
        f.render_widget(version_line, right_area);
    }
}

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

    #[test]
    fn test_status_bar_creation() {
        let status_bar = StatusBar::new(
            "0.1.0".to_string(),
            "/home/user/projects/crabcode".to_string(),
            Some("main".to_string()),
            "PLAN".to_string(),
            "nano-gpt".to_string(),
        );
        assert_eq!(status_bar.version, "0.1.0");
        assert_eq!(status_bar.cwd, "/home/user/projects/crabcode");
        assert_eq!(status_bar.branch, Some("main".to_string()));
        assert_eq!(status_bar.agent, "PLAN");
        assert_eq!(status_bar.model, "nano-gpt");
    }

    #[test]
    fn test_status_bar_no_branch() {
        let status_bar = StatusBar::new(
            "0.1.0".to_string(),
            "/home/user/projects/crabcode".to_string(),
            None,
            "BUILD".to_string(),
            "z-ai".to_string(),
        );
        assert_eq!(status_bar.branch, None);
        assert_eq!(status_bar.agent, "BUILD");
        assert_eq!(status_bar.model, "z-ai");
    }

    #[test]
    fn test_status_bar_empty_branch() {
        let status_bar = StatusBar::new(
            "0.1.0".to_string(),
            "/home/user/projects/crabcode".to_string(),
            None,
            "PLAN".to_string(),
            "nano-gpt".to_string(),
        );
        assert!(status_bar.branch.is_none());
    }
}