magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::theme::MissionControlTheme;
use ratatui::{
    Frame,
    layout::Rect,
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, BorderType, Borders, Clear},
};

/// Normal modals stay within 30–80% of each available dimension.
pub(crate) fn modal_area(area: Rect, width_percent: u16, height_percent: u16) -> Rect {
    compact_modal_area(
        area,
        width_percent.clamp(30, 80),
        height_percent.clamp(30, 80),
    )
}

/// Explicit exception for compact dialogs; percentages still cannot exceed the parent.
pub(crate) fn compact_modal_area(area: Rect, width_percent: u16, height_percent: u16) -> Rect {
    let width = (u32::from(area.width) * u32::from(width_percent.min(100)) / 100) as u16;
    let height = (u32::from(area.height) * u32::from(height_percent.min(100)) / 100) as u16;
    Rect::new(
        area.x.saturating_add((area.width - width) / 2),
        area.y.saturating_add((area.height - height) / 2),
        width,
        height,
    )
}

fn close_control_label(hint: &str) -> Line<'_> {
    Line::from(vec![Span::raw(hint), Span::raw(" [X]")]).right_aligned()
}

/// Matches the visible, right-aligned control inside the top border.
pub(crate) fn close_control_area(area: Rect, hint: Option<&str>) -> Option<Rect> {
    let width = close_control_label(hint?)
        .width()
        .min(usize::from(area.width.saturating_sub(2))) as u16;
    (width > 0 && area.height > 1)
        .then(|| Rect::new(area.right().saturating_sub(width + 1), area.y, width, 1))
}

pub(crate) fn close_control_clicked(
    mouse: crossterm::event::MouseEvent,
    control: Option<Rect>,
) -> bool {
    mouse.kind == crossterm::event::MouseEventKind::Down(crossterm::event::MouseButton::Left)
        && control.is_some_and(|area| area.contains((mouse.column, mouse.row).into()))
}

/// `close_hint` is None when closing is disabled, or a truthful custom hint.
pub(crate) fn modal_block<'a>(
    title: impl Into<Line<'a>>,
    hotkeys: impl Into<Line<'a>>,
    theme: MissionControlTheme,
    close_hint: Option<&'a str>,
) -> Block<'a> {
    let mut block = Block::default()
        .borders(Borders::ALL)
        .border_type(BorderType::Plain)
        .style(theme.app())
        .title_top(title.into().left_aligned())
        .title_bottom(hotkeys.into().left_aligned());
    if let Some(hint) = close_hint {
        block = block.title_top(close_control_label(hint));
    }
    block
}

/// Paints a thick black surround and shell, returning its unpadded content rectangle.
pub(crate) fn draw_modal_container<'a>(
    frame: &mut Frame<'_>,
    area: Rect,
    title: impl Into<Line<'a>>,
    hotkeys: impl Into<Line<'a>>,
    theme: MissionControlTheme,
    close_hint: Option<&'a str>,
) -> Rect {
    let close_hint = close_hint.filter(|_| area.height > 1);
    let block = modal_block(title, hotkeys, theme, close_hint);
    let inner = block.inner(area);
    if !area.is_empty() {
        let left = area.x.saturating_sub(2);
        let top = area.y.saturating_sub(1);
        let surround = Rect::new(
            left,
            top,
            area.right().saturating_add(2).saturating_sub(left),
            area.bottom().saturating_add(1).saturating_sub(top),
        )
        .intersection(frame.area());
        frame.render_widget(Clear, surround);
        frame.render_widget(
            Block::default().style(Style::default().bg(Color::Rgb(0, 0, 0))),
            surround,
        );
    }
    frame.render_widget(Clear, area);
    frame.render_widget(block, area);
    inner
}

/// Selected Settings-style tab; inactive tabs (including future steps) stay muted.
pub(crate) fn modal_tab(label: &str, selected: bool, theme: MissionControlTheme) -> Span<'static> {
    if selected {
        Span::styled(
            format!("[{label}]"),
            Style::default()
                .fg(theme.text_accent())
                .bg(theme.surface_selection())
                .add_modifier(Modifier::BOLD),
        )
    } else {
        Span::styled(format!(" {label} "), theme.muted(false))
    }
}