laziergit 1.1.0

an even lazier git implementation than lazygit
use crate::app::{App, Banner};
use crate::theme;
use ratatui::{
    Frame,
    layout::{Alignment, Constraint, Layout, Rect},
    style::{Modifier, Style},
    text::{Line, Span},
    widgets::Paragraph,
};

const CHIPS: &[(&str, &str)] = &[
    ("j/k", "move"),
    ("space", "stage"),
    ("enter", "open"),
    ("c", "commit"),
    ("p", "pull"),
    ("P", "push"),
    ("b", "branch"),
    ("?", "help"),
];

pub fn header(f: &mut Frame, area: Rect, app: &App) {
    let bg = Style::new().bg(theme::HEADER_BG);
    let banner = app.banner();
    let banner_width = banner
        .as_ref()
        .map(|b| (b.text().chars().count() as u16 + 1).min(area.width / 2))
        .unwrap_or(0);
    let [left, right] =
        Layout::horizontal([Constraint::Min(0), Constraint::Length(banner_width)]).areas(area);

    let branch = &app.status.branch;
    let mut spans = vec![Span::styled(
        format!(" {} ", branch.name),
        Style::new()
            .fg(theme::BASE)
            .bg(theme::ACCENT)
            .add_modifier(Modifier::BOLD),
    )];
    if branch.ahead > 0 || branch.behind > 0 {
        spans.push(Span::styled(
            format!("{}{}", branch.ahead, branch.behind),
            Style::new().fg(theme::MUTED),
        ));
    }
    f.render_widget(Paragraph::new(Line::from(spans)).style(bg), left);

    if let Some(banner) = banner {
        let color = match banner {
            Banner::Busy(_) => theme::ACCENT,
            Banner::Ok(_) => theme::GREEN,
            Banner::Error(_) => theme::RED,
        };
        f.render_widget(
            Paragraph::new(Line::styled(
                banner.text().to_string(),
                Style::new().fg(color),
            ))
            .alignment(Alignment::Right)
            .style(bg),
            right,
        );
    }
}

pub fn footer(f: &mut Frame, area: Rect, app: &App) {
    let bg = Style::new().bg(theme::HEADER_BG);
    if let Some(pending) = app.pending_label() {
        f.render_widget(
            Paragraph::new(Line::styled(
                format!(" {pending}"),
                Style::new().fg(theme::ACCENT).add_modifier(Modifier::BOLD),
            ))
            .style(bg),
            area,
        );
        return;
    }
    let chip = Style::new()
        .fg(theme::ACCENT)
        .bg(theme::SURFACE)
        .add_modifier(Modifier::BOLD);
    let mut spans = vec![Span::raw(" ")];
    for (key, label) in CHIPS {
        spans.push(Span::styled(format!(" {key} "), chip));
        spans.push(Span::styled(
            format!(" {label}  "),
            Style::new().fg(theme::MUTED),
        ));
    }
    f.render_widget(Paragraph::new(Line::from(spans)).style(bg), area);
}