From 3cd255a4ee8e9c3eef331e03356c5d917d6e4293 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 18 Sep 2026 00:15:35 +0000 Subject: [PATCH] Dim conversation recaps in the TUI (#46332) ## What changed Apply dim styling to all rendered recap lines and remove the cyan color from `Next:`, preserving italics and bold labels. ## Testing Update recap style assertions and add a rendered-buffer snapshot covering dimmed text, label styling, line breaks, and next-action wrapping. GitOrigin-RevId: 2dfef4753fa5da4e1fae32b5cdb03a881482f91b --- codex-rs/tui/src/app/recap_tests.rs | 48 ++++++++++--------- ..._tui__app__recap__tests__recap_dimmed.snap | 28 +++++++++++ codex-rs/tui/src/history_cell/notices.rs | 4 +- 3 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 codex-rs/tui/src/app/snapshots/codex_tui__app__recap__tests__recap_dimmed.snap diff --git a/codex-rs/tui/src/app/recap_tests.rs b/codex-rs/tui/src/app/recap_tests.rs index 9b5c51285c..6331277f10 100644 --- a/codex-rs/tui/src/app/recap_tests.rs +++ b/codex-rs/tui/src/app/recap_tests.rs @@ -28,8 +28,12 @@ use codex_app_server_protocol::Turn; use codex_app_server_protocol::TurnStatus; use codex_protocol::ThreadId; use pretty_assertions::assert_eq; +use ratatui::buffer::Buffer; +use ratatui::layout::Rect; use ratatui::style::Modifier; use ratatui::style::Stylize; +use ratatui::widgets::Paragraph; +use ratatui::widgets::Widget; use std::path::Path; use std::sync::Arc; use std::time::Duration; @@ -653,11 +657,11 @@ fn recap_history_cell_uses_hanging_indent_and_right_padding() { let cell = ThreadRecapHistoryCell::new("Automatic recaps stay compact on wide terminals.".to_string()); let lines = cell.display_lines(/*width*/ 56); - assert!( - lines - .iter() - .all(|line| line.style.add_modifier.contains(Modifier::ITALIC)) - ); + assert!(lines.iter().all(|line| { + line.style + .add_modifier + .contains(Modifier::ITALIC | Modifier::DIM) + })); assert_eq!( &lines[0].spans[..3], &[" ".into(), "↳ ".dim(), "Recap: ".bold()], @@ -726,11 +730,11 @@ fn recap_history_cell_wraps_long_urls_in_narrow_terminals() { .skip(/*n*/ 1) .all(|line| line.to_string().starts_with(" ")) ); - assert!( - lines - .iter() - .all(|line| line.style.add_modifier.contains(Modifier::ITALIC)) - ); + assert!(lines.iter().all(|line| { + line.style + .add_modifier + .contains(Modifier::ITALIC | Modifier::DIM) + })); let rendered = lines .iter() .map(ToString::to_string) @@ -827,7 +831,7 @@ fn recap_history_cell_wraps_next_action_urls_in_narrow_terminals() { .iter() .flat_map(|line| &line.spans) .find(|span| span.content == "Next: "), - Some(&"Next: ".bold().cyan().italic()), + Some(&"Next: ".bold().italic()), ); let rendered = lines .iter() @@ -856,13 +860,16 @@ fn recap_history_cell_preserves_line_breaks_and_optional_next() { .iter() .flat_map(|line| &line.spans) .find(|span| span.content == "Next: "), - Some(&"Next: ".bold().cyan().italic()), + Some(&"Next: ".bold().italic()), ); - let displayed = lines - .iter() - .map(ToString::to_string) - .collect::>() - .join("\n"); + let area = Rect::new( + /*x*/ 0, + /*y*/ 0, + /*width*/ 48, + lines.len() as u16, + ); + let mut buffer = Buffer::empty(area); + Paragraph::new(lines).render(area, &mut buffer); let raw = cell .raw_lines() .iter() @@ -870,12 +877,7 @@ fn recap_history_cell_preserves_line_breaks_and_optional_next() { .collect::>() .join("\n"); - insta::assert_snapshot!(displayed, @r" - ↳ Recap: Finished the parser. - Twelve tests pass. - Next: Run focused tests and check - the empty-input case. - "); + insta::assert_snapshot!("recap_dimmed", format!("{buffer:?}")); insta::assert_snapshot!(raw, @r" Conversation recap Finished the parser. diff --git a/codex-rs/tui/src/app/snapshots/codex_tui__app__recap__tests__recap_dimmed.snap b/codex-rs/tui/src/app/snapshots/codex_tui__app__recap__tests__recap_dimmed.snap new file mode 100644 index 0000000000..e5da9faadd --- /dev/null +++ b/codex-rs/tui/src/app/snapshots/codex_tui__app__recap__tests__recap_dimmed.snap @@ -0,0 +1,28 @@ +--- +source: tui/src/app/recap_tests.rs +assertion_line: 875 +expression: "format!(\"{buffer:?}\")" +--- +Buffer { + area: Rect { x: 0, y: 0, width: 48, height: 4 }, + content: [ + " ↳ Recap: Finished the parser. ", + " Twelve tests pass. ", + " Next: Run focused tests and check ", + " the empty-input case. ", + ], + styles: [ + x: 0, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 4, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD | DIM | ITALIC, + x: 11, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 31, y: 0, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 29, y: 1, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 11, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: BOLD | DIM | ITALIC, + x: 17, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 44, y: 2, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + x: 0, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: DIM | ITALIC, + x: 32, y: 3, fg: Reset, bg: Reset, underline: Reset, modifier: NONE, + ] +} diff --git a/codex-rs/tui/src/history_cell/notices.rs b/codex-rs/tui/src/history_cell/notices.rs index 0208e3d15f..6edf500fba 100644 --- a/codex-rs/tui/src/history_cell/notices.rs +++ b/codex-rs/tui/src/history_cell/notices.rs @@ -347,7 +347,7 @@ impl HistoryCell for ThreadRecapHistoryCell { if let Some(action) = &self.next_action { body.extend(prefix_lines( raw_lines_from_source(action), - "Next: ".bold().cyan(), + "Next: ".bold(), "".into(), )); } @@ -380,7 +380,7 @@ impl HistoryCell for ThreadRecapHistoryCell { push_owned_lines(&wrapped, &mut lines); options.initial_indent = options.subsequent_indent.clone(); } - lines + lines.into_iter().map(Line::dim).collect() } fn raw_lines(&self) -> Vec> {