magi-code 0.82.1

Repository-aware CLI coding agent for terminal work
Documentation
//! Visible-cell regressions against the former full-document ScrollView path.

use super::*;
use crate::tui::state::{
    PreviewMessage, SessionPickerRow, SessionPreview, SessionPreviewPartial, SessionPreviewState,
};
use ratatui::{Terminal, backend::TestBackend, widgets::Wrap};
use std::cell::RefCell;
use tui_scrollview::ScrollViewState;

fn draw_reference(
    frame: &mut Frame<'_>,
    area: Rect,
    text: &str,
    scroll: &RefCell<ScrollViewState>,
    theme: MissionControlTheme,
) {
    let height = usize_to_u16_saturating(crate::tui::transcript::ratatui_wrapped_visual_rows(
        text,
        scroll_view_content_width(area).max(1),
    ));
    render_scroll_view_persistent_state_with_theme(
        frame,
        area,
        height,
        Paragraph::new(text.to_owned())
            .style(theme.pane())
            .wrap(Wrap { trim: false }),
        scroll,
        theme,
    );
}

fn assert_modal_matches_reference(state: &MissionControlState, width: u16, height: u16) {
    let prompt_scroll = *state.scroll_views.system_prompt.borrow();
    let preview_scroll = *state.scroll_views.session_preview.borrow();
    let mut actual = Terminal::new(TestBackend::new(width, height)).unwrap();
    let mut expected = Terminal::new(TestBackend::new(width, height)).unwrap();
    actual
        .draw(|frame| {
            let area = frame.area();
            modals::draw_top_modal(frame, area, state);
        })
        .unwrap();
    expected
        .draw(|frame| {
            let area = frame.area();
            modals::draw_top_modal(frame, area, state);
            if let Some(prompt) = &state.modals.system_prompt_modal {
                if let Some(text_area) = system_prompt_text_area(area) {
                    draw_reference(
                        frame,
                        text_area,
                        &prompt.content,
                        &state.scroll_views.system_prompt,
                        state.theme,
                    );
                }
            } else if let Some(preview_area) = sessions_modal_preview_area(area) {
                draw_reference(
                    frame,
                    preview_area,
                    &state.session_picker_preview_text(),
                    &state.scroll_views.session_preview,
                    state.theme,
                );
            }
        })
        .unwrap();
    assert_eq!(actual.backend().buffer(), expected.backend().buffer());
    assert_eq!(*state.scroll_views.system_prompt.borrow(), prompt_scroll);
    assert_eq!(*state.scroll_views.session_preview.borrow(), preview_scroll);
}

fn large_text(label: &str) -> String {
    let mut text = String::new();
    for index in 0..400 {
        text.push_str(&format!(
            "{label}-{index:04}  keep   spaces\tand words 界 👩‍💻 e\u{301}\u{a0}together\r\n\
             \n    indented policy: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ  \n"
        ));
    }
    text.push_str("Last message line\n\n");
    text
}

#[test]
fn system_prompt_top_middle_end_and_resize_match_full_scroll_view() {
    let mut state = MissionControlState::default();
    state.open_system_prompt_modal(large_text("policy"));
    let area = system_prompt_text_area(Rect::new(0, 0, 120, 40)).unwrap();
    let width = scroll_view_content_width(area).max(1);
    assert_modal_matches_reference(&state, 120, 40);

    let middle =
        usize_to_u16_saturating(state.system_prompt_scroll_overflow(area.height, width) / 2);
    assert!(state.scroll_system_prompt_down_by(middle, area.height, width));
    assert_modal_matches_reference(&state, 120, 40);
    state
        .scroll_views
        .system_prompt
        .borrow_mut()
        .scroll_to_bottom();
    assert_modal_matches_reference(&state, 120, 40);

    // Resizing at End must clamp only the copy used for drawing, including when
    // the content pane disappears and then becomes visible again.
    for (width, height) in [(78, 24), (160, 42), (30, 10), (12, 5), (4, 2), (120, 40)] {
        assert_modal_matches_reference(&state, width, height);
    }
    state
        .scroll_views
        .system_prompt
        .borrow_mut()
        .scroll_to_top();
    assert_modal_matches_reference(&state, 120, 40);
    state.open_system_prompt_modal("replacement prompt\r\n\n".into());
    assert_modal_matches_reference(&state, 120, 40);
}

#[test]
fn session_preview_top_middle_end_resize_and_reload_match_full_scroll_view() {
    let mut state = MissionControlState::default();
    state.open_session_picker(
        vec![SessionPickerRow {
            id: "saved-session".into(),
            label: "Saved session".into(),
            is_current: false,
            preview: None,
        }],
        10,
    );
    assert_modal_matches_reference(&state, 120, 40);
    assert!(state.set_session_preview(
        "saved-session",
        SessionPreviewState::Ready(SessionPreview {
            first: PreviewMessage {
                role: "user",
                text: large_text("request"),
            },
            most_recent: Some(PreviewMessage {
                role: "assistant",
                text: large_text("response"),
            }),
        }),
    ));
    assert!(state.session_picker_focus_next());
    let area = sessions_modal_preview_area(Rect::new(0, 0, 120, 40)).unwrap();
    let width = scroll_view_content_width(area).max(1);
    assert_modal_matches_reference(&state, 120, 40);
    let middle = usize_to_u16_saturating(
        state.session_picker_preview_scroll_overflow(area.height, width) / 2,
    );
    assert!(state.session_picker_preview_down_by(middle, area.height, width));
    assert_modal_matches_reference(&state, 120, 40);
    state
        .scroll_views
        .session_preview
        .borrow_mut()
        .scroll_to_bottom();
    assert_modal_matches_reference(&state, 120, 40);
    for (width, height) in [(100, 22), (80, 20), (160, 42), (12, 5), (120, 40)] {
        assert_modal_matches_reference(&state, width, height);
    }
    state
        .scroll_views
        .session_preview
        .borrow_mut()
        .scroll_to_top();
    assert_modal_matches_reference(&state, 120, 40);

    for preview in [
        SessionPreviewState::Partial(SessionPreviewPartial {
            first: Some(PreviewMessage {
                role: "user",
                text: "short replacement\n\n".into(),
            }),
            most_recent: None,
            reason: "message limit",
        }),
        SessionPreviewState::Empty,
        SessionPreviewState::Error("unavailable".into()),
    ] {
        assert!(state.set_session_preview("saved-session", preview));
        assert_modal_matches_reference(&state, 120, 40);
    }
}

fn assert_text_viewport_matches_reference(text: &str, area: Rect, offset: u16) {
    let scroll = RefCell::new(ScrollViewState::default());
    // Stale horizontal offsets must not shift a vertically wrapped text modal.
    scroll.borrow_mut().set_offset(Position::new(3, offset));
    let before = *scroll.borrow();
    let theme = MissionControlTheme::default();
    let mut actual = Terminal::new(TestBackend::new(20, 10)).unwrap();
    let mut expected = Terminal::new(TestBackend::new(20, 10)).unwrap();
    let background = "underlying content  \n".repeat(10);
    actual
        .draw(|frame| {
            frame.render_widget(Paragraph::new(background.as_str()), frame.area());
            render_text_scroll_view_with_theme(frame, area, text, &scroll, theme);
        })
        .unwrap();
    expected
        .draw(|frame| {
            frame.render_widget(Paragraph::new(background.as_str()), frame.area());
            draw_reference(frame, area, text, &scroll, theme);
        })
        .unwrap();
    assert_eq!(actual.backend().buffer(), expected.backend().buffer());
    assert_eq!(*scroll.borrow(), before);
}

#[test]
fn text_modal_tiny_viewports_preserve_empty_lines_unicode_and_background() {
    for text in [
        "",
        "\n",
        "\n\n",
        "line\n",
        "line\n\n",
        "\r\nline\r\n\n",
        "   \t\r\n",
        "界 👩‍💻 e\u{301}  one\u{a0}two\u{200b}three\nend\n",
        "abcdefghijklmnopqrstuvwxyz0123456789",
    ] {
        for (width, height) in [(0, 4), (8, 0), (1, 1), (1, 4), (2, 4), (3, 1), (9, 5)] {
            for offset in [0, 1, u16::MAX] {
                assert_text_viewport_matches_reference(
                    text,
                    Rect::new(2, 1, width, height),
                    offset,
                );
            }
        }
    }
}

#[test]
fn text_modal_end_preserves_u16_document_height_clamp() {
    let mut text = "x\n".repeat(usize::from(u16::MAX) + 10);
    text.push_str("beyond the existing height limit");
    assert_text_viewport_matches_reference(&text, Rect::new(2, 1, 3, 4), u16::MAX);
}

#[test]
#[ignore = "manual modal scrolling workload; not physical terminal latency"]
fn text_modal_large_document_workload() {
    let text = large_text("system-prompt").repeat(4);
    let scroll = RefCell::new(ScrollViewState::default());
    let theme = MissionControlTheme::default();
    let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
    let mut viewport_times = Vec::new();
    let mut reference_times = Vec::new();
    for offset in [0, 100, u16::MAX].into_iter().cycle().take(21) {
        scroll.borrow_mut().set_offset(Position::new(0, offset));
        let start = std::time::Instant::now();
        terminal
            .draw(|frame| {
                render_text_scroll_view_with_theme(frame, frame.area(), &text, &scroll, theme);
            })
            .unwrap();
        viewport_times.push(start.elapsed().as_micros());
        let start = std::time::Instant::now();
        terminal
            .draw(|frame| {
                draw_reference(frame, frame.area(), &text, &scroll, theme);
            })
            .unwrap();
        reference_times.push(start.elapsed().as_micros());
    }
    viewport_times.sort_unstable();
    reference_times.sort_unstable();
    eprintln!(
        "text_modal_large_document frames=21 viewport_median_us={} full_reference_median_us={} width=120 height=40 physical_terminal=false",
        viewport_times[10], reference_times[10],
    );
}