diff --git a/scripts/platform-guards.test.mjs b/scripts/platform-guards.test.mjs new file mode 100644 index 0000000..d1c8e09 --- /dev/null +++ b/scripts/platform-guards.test.mjs @@ -0,0 +1,24 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import vm from 'node:vm'; + +test('only Mac platforms expose AppleScript actions', () => { + const source = fs.readFileSync('src/platform.ts', 'utf8').replace('export const isMacOS', 'globalThis.isMacOS'); + for (const [platform, expected] of [['Win32', false], ['Linux x86_64', false], ['MacIntel', true], ['MacARM', true]]) { + const context = { navigator: { platform } }; + vm.runInNewContext(source, context); + assert.equal(context.isMacOS, expected); + } + const context = {}; + vm.runInNewContext(source, context); + assert.equal(context.isMacOS, false); +}); + +test('both app reload paths and the toolbar gate saved settings by platform', () => { + const app = fs.readFileSync('src/App.tsx', 'utf8'); + assert.equal(app.match(/if \(isMacOS && settings.auto_reload_ide\)/g)?.length, 2); + const list = fs.readFileSync('src/components/AccountList.tsx', 'utf8'); + assert.ok(list.includes('disabled={!isMacOS}')); + assert.ok(list.includes('aria-pressed={isMacOS && autoReload}')); +}); diff --git a/src-tauri/src/ide_control.rs b/src-tauri/src/ide_control.rs index 3674568..4b69010 100644 --- a/src-tauri/src/ide_control.rs +++ b/src-tauri/src/ide_control.rs @@ -11,6 +11,9 @@ const IDE_CONFIGS: &[(&str, &str)] = &[ /// 检测运行中的 IDE pub fn detect_running_ides() -> Vec { + if !cfg!(target_os = "macos") { + return Vec::new(); + } let mut running = Vec::new(); for &(name, bundle_id) in IDE_CONFIGS { @@ -38,6 +41,9 @@ pub fn detect_running_ides() -> Vec { /// 重载指定 IDE pub fn reload_ide(name: &str, use_window_reload: bool) -> Result<(), String> { + if !cfg!(target_os = "macos") { + return Err("IDE 自动重载仅支持 macOS,请手动重载 IDE".into()); + } // 杀死所有 codex 进程(排除 Codex Switcher 自身) let script = r#" for pid in $(pgrep -f codex 2>/dev/null); do @@ -108,6 +114,9 @@ pub fn reload_ide(name: &str, use_window_reload: bool) -> Result<(), String> { /// 移除 Codex App 的隔离属性 (修复闪退) pub fn remove_quarantine() -> Result<(), String> { + if !cfg!(target_os = "macos") { + return Err("隔离属性修复仅适用于 macOS".into()); + } let script = r#" do shell script "xattr -dr com.apple.quarantine /Applications/Codex.app" with administrator privileges "#; @@ -130,3 +139,13 @@ fn run_applescript(script: &str) -> Result { Ok(String::from_utf8_lossy(&output.stdout).to_string()) } + +#[cfg(all(test, not(target_os = "macos")))] +mod tests { + #[test] + fn unsupported_actions_return_before_spawning_platform_tools() { + assert!(super::detect_running_ides().is_empty()); + assert!(super::reload_ide("Visual Studio Code", false).unwrap_err().contains("macOS")); + assert!(super::remove_quarantine().unwrap_err().contains("macOS")); + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6ae6eb2..dd7a194 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -4087,6 +4087,9 @@ async fn fix_codex_quarantine( /// 重载 IDE 窗口 #[tauri::command] async fn reload_ide_windows(use_window_reload: bool) -> Result, String> { + if !cfg!(target_os = "macos") { + return Err("IDE 自动重载仅支持 macOS,请手动重载 IDE".into()); + } let ides = ide_control::detect_running_ides(); let mut reloaded = Vec::new(); diff --git a/src-tauri/src/proxy.rs b/src-tauri/src/proxy.rs index b52101d..dd88619 100644 --- a/src-tauri/src/proxy.rs +++ b/src-tauri/src/proxy.rs @@ -2267,7 +2267,7 @@ fn mark_current_banned(state: &ProxyState) { println!("[Proxy] 账号 {} 已标记为封号", name); let _ = state.app_handle.emit("proxy-account-banned", &name); // macOS 系统通知(可配置) - if store.settings.notify_on_switch { + if cfg!(target_os = "macos") && store.settings.notify_on_switch { let notify_name = name.clone(); std::thread::spawn(move || { let _ = std::process::Command::new("osascript") @@ -2581,7 +2581,7 @@ fn do_switch(state: &ProxyState, new_id: &str, reason: SwitchReason) -> Result<( } // macOS 系统通知(可配置) - if notify_enabled { + if cfg!(target_os = "macos") && notify_enabled { let from = from_name.unwrap_or_else(|| "无".to_string()); let notify_msg = format!("{} → {}", from, to_name); std::thread::spawn(move || { diff --git a/src/App.tsx b/src/App.tsx index fb663e3..cd668db 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,6 +20,7 @@ import CachePanel from './components/CachePanel'; import { ConfirmModal } from './components/ConfirmModal'; import { RelayImportConfirm } from './components/RelayImportConfirm'; import './App.css'; +import { isMacOS } from './platform'; type PageType = 'dashboard' | 'accounts' | 'proxy' | 'routes' | 'stats' | 'cache' | 'skills' | 'settings'; @@ -187,7 +188,7 @@ function App() { // 执行真正的切换逻辑 const performSwitch = async (id: string) => { await switchTo(id); - if (settings.auto_reload_ide) { + if (isMacOS && settings.auto_reload_ide) { setTimeout(async () => { await reloadIdeWindows(false); }, 300); @@ -425,7 +426,7 @@ function App() { onForceOverwriteDisk={async () => { try { await invoke('force_overwrite_disk_with_current'); - if (settings.auto_reload_ide) { + if (isMacOS && settings.auto_reload_ide) { await reloadIdeWindows(false); } checkSyncStatus(); diff --git a/src/components/AccountList.tsx b/src/components/AccountList.tsx index 11df42b..72b0354 100644 --- a/src/components/AccountList.tsx +++ b/src/components/AccountList.tsx @@ -3,6 +3,7 @@ import { Zap, RefreshCw, ArrowLeftRight, Trash2, Clock, UploadCloud, Plus, Gauge import { Account, AppSettings, LunaReserveWindow, RelayUsageCache, SparkWindows, effectiveKind } from '../hooks/useAccounts'; import { invoke } from '@tauri-apps/api/core'; import { openUrl } from '@tauri-apps/plugin-opener'; +import { isMacOS } from '../platform'; import { AntigravityQuota, type AntigravityModelQuota } from './AntigravityQuota'; import { AgyRelayModelQuotas, RelayQuotaWindows } from './RelayQuotaWindows'; import { relayCurrentState } from '../utils/relayCurrent'; @@ -842,11 +843,13 @@ export function AccountList({
{onAddAccount && ( diff --git a/src/platform.ts b/src/platform.ts new file mode 100644 index 0000000..b096f22 --- /dev/null +++ b/src/platform.ts @@ -0,0 +1,2 @@ +const platform = typeof navigator === 'undefined' ? '' : navigator.platform; +export const isMacOS = platform.startsWith('Mac');