mirror of
https://github.com/openai/codex.git
synced 2026-10-20 00:18:49 +00:00
Make URLs in the TUI warnings viewer clickable (#51451)
## What changed Render web URLs in warning details as terminal hyperlinks, preserving the full destination when a URL wraps across lines or the viewer scrolls. ## Testing Add a regression test for hyperlink destinations after wrapping and scrolling, plus a snapshot of a warning containing a wrapped URL. GitOrigin-RevId: 6500e6129a95b2dea8e8f53296ca056e63196a57
This commit is contained in:
+13
@@ -0,0 +1,13 @@
|
||||
---
|
||||
source: tui/src/bottom_pane/warnings_view_tests.rs
|
||||
expression: "draw(&view, 32, 9)"
|
||||
---
|
||||
Warnings · 1 of 2 · MCP · ex
|
||||
|
||||
Review https://github.com/
|
||||
openai/codex/pull/12345?
|
||||
diff=split before
|
||||
continuing.
|
||||
|
||||
|
||||
k keep & next · ⌃o copy
|
||||
@@ -46,24 +46,31 @@ impl Renderable for WarningsView {
|
||||
}
|
||||
let lines: Vec<_> = details
|
||||
.lines()
|
||||
.flat_map(|line| textwrap::wrap(line, usize::from(body.width.max(/*other*/ 1))))
|
||||
.flat_map(|text| {
|
||||
let source =
|
||||
crate::terminal_hyperlinks::annotate_web_urls_in_line(text.to_owned().into());
|
||||
let wrapped =
|
||||
crate::wrapping::wrap_ranges_trim(text, usize::from(body.width.max(1)))
|
||||
.into_iter()
|
||||
.map(|range| crate::wrapping::WrappedLine {
|
||||
line: (&text[range.clone()]).into(),
|
||||
range,
|
||||
prefix_bytes: 0,
|
||||
})
|
||||
.collect();
|
||||
crate::terminal_hyperlinks::remap_source_wrapped_line(&source, wrapped)
|
||||
})
|
||||
.collect();
|
||||
let page_size = usize::from(body.height.max(/*other*/ 1));
|
||||
self.page_size.set(page_size);
|
||||
self.max_offset.set(lines.len().saturating_sub(page_size));
|
||||
let offset = self.offset.get().min(self.max_offset.get());
|
||||
self.offset.set(offset);
|
||||
for (row, line) in lines
|
||||
.iter()
|
||||
.skip(offset)
|
||||
.take(usize::from(body.height))
|
||||
.enumerate()
|
||||
{
|
||||
Line::from(line.as_ref()).render(
|
||||
Rect::new(body.x, body.y + row as u16, body.width, /*height*/ 1),
|
||||
buf,
|
||||
);
|
||||
}
|
||||
crate::terminal_hyperlinks::HyperlinkParagraph::new(
|
||||
&lines[offset..],
|
||||
ratatui::style::Style::default(),
|
||||
)
|
||||
.render(body, buf);
|
||||
// Plain k belongs to this viewer, including when configured as a chord prefix.
|
||||
let hint = |context, action| {
|
||||
self.keymap.primary_hint(context, action).filter(|hint| {
|
||||
|
||||
@@ -39,7 +39,7 @@ fn draw(view: &WarningsView, width: u16, height: u16) -> String {
|
||||
.chunks(usize::from(width))
|
||||
.map(|row| {
|
||||
row.iter()
|
||||
.map(ratatui::buffer::Cell::symbol)
|
||||
.map(|cell| crate::terminal_hyperlinks::strip_osc8(cell.symbol()))
|
||||
.collect::<String>()
|
||||
.trim_end()
|
||||
.to_string()
|
||||
@@ -169,3 +169,51 @@ fn warnings_keep_current_and_next_without_dismissing_unvisited_pages() {
|
||||
};
|
||||
assert_eq!((dismissed, kept), (expected, vec![entries()[0].clone()]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn warning_links_preserve_destinations_when_wrapped_and_scrolled() {
|
||||
let url = "https://github.com/openai/codex/pull/12345?diff=split";
|
||||
let (tx, _) = unbounded_channel();
|
||||
let mut data = entries();
|
||||
data[0].details = format!("Review {url} before continuing.");
|
||||
let mut view = WarningsView::new(
|
||||
data,
|
||||
Arc::default(),
|
||||
RuntimeKeymap::defaults(),
|
||||
AppEventSender::new(tx),
|
||||
);
|
||||
let area = Rect::new(0, 0, 32, 9);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
view.render(area, &mut buffer);
|
||||
let linked = buffer
|
||||
.content
|
||||
.iter()
|
||||
.filter(|cell| cell.symbol().contains("\x1b]8;;"))
|
||||
.map(|cell| {
|
||||
assert!(cell.symbol().starts_with(&format!("\x1b]8;;{url}\x07")));
|
||||
crate::terminal_hyperlinks::strip_osc8(cell.symbol())
|
||||
})
|
||||
.collect::<String>();
|
||||
assert_eq!(linked, url);
|
||||
insta::assert_snapshot!(
|
||||
"warnings_wrapped_url",
|
||||
draw(&view, /*width*/ 32, /*height*/ 9)
|
||||
);
|
||||
key(&mut view, KeyCode::Down);
|
||||
let area = Rect::new(0, 0, 32, 5);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
view.render(area, &mut buffer);
|
||||
key(&mut view, KeyCode::Down);
|
||||
view.render(area, &mut buffer);
|
||||
let linked = buffer
|
||||
.content
|
||||
.iter()
|
||||
.filter(|cell| cell.symbol().contains("\x1b]8;;"))
|
||||
.collect::<Vec<_>>();
|
||||
assert!(!linked.is_empty());
|
||||
assert!(
|
||||
linked
|
||||
.iter()
|
||||
.all(|cell| cell.symbol().starts_with(&format!("\x1b]8;;{url}\x07")))
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user