From e15e191ff76c17ac6045c6e9df9a725372ffa590 Mon Sep 17 00:00:00 2001 From: Charley Cunningham Date: Thu, 5 Mar 2026 16:51:40 -0800 Subject: [PATCH] fix(tui): clean up pending steer preview wrapping (#13642) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - render pending steer previews with a single `pending steer:` prefix instead of repeating it for each source line - reuse the same truncation path for pending steers and queued drafts so multiline previews behave consistently - add snapshot coverage for the multiline pending steer case Before Screenshot 2026-03-05 at 3 55 11 PM After Screenshot 2026-03-05 at 3 56 08 PM ## Codex author `codex resume 019cc054-385e-79a3-bb85-ec9499623bd8` Co-authored-by: Codex --- .../src/bottom_pane/pending_input_preview.rs | 58 +++++++++++++------ ...teer_uses_single_prefix_and_truncates.snap | 23 ++++++++ 2 files changed, 62 insertions(+), 19 deletions(-) create mode 100644 codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_multiline_pending_steer_uses_single_prefix_and_truncates.snap diff --git a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs index da7bc8cb6..c52b16c14 100644 --- a/codex-rs/tui/src/bottom_pane/pending_input_preview.rs +++ b/codex-rs/tui/src/bottom_pane/pending_input_preview.rs @@ -25,6 +25,8 @@ pub(crate) struct PendingInputPreview { edit_binding: key_hint::KeyBinding, } +const PREVIEW_LINE_LIMIT: usize = 3; + impl PendingInputPreview { pub(crate) fn new() -> Self { Self { @@ -41,6 +43,18 @@ impl PendingInputPreview { self.edit_binding = binding; } + fn push_truncated_preview_lines( + lines: &mut Vec>, + wrapped: Vec>, + overflow_line: Line<'static>, + ) { + let wrapped_len = wrapped.len(); + lines.extend(wrapped.into_iter().take(PREVIEW_LINE_LIMIT)); + if wrapped_len > PREVIEW_LINE_LIMIT { + lines.push(overflow_line); + } + } + fn as_renderable(&self, width: u16) -> Box { if (self.pending_steers.is_empty() && self.queued_messages.is_empty()) || width < 4 { return Box::new(()); @@ -50,36 +64,26 @@ impl PendingInputPreview { for steer in &self.pending_steers { let wrapped = adaptive_wrap_lines( - steer - .lines() - .map(|line| format!("pending steer: {line}").dim()), + steer.lines().map(|line| Line::from(line.dim())), RtOptions::new(width as usize) - .initial_indent(Line::from(" ! ".dim())) + .initial_indent(Line::from(" ! pending steer: ".dim())) .subsequent_indent(Line::from(" ")), ); - let len = wrapped.len(); - for line in wrapped.into_iter().take(3) { - lines.push(line); - } - if len > 3 { - lines.push(Line::from(" …".dim())); - } + Self::push_truncated_preview_lines(&mut lines, wrapped, Line::from(" …".dim())); } for message in &self.queued_messages { let wrapped = adaptive_wrap_lines( - message.lines().map(|line| line.dim().italic()), + message.lines().map(|line| Line::from(line.dim().italic())), RtOptions::new(width as usize) .initial_indent(Line::from(" ↳ ".dim())) .subsequent_indent(Line::from(" ")), ); - let len = wrapped.len(); - for line in wrapped.into_iter().take(3) { - lines.push(line); - } - if len > 3 { - lines.push(Line::from(" …".dim().italic())); - } + Self::push_truncated_preview_lines( + &mut lines, + wrapped, + Line::from(" …".dim().italic()), + ); } if !self.queued_messages.is_empty() { @@ -266,4 +270,20 @@ mod tests { format!("{buf:?}") ); } + + #[test] + fn render_multiline_pending_steer_uses_single_prefix_and_truncates() { + let mut queue = PendingInputPreview::new(); + queue + .pending_steers + .push("First line\nSecond line\nThird line\nFourth line".to_string()); + let width = 48; + let height = queue.desired_height(width); + let mut buf = Buffer::empty(Rect::new(0, 0, width, height)); + queue.render(Rect::new(0, 0, width, height), &mut buf); + assert_snapshot!( + "render_multiline_pending_steer_uses_single_prefix_and_truncates", + format!("{buf:?}") + ); + } } diff --git a/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_multiline_pending_steer_uses_single_prefix_and_truncates.snap b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_multiline_pending_steer_uses_single_prefix_and_truncates.snap new file mode 100644 index 000000000..eaf9edfc6 --- /dev/null +++ b/codex-rs/tui/src/bottom_pane/snapshots/codex_tui__bottom_pane__pending_input_preview__tests__render_multiline_pending_steer_uses_single_prefix_and_truncates.snap @@ -0,0 +1,23 @@ +--- +source: tui/src/bottom_pane/pending_input_preview.rs +expression: "format!(\"{buf:?}\")" +--- +Buffer { + area: Rect { x: 0, y: 0, width: 48, height: 4 }, + content: [ + " ! pending steer: First line ", + " Second line ", + " Third line ", + " … ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 29, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 4, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 15, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 4, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 14, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM, + x: 5, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + ] +}