magi-code 0.82.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,
    )
}

/// `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(Line::from(hint).right_aligned());
    }
    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 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))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ratatui::{Terminal, backend::TestBackend};

    #[test]
    fn normal_geometry_clamps_and_centers_in_offset_parent() {
        let parent = Rect::new(10, 20, 100, 100);
        assert_eq!(modal_area(parent, 60, 60), Rect::new(30, 40, 60, 60));
        assert_eq!(modal_area(parent, 0, 100), Rect::new(45, 30, 30, 80));
        assert_eq!(
            compact_modal_area(parent, 40, 20),
            Rect::new(40, 60, 40, 20)
        );
        for width in 0..12 {
            for height in 0..12 {
                let parent = Rect::new(3, 5, width, height);
                let modal = modal_area(parent, 60, 60);
                assert_eq!(modal.intersection(parent), modal);
            }
        }
    }

    #[test]
    fn surround_clears_all_sides_and_clips_at_terminal_edges() {
        let theme = MissionControlTheme::default();
        let mut terminal = Terminal::new(TestBackend::new(20, 10)).unwrap();
        for area in [
            Rect::new(3, 2, 10, 5),
            Rect::new(10, 5, 10, 5),
            Rect::new(0, 0, 10, 5),
            Rect::new(1, 0, 10, 5),
            Rect::new(3, 2, 0, 5),
            Rect::new(3, 2, 10, 0),
        ] {
            terminal
                .draw(|frame| {
                    for cell in &mut frame.buffer_mut().content {
                        cell.set_symbol("x")
                            .set_bg(Color::Blue)
                            .set_style(Style::default().add_modifier(Modifier::REVERSED));
                    }
                    let inner = draw_modal_container(frame, area, "", "", theme, None);
                    assert_eq!(inner, modal_block("", "", theme, None).inner(area));
                })
                .unwrap();
            let buffer = terminal.backend().buffer();
            for y in 0..10 {
                for x in 0..20 {
                    let position = (x, y).into();
                    if area.contains(position) {
                        assert_eq!(buffer[(x, y)].bg, theme.app().bg.unwrap());
                    } else if !area.is_empty()
                        && x >= area.x.saturating_sub(2)
                        && x < area.right() + 2
                        && y >= area.y.saturating_sub(1)
                        && y < area.bottom() + 1
                    {
                        assert_eq!(buffer[(x, y)].symbol(), " ");
                        assert_eq!(buffer[(x, y)].bg, Color::Rgb(0, 0, 0));
                        assert_eq!(buffer[(x, y)].modifier, Modifier::empty());
                    } else {
                        assert_eq!(buffer[(x, y)].symbol(), "x");
                        assert_eq!(buffer[(x, y)].bg, Color::Blue);
                    }
                }
            }
        }
    }

    #[test]
    fn shell_places_chrome_on_borders_and_clears_content() {
        let theme = MissionControlTheme::default();
        let mut terminal = Terminal::new(TestBackend::new(50, 12)).unwrap();
        for close_hint in [Some("[Esc] close"), None] {
            terminal
                .draw(|frame| {
                    draw_modal_container(
                        frame,
                        frame.area(),
                        "Picker",
                        "[Enter] select",
                        theme,
                        close_hint,
                    );
                })
                .unwrap();
            let buffer = terminal.backend().buffer();
            let row = |y| (0..50).map(|x| buffer[(x, y)].symbol()).collect::<String>();
            assert!(row(0).starts_with("┌Picker"));
            assert_eq!(row(0).contains("[Esc] close"), close_hint.is_some());
            assert!(row(11).contains("[Enter] select"));
            assert!(!row(1).contains("select"));
            assert_eq!(buffer[(25, 5)].bg, theme.app().bg.unwrap());
        }
    }
}