diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index e4157c5a..7cf3b0cb 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -4735,19 +4735,18 @@ async fn apply_command_result( #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] fn open_external_url(url: &str) -> Result<()> { - let mut command = external_url_command(url); + spawn_external_url_command(external_url_command(url)) +} - let status = command +#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] +fn spawn_external_url_command(mut command: Command) -> Result<()> { + command + .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) - .status() - .map_err(|err| anyhow::anyhow!("failed to launch browser command: {err}"))?; - if !status.success() { - return Err(anyhow::anyhow!( - "browser command exited with status {status}" - )); - } - Ok(()) + .spawn() + .map(|_| ()) + .map_err(|err| anyhow::anyhow!("failed to launch browser command: {err}")) } #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))] diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index 5ec4f788..5e386bb5 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -2220,6 +2220,34 @@ fn event_poll_timeout_has_nonzero_floor() { ); } +#[test] +#[cfg(any(unix, windows))] +fn external_url_launcher_does_not_wait_for_browser_process() { + let command = slow_external_url_command(); + let start = Instant::now(); + + spawn_external_url_command(command).expect("spawn external URL command"); + + assert!( + start.elapsed() < Duration::from_millis(750), + "opening a feedback URL must not wait for the browser command to exit" + ); +} + +#[cfg(unix)] +fn slow_external_url_command() -> Command { + let mut command = Command::new("sh"); + command.args(["-c", "sleep 1"]); + command +} + +#[cfg(windows)] +fn slow_external_url_command() -> Command { + let mut command = Command::new("cmd"); + command.args(["/C", "ping -n 2 127.0.0.1 >NUL"]); + command +} + #[test] fn footer_status_line_spans_show_mode_and_model_idle_and_active() { let mut app = create_test_app();