mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
Preserve status usage hyperlinks when the URL wraps (#51457)
## Why The ChatGPT usage URL in the status card loses its terminal hyperlink when it wraps across lines in a narrow terminal. ## What changed Attach the usage URL destination to each underlined span so every wrapped fragment remains clickable. ## Testing Extend status hyperlink coverage at widths of 24, 42, and 120 columns to verify that linked fragments reconstruct the full URL and all point to the same destination. GitOrigin-RevId: 516421380763f48da8af63b4d374e2b352722ddb
This commit is contained in:
@@ -8,7 +8,6 @@ use crate::style::accent_color;
|
||||
use crate::token_usage::TokenUsage;
|
||||
use crate::token_usage::TokenUsageInfo;
|
||||
use crate::version::CODEX_CLI_VERSION;
|
||||
use crate::width::display_width;
|
||||
use chrono::DateTime;
|
||||
use chrono::Local;
|
||||
use codex_app_server_protocol::AskForApproval;
|
||||
@@ -949,20 +948,22 @@ impl HistoryCell for Arc<StatusHistoryCell> {
|
||||
) -> Vec<crate::terminal_hyperlinks::HyperlinkLine> {
|
||||
let mut lines =
|
||||
crate::terminal_hyperlinks::plain_hyperlink_lines(self.display_lines(width));
|
||||
for line in &mut lines {
|
||||
let visible = line
|
||||
.line
|
||||
.spans
|
||||
.iter()
|
||||
.map(|span| span.content.as_ref())
|
||||
.collect::<String>();
|
||||
if let Some(start_byte) = visible.find(CHATGPT_USAGE_URL) {
|
||||
let start = display_width(&visible[..start_byte]);
|
||||
line.hyperlinks
|
||||
.push(crate::terminal_hyperlinks::TerminalHyperlink::web(
|
||||
start..start + display_width(CHATGPT_USAGE_URL),
|
||||
CHATGPT_USAGE_URL.to_string(),
|
||||
));
|
||||
// The usage URL has a known destination; wrapping preserves its underlined spans.
|
||||
// Attach that destination to every fragment instead of searching for the full URL.
|
||||
if self.show_chatgpt_usage_link {
|
||||
for line in &mut lines {
|
||||
let mut column = 0;
|
||||
for span in &line.line.spans {
|
||||
let end = column + span.width();
|
||||
if span.style.add_modifier.contains(Modifier::UNDERLINED) {
|
||||
line.hyperlinks
|
||||
.push(crate::terminal_hyperlinks::TerminalHyperlink::web(
|
||||
column..end,
|
||||
CHATGPT_USAGE_URL.to_string(),
|
||||
));
|
||||
}
|
||||
column = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
lines
|
||||
|
||||
@@ -828,23 +828,33 @@ async fn status_uses_server_provider_id_and_auth_requirement() {
|
||||
sanitize_directory(render_lines(&composite.display_lines(/*width*/ 120))).join("\n");
|
||||
assert_snapshot!("status_server_auth_required", rendered);
|
||||
|
||||
for width in [42, 120] {
|
||||
let destinations: Vec<String> = composite
|
||||
.display_hyperlink_lines(width)
|
||||
.into_iter()
|
||||
.flat_map(|line| line.hyperlinks.into_iter())
|
||||
.map(|link| link.destination)
|
||||
.collect();
|
||||
assert_eq!(destinations, vec!["https://chatgpt.com/settings/usage"]);
|
||||
for width in [24, 42, 120] {
|
||||
let lines = composite.display_hyperlink_lines(width);
|
||||
let mut fragments = String::new();
|
||||
let mut destinations = Vec::new();
|
||||
for line in lines {
|
||||
let visible = line.line.to_string();
|
||||
for link in line.hyperlinks {
|
||||
fragments.extend(
|
||||
visible
|
||||
.chars()
|
||||
.skip(link.columns.start)
|
||||
.take(link.columns.len()),
|
||||
);
|
||||
destinations.push(link.destination);
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
fragments, "https://chatgpt.com/settings/usage",
|
||||
"width {width}"
|
||||
);
|
||||
assert!(!destinations.is_empty());
|
||||
assert!(
|
||||
destinations
|
||||
.iter()
|
||||
.all(|destination| destination == "https://chatgpt.com/settings/usage")
|
||||
);
|
||||
}
|
||||
|
||||
let narrow_destinations: Vec<String> = composite
|
||||
.display_hyperlink_lines(/*width*/ 24)
|
||||
.into_iter()
|
||||
.flat_map(|line| line.hyperlinks.into_iter())
|
||||
.map(|link| link.destination)
|
||||
.collect();
|
||||
assert_eq!(narrow_destinations, Vec::<String>::new());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user