diff --git a/codex-rs/tui/src/bottom_pane/async_questions/mod.rs b/codex-rs/tui/src/bottom_pane/async_questions/mod.rs index de4232ef8f..edf4e40d74 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/mod.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/mod.rs @@ -17,10 +17,6 @@ use crate::keymap::KeymapContext; use crate::keymap::ListAction; use crate::keymap::RuntimeKeymap; use crate::terminal_hyperlinks::HyperlinkLine; -use crate::terminal_hyperlinks::annotate_web_urls_in_line; -use crate::terminal_hyperlinks::remap_source_wrapped_line; -use crate::wrapping::WrappedLine; -use crate::wrapping::wrap_ranges_trim; use codex_protocol::items::AsyncUserInputQuestion; use crossterm::event::KeyCode; use crossterm::event::KeyEvent; @@ -156,26 +152,29 @@ impl AsyncQuestions { } pub(super) fn wrapped_question_lines(&self, width: u16) -> Vec { + let width = usize::from(width.max(1)); self.current_question() - .map(|q| { - // Line::from removes line endings, so wrap and annotate each logical line - // separately to keep source byte ranges aligned with the visible text. - q.title - .split('\n') - .flat_map(|text| { - let text = text.strip_suffix('\r').unwrap_or(text); - let source = annotate_web_urls_in_line(text.to_owned().into()); - let wrapped = wrap_ranges_trim(text, usize::from(width.max(1))) - .into_iter() - .map(|range| WrappedLine { - line: (&text[range.clone()]).into(), - range, - prefix_bytes: 0, - }) - .collect(); - remap_source_wrapped_line(&source, wrapped) - }) - .collect() + .map(|question| { + crate::markdown_render::render_markdown_lines_with_width_cwd_and_hidden_link_destinations( + &question.title, + Some(width), + /*cwd*/ None, + &crate::markdown_render::hide_web_link_destination, + ) + .into_iter() + .flat_map(|line| { + if line.width() <= width { + vec![line] + } else { + // The question layout counts physical rows, so break overwide Markdown + // tokens while preserving their hyperlink destinations. + crate::terminal_hyperlinks::remap_source_wrapped_line( + &line, + crate::wrapping::word_wrap_line_with_source(&line.line, width), + ) + } + }) + .collect() }) .unwrap_or_default() } diff --git a/codex-rs/tui/src/bottom_pane/async_questions/render.rs b/codex-rs/tui/src/bottom_pane/async_questions/render.rs index f83dff8c10..037fc8d723 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/render.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/render.rs @@ -52,7 +52,7 @@ impl Renderable for AsyncQuestions { Paragraph::new(self.progress_prefix_text().dim()).render(sections.progress_area, buf); HyperlinkParagraph::new( §ions.question_lines, - crate::style::accent_style().bold(), + crate::style::accent_style().not_bold(), ) .render(sections.question_area, buf); diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_clipped_prompt.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_clipped_prompt.snap index eb8920a91f..3ec5f41f35 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_clipped_prompt.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_clipped_prompt.snap @@ -1,6 +1,7 @@ --- source: tui/src/bottom_pane/async_questions/state_tests.rs +assertion_line: 483 expression: buffer_text(&clipped) --- - Long question Long question Long question + Long question Long question Long question Long diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_multiline.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_multiline.snap index 54cdcd05c7..59f43b6c34 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_multiline.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_multiline.snap @@ -1,5 +1,6 @@ --- source: tui/src/bottom_pane/async_questions/state_tests.rs +assertion_line: 220 expression: "rendered[0]" --- @@ -7,11 +8,10 @@ expression: "rendered[0]" Run this command: - ```sh printf '診 断 ' - ``` - Then review (https://example.com/diagnostics?view=full)? + Then review the diagnostics + (https://example.com/diagnostics?view=full)? Type your answer diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_url.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_url.snap index 4435c91add..0ecfd4b1ac 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_url.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__question_wrapped_url.snap @@ -1,13 +1,13 @@ --- source: tui/src/bottom_pane/async_questions/state_tests.rs -assertion_line: 172 +assertion_line: 171 expression: "crate::terminal_hyperlinks::strip_osc8(&buffer_text(&buffer))" --- 2 of 2 - Review this change (https:// - github.com/openai/codex/pull/12345? - diff=split)? + Review this change + (https://github.com/openai/codex/ + pull/12345?diff=split)? › 1. Named 2. Other diff --git a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__selected_other_placeholder.snap b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__selected_other_placeholder.snap index 324419be1e..db5d210a59 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__selected_other_placeholder.snap +++ b/codex-rs/tui/src/bottom_pane/async_questions/snapshots/codex_tui__bottom_pane__async_questions__tests__selected_other_placeholder.snap @@ -1,13 +1,14 @@ --- source: tui/src/bottom_pane/async_questions/state_tests.rs +assertion_line: 253 expression: buffer_text(&buffer) --- - Long question Long question Long question - Long question Long question Long question - Long question Long question Long question - Long question Long question Long question - Long question Long question Long question - Long question Long question Long question - Long question Long question Long question + Long question Long question Long question Long + question Long question Long question Long + question Long question Long question Long + question Long question Long question Long + question Long question Long question Long + question Long question Long question Long + question Long question Long question Long › 2. Other diff --git a/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs b/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs index 713ac5194a..cf2801c052 100644 --- a/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs +++ b/codex-rs/tui/src/bottom_pane/async_questions/state_tests.rs @@ -181,9 +181,11 @@ fn wrapped_question_url_keeps_its_complete_destination() { } #[test] -fn multiline_question_preserves_line_breaks_and_link_destinations() { +fn markdown_question_preserves_formatting_and_link_destinations() { let url = "https://example.com/diagnostics?view=full"; - let title = format!("Run this command:\n\n```sh\nprintf '診断'\n```\nThen review ({url})?\n"); + let title = format!( + "Run **this command**:\n\n```sh\nprintf '診断'\n```\nThen review the [diagnostics]({url})?\n" + ); let mut rendered = Vec::new(); for line_ending in ["\n", "\r\n"] { let mut editor = editor(); @@ -207,9 +209,17 @@ fn multiline_question_preserves_line_breaks_and_link_destinations() { }) }) .collect::(); - assert_eq!(linked_text, url); + assert!(linked_text.contains("diagnostics")); let text = crate::terminal_hyperlinks::strip_osc8(&buffer_text(&buffer)); if width == 60 { + let bold_text = buffer + .content + .iter() + .filter(|cell| cell.modifier.contains(Modifier::BOLD)) + .map(ratatui::buffer::Cell::symbol) + .collect::(); + assert!(bold_text.contains("this command")); + assert!(!bold_text.contains("Run ")); rendered.push(text); } }