From 79d66a69a30c7d2fb8f6e4c4357b840ec1da432d Mon Sep 17 00:00:00 2001 From: yovinchen Date: Sat, 6 Sep 2025 20:53:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=82=E9=85=8Dwindows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/claude_binary.rs | 155 +++++++++++++++++++++++++-------- src-tauri/tauri.conf.json | 11 +-- src/lib/i18n.ts | 8 +- 3 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src-tauri/src/claude_binary.rs b/src-tauri/src/claude_binary.rs index f0a4713..315658c 100644 --- a/src-tauri/src/claude_binary.rs +++ b/src-tauri/src/claude_binary.rs @@ -48,10 +48,30 @@ pub fn find_claude_binary(app_handle: &tauri::AppHandle) -> Result u8 { fn discover_system_installations() -> Vec { let mut installations = Vec::new(); - // 1. Try 'which' command first (now works in production) - if let Some(installation) = try_which_command() { - installations.push(installation); - } + // 1. Try system command first (now works in production and can return multiple installations) + installations.extend(find_which_installations()); // 2. Check NVM paths installations.extend(find_nvm_installations()); @@ -164,48 +182,111 @@ fn discover_system_installations() -> Vec { installations } -/// Try using the 'which' command to find Claude -fn try_which_command() -> Option { - debug!("Trying 'which claude' to find binary..."); +/// Try using the command to find Claude installations +/// Returns multiple installations if found (Windows 'where' can return multiple paths) +fn find_which_installations() -> Vec { + debug!("Trying to find claude binary..."); - match Command::new("which").arg("claude").output() { + // Use 'where' on Windows, 'which' on Unix + #[cfg(target_os = "windows")] + let command_name = "where"; + #[cfg(not(target_os = "windows"))] + let command_name = "which"; + + let mut installations = Vec::new(); + + match Command::new(command_name).arg("claude").output() { Ok(output) if output.status.success() => { let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); if output_str.is_empty() { - return None; + return installations; } - // Parse aliased output: "claude: aliased to /path/to/claude" - let path = if output_str.starts_with("claude:") && output_str.contains("aliased to") { - output_str - .split("aliased to") - .nth(1) - .map(|s| s.trim().to_string()) - } else { - Some(output_str) - }?; + // Process each line (Windows 'where' can return multiple paths) + for line in output_str.lines() { + let mut path = line.trim().to_string(); + + if path.is_empty() { + continue; + } - debug!("'which' found claude at: {}", path); + // Parse aliased output: "claude: aliased to /path/to/claude" + if path.starts_with("claude:") && path.contains("aliased to") { + if let Some(aliased_path) = path.split("aliased to").nth(1) { + path = aliased_path.trim().to_string(); + } else { + continue; + } + } - // Verify the path exists - if !PathBuf::from(&path).exists() { - warn!("Path from 'which' does not exist: {}", path); - return None; + // Convert Unix-style path to Windows path if needed + #[cfg(target_os = "windows")] + let path = { + if path.starts_with("/c/") { + // Convert /c/path to C:\path + let windows_path = path.replace("/c/", "C:\\").replace("/", "\\"); + windows_path + } else if path.starts_with("/") && path.len() > 3 && path.chars().nth(2) == Some('/') { + // Convert /X/path to X:\path where X is drive letter + let drive = path.chars().nth(1).unwrap(); + let rest = &path[3..]; + format!("{}:\\{}", drive.to_uppercase(), rest.replace("/", "\\")) + } else { + path + } + }; + + #[cfg(not(target_os = "windows"))] + let path = path; + + debug!("'{}' found claude at: {}", command_name, path); + + // On Windows, prefer .cmd files over shell scripts + #[cfg(target_os = "windows")] + let final_path = { + if !path.ends_with(".cmd") && !path.ends_with(".exe") { + // Check if there's a .cmd file alongside + let cmd_path = format!("{}.cmd", path); + if PathBuf::from(&cmd_path).exists() { + // Only use .cmd if the original doesn't work + if let Err(_) = get_claude_version(&path) { + cmd_path + } else { + path + } + } else { + path + } + } else { + path + } + }; + + #[cfg(not(target_os = "windows"))] + let final_path = path; + + // Verify the path exists + if !PathBuf::from(&final_path).exists() { + warn!("Path from '{}' does not exist: {}", command_name, final_path); + continue; + } + + // Get version + let version = get_claude_version(&final_path).ok().flatten(); + + installations.push(ClaudeInstallation { + path: final_path, + version, + source: command_name.to_string(), + installation_type: InstallationType::System, + }); } - - // Get version - let version = get_claude_version(&path).ok().flatten(); - - Some(ClaudeInstallation { - path, - version, - source: "which".to_string(), - installation_type: InstallationType::System, - }) } - _ => None, + _ => {} } + + installations } /// Find Claude installations in NVM directories diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 08c4a6a..2435c32 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -35,17 +35,14 @@ "bundle": { "active": true, "targets": [ - "deb", - "rpm", - "appimage", - "app", - "dmg" + "msi" ], "icon": [ "icons/32x32.png", - "icons/128x128.png", + "icons/128x128.png", "icons/128x128@2x.png", - "icons/icon.icns" + "icons/icon.icns", + "icons/icon.png" ], "resources": [], "externalBin": [], diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index 97c3c1f..fa72710 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -33,6 +33,12 @@ i18n zh: { common: zh, }, + 'zh-CN': { + common: zh, + }, + 'zh-TW': { + common: zh, + }, }, // 命名空间配置 @@ -48,7 +54,7 @@ i18n }, // 白名单支持的语言 - supportedLngs: ['en', 'zh'], + supportedLngs: ['en', 'zh', 'zh-CN', 'zh-TW'], // 非显式支持的语言回退到en nonExplicitSupportedLngs: true,