diff --git a/TAURI_MIGRATION_PLAN.md b/TAURI_MIGRATION_PLAN.md new file mode 100644 index 0000000..44954ba --- /dev/null +++ b/TAURI_MIGRATION_PLAN.md @@ -0,0 +1,244 @@ +# Tauri 重构计划 + +## 项目概述 + +将 CC Switch 从 Electron 框架迁移到 Tauri,以大幅减少应用体积并提升性能。 + +### 目标收益 + +- **体积优化**: 77MB → ~8MB (减少 90%) +- **内存占用**: 减少 60-70% +- **启动速度**: 提升 3-5 倍 +- **安全性**: Rust 内存安全 + 细粒度权限控制 + +## 技术栈对比 + +| 技术层 | Electron (当前) | Tauri (目标) | +| -------- | -------------------- | ------------------------- | +| 后端 | Node.js + TypeScript | Rust | +| 前端 | React + TypeScript | React + TypeScript (不变) | +| IPC | Electron IPC | Tauri Commands | +| 文件操作 | Node.js fs | Rust std::fs | +| 配置存储 | electron-store | tauri-plugin-store | +| 打包 | electron-builder | Tauri CLI | +| WebView | Chromium (内置) | 系统 WebView | + +## 迁移计划 + +### Phase 1: 环境准备 (Day 1 上午) + +- [x] 安装 Rust 开发环境 + ```bash + # Windows: 下载 rustup-init.exe + # https://www.rust-lang.org/tools/install + ``` +- [x] 安装 Tauri CLI + ```bash + pnpm add -g @tauri-apps/cli + ``` +- [x] 在现有项目中集成 Tauri + + ```bash + # 安装 Tauri CLI 作为开发依赖 + pnpm add -D @tauri-apps/cli + + # 在现有项目中初始化 Tauri + pnpm tauri init + + # 安装 Tauri API 包 + pnpm add @tauri-apps/api + ``` + +### Phase 2: 项目结构调整 (Day 1 下午) + +- [x] 创建 Tauri 项目配置 + - `src-tauri/` - Rust 后端代码 + - `src-tauri/tauri.conf.json` - Tauri 配置 + - `src-tauri/Cargo.toml` - Rust 依赖管理 +- [x] 迁移前端构建配置 + - 调整 Vite 配置适配 Tauri ✅ + - 更新 package.json scripts ✅ +- [x] 配置应用图标和元数据 + +### Phase 3: 后端功能迁移 (Day 2) + +#### 3.1 核心功能模块 (上午) + +- [x] **配置文件管理** (`src-tauri/src/config.rs`) + - 读取 ~/.claude/settings.json + - 写入配置文件 + - 备份/恢复配置 +- [x] **供应商管理** (`src-tauri/src/provider.rs`) + - 供应商列表的增删改查 + - 供应商配置切换逻辑 + - 配置文件命名规则 (settings-{name}.json) + +#### 3.2 Tauri Commands 实现 (下午) + +```rust +// 需要实现的命令列表 - 已完成 +#[tauri::command] +async fn get_providers() -> Result, String> + +#[tauri::command] +async fn get_current_provider() -> Result + +#[tauri::command] +async fn add_provider(provider: Provider) -> Result + +#[tauri::command] +async fn update_provider(provider: Provider) -> Result + +#[tauri::command] +async fn delete_provider(id: String) -> Result + +#[tauri::command] +async fn switch_provider(id: String) -> Result + +#[tauri::command] +async fn import_default_config() -> Result + +#[tauri::command] +async fn get_claude_config_status() -> Result +``` + +#### 3.3 数据存储 (`src-tauri/src/store.rs`) + +- [x] 使用 tauri-plugin-store 替代 electron-store +- [x] 迁移配置存储逻辑 (~/.cc-switch/config.json) + +### Phase 4: 前端适配 (Day 2 傍晚) + +#### 4.1 API 层重构 + +- [ ] 创建 `src/lib/tauri-api.ts` + - 替换 Electron IPC 调用为 Tauri invoke + - 保持 API 接口一致,减少组件改动 + +```typescript +// 示例:迁移前后对比 +// Electron (旧) +window.electronAPI.getProviders(); + +// Tauri (新) +import { invoke } from "@tauri-apps/api/tauri"; +invoke("get_providers"); +``` + +#### 4.2 最小化前端改动 + +- [ ] 更新 preload 桥接逻辑 +- [ ] 调整窗口控制相关代码 +- [ ] 处理文件路径差异 + +### Phase 5: 测试与优化 (Day 3 上午) + +#### 5.1 功能测试清单 + +- [ ] 供应商列表显示 +- [ ] 添加新供应商 +- [ ] 编辑供应商信息 +- [ ] 删除供应商 +- [ ] 切换供应商配置 +- [ ] 导入默认配置 +- [ ] 预设模板功能 +- [ ] API Key 快速输入 + +#### 5.2 跨平台测试 + +- [ ] Windows 10/11 测试 +- [ ] 不考虑 Windows 7/8 兼容性 +- [ ] macOS 测试 (如有条件) +- [ ] Linux 测试 (如有条件) + +#### 5.3 性能优化 + +- [ ] Rust 代码优化 (release 模式) +- [ ] 减少不必要的文件 I/O +- [ ] 优化启动加载流程 + +### Phase 6: 构建与发布 (Day 3 下午) + +#### 6.1 构建配置 + +- [ ] 配置 GitHub Actions CI/CD +- [ ] 设置代码签名 (Windows/macOS) +- [ ] 配置自动更新机制 + +#### 6.2 打包发布 + +- [ ] Windows NSIS 安装包 +- [ ] Windows 便携版 (portable) +- [ ] macOS .app 包 +- [ ] Linux AppImage + +#### 6.3 版本发布 + +- [ ] 创建 3.0.0-beta.1 预发布 +- [ ] 编写迁移说明文档 +- [ ] 更新 README.md + +## 风险与应对 + +### 技术风险 + +1. **Rust 学习曲线** + + - 风险:Rust 语法相对复杂 + - 应对:专注于基础文件 I/O,使用成熟库 + +2. **WebView2 兼容性** + +- 不需要支持旧版 Windows + +3. **跨平台差异** + - 风险:不同系统的文件路径处理 + - 应对:使用 Tauri API 统一处理路径 + +### 用户体验风险 + +1. **界面渲染差异** + + - 风险:WebView 渲染可能与 Chromium 有细微差异 + - 应对:充分测试,必要时调整 CSS + +2. **功能回归** + - 风险:迁移过程中遗漏功能 + - 应对:严格按照测试清单验证 + +## 回滚方案 + +如果 Tauri 版本出现严重问题: + +1. 立即从 electron-legacy 分支发布修复版本 +2. 在 GitHub Release 页面提供两个版本下载 +3. 明确标注版本差异和适用场景 + +## 时间线 + +- **Day 1**: 环境搭建 + 项目结构 +- **Day 2**: 后端迁移 + 前端适配 +- **Day 3**: 测试优化 + 构建发布 +- **Total**: 3 个工作日完成迁移 + +## 成功标准 + +- ✅ 应用体积 < 10MB +- ✅ 冷启动时间 < 1 秒 +- ✅ 所有现有功能正常工作 +- ✅ 通过所有测试用例 +- ✅ 成功构建三平台安装包 + +## 后续优化 (可选) + +- 添加系统托盘功能 +- 实现自动更新机制 +- 添加快捷键支持 +- 优化动画效果 +- 支持深色模式跟随系统 + +--- + +_最后更新:2024-12-23_ +_负责人:Jason Young_ +_状态:进行中 - Phase 3 已完成_ diff --git a/src/renderer/lib/tauri-api.ts b/src/renderer/lib/tauri-api.ts new file mode 100644 index 0000000..4c08175 --- /dev/null +++ b/src/renderer/lib/tauri-api.ts @@ -0,0 +1,163 @@ +import { invoke } from '@tauri-apps/api/core'; +import { open } from '@tauri-apps/plugin-shell'; +import { Provider } from '../../shared/types'; + +// 定义配置状态类型 +interface ConfigStatus { + exists: boolean; + path: string; + error?: string; +} + +// 定义导入结果类型 +interface ImportResult { + success: boolean; + message?: string; +} + +// Tauri API 封装,保持与 Electron API 相同的接口 +export const tauriAPI = { + // 获取所有供应商 + getProviders: async (): Promise> => { + try { + return await invoke('get_providers'); + } catch (error) { + console.error('获取供应商列表失败:', error); + return {}; + } + }, + + // 获取当前供应商ID + getCurrentProvider: async (): Promise => { + try { + return await invoke('get_current_provider'); + } catch (error) { + console.error('获取当前供应商失败:', error); + return ''; + } + }, + + // 添加供应商 + addProvider: async (provider: Provider): Promise => { + try { + return await invoke('add_provider', { provider }); + } catch (error) { + console.error('添加供应商失败:', error); + throw error; + } + }, + + // 更新供应商 + updateProvider: async (provider: Provider): Promise => { + try { + return await invoke('update_provider', { provider }); + } catch (error) { + console.error('更新供应商失败:', error); + throw error; + } + }, + + // 删除供应商 + deleteProvider: async (id: string): Promise => { + try { + return await invoke('delete_provider', { id }); + } catch (error) { + console.error('删除供应商失败:', error); + throw error; + } + }, + + // 切换供应商 + switchProvider: async (providerId: string): Promise => { + try { + return await invoke('switch_provider', { id: providerId }); + } catch (error) { + console.error('切换供应商失败:', error); + return false; + } + }, + + // 导入当前配置为默认供应商 + importCurrentConfigAsDefault: async (): Promise => { + try { + const success = await invoke('import_default_config'); + return { + success, + message: success ? '成功导入默认配置' : '导入失败' + }; + } catch (error) { + console.error('导入默认配置失败:', error); + return { + success: false, + message: String(error) + }; + } + }, + + // 获取 Claude Code 配置文件路径 + getClaudeCodeConfigPath: async (): Promise => { + try { + return await invoke('get_claude_code_config_path'); + } catch (error) { + console.error('获取配置路径失败:', error); + return ''; + } + }, + + // 获取 Claude Code 配置状态 + getClaudeConfigStatus: async (): Promise => { + try { + return await invoke('get_claude_config_status'); + } catch (error) { + console.error('获取配置状态失败:', error); + return { + exists: false, + path: '', + error: String(error) + }; + } + }, + + // 打开配置文件夹 + openConfigFolder: async (): Promise => { + try { + await invoke('open_config_folder'); + } catch (error) { + console.error('打开配置文件夹失败:', error); + } + }, + + // 打开外部链接 + openExternal: async (url: string): Promise => { + try { + await invoke('open_external', { url }); + } catch (error) { + console.error('打开外部链接失败:', error); + } + }, + + // 选择配置文件(Tauri 暂不实现,保留接口兼容性) + selectConfigFile: async (): Promise => { + console.warn('selectConfigFile 在 Tauri 版本中暂不支持'); + return null; + } +}; + +// 创建全局 API 对象,兼容现有代码 +if (typeof window !== 'undefined') { + // 检测是否在 Tauri 环境中 + const isTauri = '__TAURI__' in window; + + if (isTauri) { + // 在 Tauri 环境中,将 API 绑定到 window.electronAPI + // 保持代码兼容性,无需修改组件代码 + (window as any).electronAPI = tauriAPI; + } + + // 提供平台信息 + (window as any).platform = { + isMac: navigator.platform.toLowerCase().includes('mac') + }; +} + +export default tauriAPI; \ No newline at end of file diff --git a/src/renderer/main.tsx b/src/renderer/main.tsx index a6e435c..359efc1 100644 --- a/src/renderer/main.tsx +++ b/src/renderer/main.tsx @@ -2,12 +2,8 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App' import './index.css' - -declare global { - interface Window { - platform?: { isMac?: boolean } - } -} +// 导入 Tauri API(自动绑定到 window.electronAPI) +import './lib/tauri-api' // 根据平台添加 body class,便于平台特定样式 if (window.platform?.isMac) { diff --git a/src/renderer/vite-env.d.ts b/src/renderer/vite-env.d.ts new file mode 100644 index 0000000..0d7966f --- /dev/null +++ b/src/renderer/vite-env.d.ts @@ -0,0 +1,39 @@ +/// + +import { Provider } from './shared/types'; + +interface ImportResult { + success: boolean; + message?: string; +} + +interface ConfigStatus { + exists: boolean; + path: string; + error?: string; +} + +declare global { + interface Window { + electronAPI: { + getProviders: () => Promise>; + getCurrentProvider: () => Promise; + addProvider: (provider: Provider) => Promise; + deleteProvider: (id: string) => Promise; + updateProvider: (provider: Provider) => Promise; + switchProvider: (providerId: string) => Promise; + importCurrentConfigAsDefault: () => Promise; + getClaudeCodeConfigPath: () => Promise; + getClaudeConfigStatus: () => Promise; + selectConfigFile: () => Promise; + openConfigFolder: () => Promise; + openExternal: (url: string) => Promise; + }; + platform: { + isMac: boolean; + }; + __TAURI__?: any; + } +} + +export {}; \ No newline at end of file