diff --git a/codex-rs/tui/src/pager_overlay.rs b/codex-rs/tui/src/pager_overlay.rs index 271674b3..b92939a1 100644 --- a/codex-rs/tui/src/pager_overlay.rs +++ b/codex-rs/tui/src/pager_overlay.rs @@ -18,7 +18,9 @@ use ratatui::style::Stylize; use ratatui::text::Line; use ratatui::text::Span; use ratatui::text::Text; +use ratatui::widgets::Clear; use ratatui::widgets::Paragraph; +use ratatui::widgets::Widget; use ratatui::widgets::WidgetRef; pub(crate) enum Overlay { @@ -98,6 +100,7 @@ impl PagerView { } fn render(&mut self, area: Rect, buf: &mut Buffer) { + Clear.render(area, buf); self.render_header(area, buf); let content_area = self.scroll_area(area); self.update_last_content_height(content_area.height); @@ -139,6 +142,7 @@ impl PagerView { // Removed unused render_content_page (replaced by render_content_page_prepared) fn render_content_page_prepared(&self, area: Rect, buf: &mut Buffer, page: &[Line<'static>]) { + Clear.render(area, buf); Paragraph::new(page.to_vec()).render_ref(area, buf); let visible = page.len(); @@ -547,6 +551,17 @@ impl StaticOverlay { mod tests { use super::*; use insta::assert_snapshot; + use std::collections::HashMap; + use std::path::PathBuf; + use std::sync::Arc; + use std::time::Duration; + + use crate::history_cell::CommandOutput; + use crate::history_cell::HistoryCell; + use crate::history_cell::PatchEventType; + use crate::history_cell::new_patch_event; + use codex_core::protocol::FileChange; + use codex_protocol::parse_command::ParsedCommand; use ratatui::Terminal; use ratatui::backend::TestBackend; @@ -610,6 +625,99 @@ mod tests { assert_snapshot!(term.backend()); } + fn buffer_to_text(buf: &Buffer, area: Rect) -> String { + let mut out = String::new(); + for y in area.y..area.bottom() { + for x in area.x..area.right() { + let symbol = buf[(x, y)].symbol(); + if symbol.is_empty() { + out.push(' '); + } else { + out.push(symbol.chars().next().unwrap_or(' ')); + } + } + // Trim trailing spaces for stability. + while out.ends_with(' ') { + out.pop(); + } + out.push('\n'); + } + out + } + + #[test] + fn transcript_overlay_apply_patch_scroll_vt100_clears_previous_page() { + let cwd = PathBuf::from("/repo"); + let mut cells: Vec> = Vec::new(); + + let mut approval_changes = HashMap::new(); + approval_changes.insert( + PathBuf::from("foo.txt"), + FileChange::Add { + content: "hello\nworld\n".to_string(), + }, + ); + let approval_cell: Arc = Arc::new(new_patch_event( + PatchEventType::ApprovalRequest, + approval_changes, + &cwd, + )); + cells.push(approval_cell); + + let mut apply_changes = HashMap::new(); + apply_changes.insert( + PathBuf::from("foo.txt"), + FileChange::Add { + content: "hello\nworld\n".to_string(), + }, + ); + let apply_begin_cell: Arc = Arc::new(new_patch_event( + PatchEventType::ApplyBegin { + auto_approved: false, + }, + apply_changes, + &cwd, + )); + cells.push(apply_begin_cell); + + let apply_end_cell: Arc = + Arc::new(crate::history_cell::new_user_approval_decision(vec![ + "✓ Patch applied".green().bold().into(), + "src/foo.txt".dim().into(), + ])); + cells.push(apply_end_cell); + + let mut exec_cell = crate::history_cell::new_active_exec_command( + "exec-1".into(), + vec!["bash".into(), "-lc".into(), "ls".into()], + vec![ParsedCommand::Unknown { cmd: "ls".into() }], + ); + exec_cell.complete_call( + "exec-1", + CommandOutput { + exit_code: 0, + stdout: "src\nREADME.md\n".into(), + stderr: String::new(), + formatted_output: "src\nREADME.md\n".into(), + }, + Duration::from_millis(420), + ); + let exec_cell: Arc = Arc::new(exec_cell); + cells.push(exec_cell); + + let mut overlay = TranscriptOverlay::new(cells); + let area = Rect::new(0, 0, 80, 12); + let mut buf = Buffer::empty(area); + + overlay.render(area, &mut buf); + overlay.view.scroll_offset = 0; + overlay.view.wrap_cache = None; + overlay.render(area, &mut buf); + + let snapshot = buffer_to_text(&buf, area); + assert_snapshot!("transcript_overlay_apply_patch_scroll_vt100", snapshot); + } + #[test] fn transcript_overlay_keeps_scroll_pinned_at_bottom() { let mut overlay = TranscriptOverlay::new( diff --git a/codex-rs/tui/src/snapshots/codex_tui__pager_overlay__tests__transcript_overlay_apply_patch_scroll_vt100.snap b/codex-rs/tui/src/snapshots/codex_tui__pager_overlay__tests__transcript_overlay_apply_patch_scroll_vt100.snap new file mode 100644 index 00000000..725f210c --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__pager_overlay__tests__transcript_overlay_apply_patch_scroll_vt100.snap @@ -0,0 +1,16 @@ +--- +source: tui/src/pager_overlay.rs +assertion_line: 721 +expression: snapshot +--- +/ T R A N S C R I P T / / / / / / / / / / / / / / / / / / / / / / / / / / / / / +• Proposed Change foo.txt (+2 -0) + 1 +hello + 2 +world + +• Change Approved foo.txt (+2 -0) + +✓ Patch applied +─────────────────────────────────────────────────────────────────────────── 0% ─ + ↑/↓ scroll PgUp/PgDn page Home/End jump + q quit Esc edit prev