diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 48731eac42..9225d7409c 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2511,6 +2511,7 @@ dependencies = [ "unicode-segmentation", "unicode-width 0.2.1", "url", + "urlencoding", "uuid", "vt100", "webbrowser", diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index 594acb33d4..6d8a1e6ac5 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -101,6 +101,7 @@ two-face = { version = "0.5", default-features = false, features = ["syntect-def unicode-segmentation = { workspace = true } unicode-width = { workspace = true } url = { workspace = true } +urlencoding = { workspace = true } webbrowser = { workspace = true } uuid = { workspace = true } diff --git a/codex-rs/tui/src/markdown_render.rs b/codex-rs/tui/src/markdown_render.rs index 54410bdfbc..85c0cda58b 100644 --- a/codex-rs/tui/src/markdown_render.rs +++ b/codex-rs/tui/src/markdown_render.rs @@ -20,6 +20,7 @@ use std::path::Path; use std::path::PathBuf; use std::sync::LazyLock; use url::Url; +use urlencoding::decode; struct MarkdownStyles { h1: Style, @@ -157,7 +158,19 @@ fn local_link_display_text(dest_url: &str, cwd: Option<&Path>) -> Option fn split_local_link_destination(dest_url: &str) -> Option<(PathBuf, String)> { if dest_url.starts_with("file://") { let url = Url::parse(dest_url).ok()?; - let path = url.to_file_path().ok()?; + let path = url.to_file_path().ok().or_else(|| { + let decoded_path = decode(url.path()).ok()?.into_owned(); + if decoded_path.is_empty() { + return None; + } + let file_path = match url.host_str() { + Some(host) if !host.is_empty() && host != "localhost" => { + format!("//{host}{decoded_path}") + } + _ => decoded_path, + }; + Some(PathBuf::from(file_path)) + })?; let location_suffix = url .fragment() .map(|fragment| format!("#{fragment}"))