diff --git a/src-tauri/src/commands/misc.rs b/src-tauri/src/commands/misc.rs index 8818ac26..61b0b47d 100644 --- a/src-tauri/src/commands/misc.rs +++ b/src-tauri/src/commands/misc.rs @@ -947,6 +947,7 @@ exec bash --norc --noprofile // Note: Kitty doesn't need the -e flag, others do let result = match terminal { "iterm2" => launch_macos_iterm2(&script_file), + "warp" => launch_macos_warp(&script_file), "alacritty" => launch_macos_open_app("Alacritty", &script_file, true), "kitty" => launch_macos_open_app("kitty", &script_file, false), "ghostty" => launch_macos_open_app("Ghostty", &script_file, true), @@ -1069,6 +1070,57 @@ fn launch_macos_open_app( Ok(()) } +#[cfg(target_os = "macos")] +fn launch_macos_warp(script_file: &std::path::Path) -> Result<(), String> { + use std::io::Write; + use std::os::unix::fs::PermissionsExt; + use std::process::Command; + + let mut cmd = Command::new("open"); + cmd.arg("-a").arg("Warp"); + + // Warp URI scheme cannot work well with script_file, because: + // + // 1. script_file's name ends up with .sh, so Warp would open the file rather than execute it + // 2. script_file has no execution permission, so we need to add one more indirection + let mut second_script_file = tempfile::Builder::new() + .disable_cleanup(true) + .permissions(std::fs::Permissions::from_mode(0o755)) + .tempfile() + .map_err(|e| format!("Failed to create temporary script file: {e}"))?; + + writeln!( + &mut second_script_file, + r#"#!/usr/bin/env sh + + rm -- "$0" + + exec bash {} + "#, + script_file.display(), + ) + .map_err(|e| format!("Failed to write to temporary script file for Warp: {e}"))?; + + let mut warp_url = url::Url::parse("warp://action/new_tab").unwrap(); + warp_url + .query_pairs_mut() + .append_pair("path", &second_script_file.path().to_string_lossy()); + let warp_url = warp_url.to_string(); + cmd.arg(warp_url); + + let output = cmd.output().map_err(|e| format!("启动 Warp 失败: {e}"))?; + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!( + "Warp 启动失败 (exit code: {:?}): {}", + output.status.code(), + stderr + )); + } + + Ok(()) +} + /// Linux: 根据用户首选终端启动 #[cfg(target_os = "linux")] fn launch_linux_terminal(config_file: &std::path::Path, cwd: Option<&Path>) -> Result<(), String> { @@ -1356,6 +1408,7 @@ read -n 1 -s let result = match terminal { "iterm2" => launch_macos_iterm2(&script_file), + "warp" => launch_macos_warp(&script_file), "alacritty" => launch_macos_open_app("Alacritty", &script_file, true), "kitty" => launch_macos_open_app("kitty", &script_file, false), "ghostty" => launch_macos_open_app("Ghostty", &script_file, true), diff --git a/src-tauri/src/session_manager/terminal/mod.rs b/src-tauri/src/session_manager/terminal/mod.rs index 085a86f0..420eff9b 100644 --- a/src-tauri/src/session_manager/terminal/mod.rs +++ b/src-tauri/src/session_manager/terminal/mod.rs @@ -22,6 +22,8 @@ pub fn launch_terminal( "wezterm" => launch_wezterm(command, cwd), "kaku" => launch_kaku(command, cwd), "alacritty" => launch_alacritty(command, cwd), + #[cfg(unix)] + "warp" => launch_warp(command, cwd), "custom" => launch_custom(command, cwd, custom_config), _ => Err(format!("Unsupported terminal target: {target}")), } @@ -201,6 +203,48 @@ fn build_wezterm_compatible_args_with_shell( args } +#[cfg(unix)] +fn launch_warp(command: &str, cwd: Option<&str>) -> Result<(), String> { + use std::io::Write; + use std::os::unix::fs::PermissionsExt; + + let cwd = cwd.ok_or("Failed to resume session without cwd")?; + + let mut script_file = tempfile::Builder::new() + .disable_cleanup(true) + .permissions(std::fs::Permissions::from_mode(0o755)) + .tempfile_in(cwd) + .map_err(|e| format!("Failed to create temporary script file for launching Warp: {e}"))?; + + writeln!( + &mut script_file, + r#"#!/usr/bin/env sh + + rm -- "$0" + + exec {command} + "#, + ) + .map_err(|e| format!("Failed to write to temporary script file for Warp: {e}"))?; + + let mut warp_url = url::Url::parse("warp://action/new_tab").unwrap(); + warp_url + .query_pairs_mut() + .append_pair("path", &script_file.path().to_string_lossy()); + let warp_url = warp_url.to_string(); + + let status = Command::new("open") + .args(["-a", "Warp", &warp_url]) + .status() + .map_err(|e| format!("Failed to launch Warp: {e}"))?; + + if status.success() { + Ok(()) + } else { + Err("Failed to launch Warp.".to_string()) + } +} + fn launch_alacritty(command: &str, cwd: Option<&str>) -> Result<(), String> { // Alacritty: open -na Alacritty --args --working-directory ... -e shell -c command let full_command = build_shell_command(command, None); diff --git a/src/components/settings/TerminalSettings.tsx b/src/components/settings/TerminalSettings.tsx index 4cf43d41..916b5fbe 100644 --- a/src/components/settings/TerminalSettings.tsx +++ b/src/components/settings/TerminalSettings.tsx @@ -17,6 +17,7 @@ const MACOS_TERMINALS = [ { value: "ghostty", labelKey: "settings.terminal.options.macos.ghostty" }, { value: "wezterm", labelKey: "settings.terminal.options.macos.wezterm" }, { value: "kaku", labelKey: "settings.terminal.options.macos.kaku" }, + { value: "warp", labelKey: "settings.terminal.options.macos.warp" }, ] as const; const WINDOWS_TERMINALS = [ diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 26e4b09e..42939438 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -549,7 +549,8 @@ "kitty": "Kitty", "ghostty": "Ghostty", "wezterm": "WezTerm", - "kaku": "Kaku" + "kaku": "Kaku", + "warp": "Warp" }, "windows": { "cmd": "Command Prompt", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index a351f0d1..5c5a110c 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -549,7 +549,8 @@ "kitty": "Kitty", "ghostty": "Ghostty", "wezterm": "WezTerm", - "kaku": "Kaku" + "kaku": "Kaku", + "warp": "Warp" }, "windows": { "cmd": "コマンドプロンプト", diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 19e1c21c..5add807a 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -549,7 +549,8 @@ "kitty": "Kitty", "ghostty": "Ghostty", "wezterm": "WezTerm", - "kaku": "Kaku" + "kaku": "Kaku", + "warp": "Warp" }, "windows": { "cmd": "命令提示符",