diff --git a/src-tauri/src/claude_desktop_config.rs b/src-tauri/src/claude_desktop_config.rs index a94849b78..ca0fdd08b 100644 --- a/src-tauri/src/claude_desktop_config.rs +++ b/src-tauri/src/claude_desktop_config.rs @@ -599,6 +599,8 @@ pub fn map_proxy_request_model(mut body: Value, provider: &Provider) -> Result Result { + // get_proxy_config is async-tagged but its body is fully synchronous (rusqlite + // under a Mutex), so block_on cannot deadlock the calling thread. let config = futures::executor::block_on(db.get_proxy_config())?; Ok(format!( "{}{}", @@ -617,39 +619,32 @@ fn apply_provider_to_paths( } validate_provider(provider)?; - let snapshots = snapshot_files(paths)?; - - if let Err(err) = apply_provider_to_paths_inner(db, provider, paths) { - if let Err(rollback_err) = restore_snapshots(&snapshots) { - log::error!( - "Failed to rollback Claude Desktop config after apply error: {rollback_err}" - ); - return Err(AppError::Message(format!( - "{err}; rollback failed: {rollback_err}" - ))); - } - return Err(err); - } - - Ok(()) + with_rollback(paths, |paths| apply_provider_to_paths_inner(db, provider, paths)) } fn restore_official_at_paths(paths: &ClaudeDesktopPaths) -> Result<(), AppError> { + with_rollback(paths, restore_official_at_paths_inner) +} + +fn with_rollback(paths: &ClaudeDesktopPaths, op: F) -> Result<(), AppError> +where + F: FnOnce(&ClaudeDesktopPaths) -> Result<(), AppError>, +{ let snapshots = snapshot_files(paths)?; - - if let Err(err) = restore_official_at_paths_inner(paths) { - if let Err(rollback_err) = restore_snapshots(&snapshots) { - log::error!( - "Failed to rollback Claude Desktop config after restore error: {rollback_err}" - ); - return Err(AppError::Message(format!( - "{err}; rollback failed: {rollback_err}" - ))); - } - return Err(err); + match op(paths) { + Ok(()) => Ok(()), + Err(err) => match restore_snapshots(&snapshots) { + Ok(()) => Err(err), + Err(rollback_err) => { + log::error!( + "Failed to rollback Claude Desktop config after error: {rollback_err}" + ); + Err(AppError::Message(format!( + "{err}; rollback failed: {rollback_err}" + ))) + } + }, } - - Ok(()) } fn apply_provider_to_paths_inner( diff --git a/src-tauri/src/services/proxy.rs b/src-tauri/src/services/proxy.rs index bafbac56d..8ef08c198 100644 --- a/src-tauri/src/services/proxy.rs +++ b/src-tauri/src/services/proxy.rs @@ -466,10 +466,7 @@ impl ProxyService { AppType::Claude => self.read_claude_live()?, AppType::Codex => self.read_codex_live()?, AppType::Gemini => self.read_gemini_live()?, - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features - return Err("该应用不支持代理功能".to_string()); - } + _ => return Err("该应用不支持代理功能".to_string()), }; self.sync_live_config_to_provider(app_type, &live_config) @@ -683,9 +680,7 @@ impl ProxyService { } } } - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features, skip silently - } + _ => {} } Ok(()) @@ -864,10 +859,7 @@ impl ProxyService { AppType::Claude => ("claude", self.read_claude_live()?), AppType::Codex => ("codex", self.read_codex_live()?), AppType::Gemini => ("gemini", self.read_gemini_live()?), - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features - return Err("该应用不支持代理功能".to_string()); - } + _ => return Err("该应用不支持代理功能".to_string()), }; let json_str = serde_json::to_string(&config) @@ -1008,10 +1000,7 @@ impl ProxyService { self.write_gemini_live(&live_config)?; log::info!("Gemini Live 配置已接管,代理地址: {proxy_url}"); } - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features - return Err("该应用不支持代理功能".to_string()); - } + _ => return Err("该应用不支持代理功能".to_string()), } Ok(()) @@ -1061,9 +1050,7 @@ impl ProxyService { let _ = self.write_gemini_live(&live_config); } } - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features, skip silently - } + _ => {} } Ok(()) @@ -1101,9 +1088,7 @@ impl ProxyService { log::info!("Gemini Live 配置已恢复"); } } - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features, skip silently - } + _ => {} } Ok(()) @@ -1192,10 +1177,7 @@ impl ProxyService { AppType::Claude => self.write_claude_live(config), AppType::Codex => self.write_codex_live(config), AppType::Gemini => self.write_gemini_live(config), - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features - Err("该应用不支持代理功能".to_string()) - } + _ => Err("该应用不支持代理功能".to_string()), } } @@ -1213,10 +1195,7 @@ impl ProxyService { Ok(config) => Self::is_gemini_live_taken_over(&config), Err(_) => false, }, - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy takeover - false - } + _ => false, } } @@ -1256,10 +1235,7 @@ impl ProxyService { AppType::Claude => self.cleanup_claude_takeover_placeholders_in_live(), AppType::Codex => self.cleanup_codex_takeover_placeholders_in_live(), AppType::Gemini => self.cleanup_gemini_takeover_placeholders_in_live(), - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - // These apps don't support proxy features - Ok(()) - } + _ => Ok(()), } } @@ -1520,9 +1496,7 @@ impl ProxyService { serde_json::to_string(&env_backup) .map_err(|e| format!("序列化 Gemini 配置失败: {e}"))? } - AppType::OpenCode | AppType::OpenClaw | AppType::Hermes | AppType::ClaudeDesktop => { - return Err(format!("未知的应用类型: {app_type}")); - } + _ => return Err(format!("未知的应用类型: {app_type}")), }; self.db diff --git a/src/hooks/useProviderActions.ts b/src/hooks/useProviderActions.ts index 2e02ebbf4..295609f06 100644 --- a/src/hooks/useProviderActions.ts +++ b/src/hooks/useProviderActions.ts @@ -227,26 +227,21 @@ export function useProviderActions( // 若已弹过 proxyRequired 警告则不再弹 success if (!proxyRequiredReason) { - // OpenCode/OpenClaw: show "added to config" message instead of "switched" - const isMultiProviderApp = - activeApp === "opencode" || activeApp === "openclaw"; - const messageKey = - activeApp === "claude-desktop" - ? provider.meta?.claudeDesktopMode === "proxy" - ? "notifications.claudeDesktopProxyRestartRequired" - : "notifications.claudeDesktopRestartRequired" - : isMultiProviderApp - ? "notifications.addToConfigSuccess" - : "notifications.switchSuccess"; - const defaultMessage = - activeApp === "claude-desktop" - ? provider.meta?.claudeDesktopMode === "proxy" - ? "切换成功,请保持 CC Switch 运行,并重启 Claude Desktop 后生效" - : "切换成功,重启 Claude Desktop 后生效" - : isMultiProviderApp - ? "已添加到配置" - : "切换成功!"; - + let messageKey = "notifications.switchSuccess"; + let defaultMessage = "切换成功!"; + if (activeApp === "claude-desktop") { + if (provider.meta?.claudeDesktopMode === "proxy") { + messageKey = "notifications.claudeDesktopProxyRestartRequired"; + defaultMessage = + "切换成功,请保持 CC Switch 运行,并重启 Claude Desktop 后生效"; + } else { + messageKey = "notifications.claudeDesktopRestartRequired"; + defaultMessage = "切换成功,重启 Claude Desktop 后生效"; + } + } else if (activeApp === "opencode" || activeApp === "openclaw") { + messageKey = "notifications.addToConfigSuccess"; + defaultMessage = "已添加到配置"; + } toast.success(t(messageKey, { defaultValue: defaultMessage }), { closeButton: true, });