mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-06-16 13:34:04 +08:00
docs: reorganize docs directory structure
- Delete 9 completed planning/roadmap documents - Move 23 release notes into docs/release-notes/ with simplified filenames - Update all cross-references in READMEs, CHANGELOG, and release notes - Remove dangling doc reference in deeplink/mod.rs
This commit is contained in:
+1
-1
@@ -653,7 +653,7 @@ This beta release introduces the **Local API Proxy** feature, along with Skills
|
||||
|
||||
### Stats
|
||||
|
||||
- 51 commits since v3.7.1; 207 files changed; +17,297 / -6,870 lines. See [release-note-v3.8.0](docs/release-note-v3.8.0-en.md) for details.
|
||||
- 51 commits since v3.7.1; 207 files changed; +17,297 / -6,870 lines. See [release-note-v3.8.0](docs/release-notes/v3.8.0-en.md) for details.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ Modern AI-powered coding relies on CLI tools like Claude Code, Codex, Gemini CLI
|
||||
|
||||
## Features
|
||||
|
||||
[Full Changelog](CHANGELOG.md) | [Release Notes](docs/release-note-v3.11.1-en.md)
|
||||
[Full Changelog](CHANGELOG.md) | [Release Notes](docs/release-notes/v3.11.1-en.md)
|
||||
|
||||
### Provider Management
|
||||
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ Claude Code / Codex / Gemini 公式チャンネルが最安で元価格の 38% /
|
||||
|
||||
## 特長
|
||||
|
||||
[完全な更新履歴](CHANGELOG.md) | [リリースノート](docs/release-note-v3.11.1-ja.md)
|
||||
[完全な更新履歴](CHANGELOG.md) | [リリースノート](docs/release-notes/v3.11.1-ja.md)
|
||||
|
||||
### プロバイダ管理
|
||||
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ Claude Code / Codex / Gemini 官方渠道低至 3.8 / 0.2 / 0.9 折,充值更
|
||||
|
||||
## 功能特性
|
||||
|
||||
[完整更新日志](CHANGELOG.md) | [发布说明](docs/release-note-v3.11.1-zh.md)
|
||||
[完整更新日志](CHANGELOG.md) | [发布说明](docs/release-notes/v3.11.1-zh.md)
|
||||
|
||||
### 供应商管理
|
||||
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
# CC Switch Rust 后端重构方案
|
||||
|
||||
## 目录
|
||||
- [背景与现状](#背景与现状)
|
||||
- [问题确认](#问题确认)
|
||||
- [方案评估](#方案评估)
|
||||
- [渐进式重构路线](#渐进式重构路线)
|
||||
- [测试策略](#测试策略)
|
||||
- [风险与对策](#风险与对策)
|
||||
- [总结](#总结)
|
||||
|
||||
## 背景与现状
|
||||
- 前端已完成重构,后端 (Tauri + Rust) 仍维持历史结构。
|
||||
- 核心文件集中在 `src-tauri/src/commands.rs`、`lib.rs` 等超大文件中,业务逻辑与界面事件耦合严重。
|
||||
- 测试覆盖率低,只有零散单元测试,缺乏集成验证。
|
||||
|
||||
## 问题确认
|
||||
|
||||
| 提案问题 | 实际情况 | 严重程度 |
|
||||
| --- | --- | --- |
|
||||
| `commands.rs` 过长 | ✅ 1526 行,包含 32 个命令,职责混杂 | 🔴 高 |
|
||||
| `lib.rs` 缺少服务层 | ✅ 541 行,托盘/事件/业务逻辑耦合 | 🟡 中 |
|
||||
| `Result<T, String>` 泛滥 | ✅ 118 处,错误上下文丢失 | 🟡 中 |
|
||||
| 全局 `Mutex` 阻塞 | ✅ 31 处 `.lock()` 调用,读写不分离 | 🟡 中 |
|
||||
| 配置逻辑分散 | ✅ 分布在 5 个文件 (`config`/`app_config`/`app_store`/`settings`/`codex_config`) | 🟢 低 |
|
||||
|
||||
代码规模分布(约 5.4k SLOC):
|
||||
- `commands.rs`: 1526 行(28%)→ 第一优先级 🎯
|
||||
- `lib.rs`: 541 行(10%)→ 托盘逻辑与业务耦合
|
||||
- `mcp.rs`: 732 行(14%)→ 相对清晰
|
||||
- `migration.rs`: 431 行(8%)→ 一次性逻辑
|
||||
- 其他文件合计:2156 行(40%)
|
||||
|
||||
## 方案评估
|
||||
|
||||
### ✅ 优点
|
||||
1. **分层架构清晰**
|
||||
- `commands/`:Tauri 命令薄层
|
||||
- `services/`:业务流程,如供应商切换、MCP 同步
|
||||
- `infrastructure/`:配置读写、外设交互
|
||||
- `domain/`:数据模型 (`Provider`, `AppType` 等)
|
||||
→ 提升可测试性、降低耦合度、方便团队协作。
|
||||
|
||||
2. **统一错误处理**
|
||||
- 引入 `AppError`(`thiserror`),保留错误链和上下文。
|
||||
- Tauri 命令仍返回 `Result<T, String>`,通过 `From<AppError>` 自动转换。
|
||||
- 改善日志可读性,利于排查。
|
||||
|
||||
3. **并发优化**
|
||||
- `AppState` 切换为 `RwLock<MultiAppConfig>`。
|
||||
- 读多写少的场景提升吞吐(如频繁查询供应商列表)。
|
||||
|
||||
### ⚠️ 风险
|
||||
1. **过度设计**
|
||||
- 完整 DDD 四层在 5k 行项目中会增加 30-50% 维护成本。
|
||||
- Rust trait + repository 样板较多,收益不足。
|
||||
- 推荐“轻量分层”而非正统 DDD。
|
||||
|
||||
2. **迁移成本高**
|
||||
- `commands.rs` 拆分、错误统一、锁改造触及多文件。
|
||||
- 测试缺失导致重构风险高,需先补测试。
|
||||
- 估算完整改造需 5-6 周;建议分阶段输出可落地价值。
|
||||
|
||||
3. **技术选型需谨慎**
|
||||
- `parking_lot` 相比标准库 `RwLock` 提升有限,不必引入。
|
||||
- `spawn_blocking` 仅用于 >100ms 的阻塞任务,避免滥用。
|
||||
- 以现有依赖为主,控制复杂度。
|
||||
|
||||
## 实施进度
|
||||
- **阶段 1:统一错误处理 ✅**
|
||||
- 引入 `thiserror` 并在 `src-tauri/src/error.rs` 定义 `AppError`,提供常用构造函数和 `From<AppError> for String`,保留错误链路。
|
||||
- 配置、存储、同步等核心模块(`config.rs`、`app_config.rs`、`app_store.rs`、`store.rs`、`codex_config.rs`、`claude_mcp.rs`、`claude_plugin.rs`、`import_export.rs`、`mcp.rs`、`migration.rs`、`speedtest.rs`、`usage_script.rs`、`settings.rs`、`lib.rs` 等)已统一返回 `Result<_, AppError>`,避免字符串错误丢失上下文。
|
||||
- Tauri 命令层继续返回 `Result<_, String>`,通过 `?` + `Into<String>` 统一转换,前端无需调整。
|
||||
- `cargo check` 通过,`rg "Result<[^>]+, String"` 巡检确认除命令层外已无字符串错误返回。
|
||||
- **阶段 2:拆分命令层 ✅**
|
||||
- 已将单一 `src-tauri/src/commands.rs` 拆分为 `commands/{provider,mcp,config,settings,misc,plugin}.rs` 并通过 `commands/mod.rs` 统一导出,保持对外 API 不变。
|
||||
- 每个文件聚焦单一功能域(供应商、MCP、配置、设置、杂项、插件),命令函数平均 150-250 行,可读性与后续维护性显著提升。
|
||||
- 相关依赖调整后 `cargo check` 通过,静态巡检确认无重复定义或未注册命令。
|
||||
- **阶段 3:补充测试 ✅**
|
||||
- `tests/import_export_sync.rs` 集成测试涵盖配置备份、Claude/Codex live 同步、MCP 投影与 Codex/Claude 双向导入流程,并新增启用项清理、非法 TOML 抛错等失败场景验证;统一使用隔离 HOME 目录避免污染真实用户环境。
|
||||
- 扩展 `lib.rs` re-export,暴露 `AppType`、`MultiAppConfig`、`AppError`、配置 IO 以及 Codex/Claude MCP 路径与同步函数,方便服务层及测试直接复用核心逻辑。
|
||||
- 新增负向测试验证 Codex 供应商缺少 `auth` 字段时的错误返回,并补充备份数量上限测试;顺带修复 `create_backup` 采用内存读写避免拷贝继承旧的修改时间,确保最新备份不会在清理阶段被误删。
|
||||
- 针对 `codex_config::write_codex_live_atomic` 补充成功与失败场景测试,覆盖 auth/config 原子写入与失败回滚逻辑(模拟目标路径为目录时的 rename 失败),降低 Codex live 写入回归风险。
|
||||
- 新增 `tests/provider_commands.rs` 覆盖 `switch_provider` 的 Codex 正常流程与供应商缺失分支,并抽取 `switch_provider_internal` 以复用 `AppError`,通过 `switch_provider_test_hook` 暴露测试入口;同时共享 `tests/support.rs` 提供隔离 HOME / 互斥工具函数。
|
||||
- 补充 Claude 切换集成测试,验证 live `settings.json` 覆写、新旧供应商快照回填以及 `.cc-switch/config.json` 持久化结果,确保阶段四提取服务层时拥有可回归的用例。
|
||||
- 增加 Codex 缺失 `auth` 场景测试,确认 `switch_provider_internal` 在关键字段缺失时返回带上下文的 `AppError`,同时保持内存状态未被污染。
|
||||
- 为配置导入命令抽取复用逻辑 `import_config_from_path` 并补充成功/失败集成测试,校验备份生成、状态同步、JSON 解析与文件缺失等错误回退路径;`export_config_to_file` 亦具备成功/缺失源文件的命令级回归。
|
||||
- 新增 `tests/mcp_commands.rs`,通过测试钩子覆盖 `import_default_config`、`import_mcp_from_claude`、`set_mcp_enabled` 等命令层行为,验证缺失文件/非法 JSON 的错误回滚以及成功路径落盘效果;阶段三目标达成,命令层关键边界已具备回归保障。
|
||||
- **阶段 4:服务层抽象 🚧(进行中)**
|
||||
- 新增 `services/provider.rs` 并实现 `ProviderService::switch` / `delete`,集中处理供应商切换、回填、MCP 同步等核心业务;命令层改为薄封装并在 `tests/provider_service.rs`、`tests/provider_commands.rs` 中完成成功与失败路径的集成验证。
|
||||
- 新增 `services/mcp.rs` 提供 `McpService`,封装 MCP 服务器的查询、增删改、启用同步与导入流程;命令层改为参数解析 + 调用服务,`tests/mcp_commands.rs` 直接使用 `McpService` 验证成功与失败路径,阶段三测试继续适配。
|
||||
- `McpService` 在内部先复制内存快照、释放写锁,再执行文件同步,避免阶段五升级后的 `RwLock` 在 I/O 场景被长时间占用;`upsert/delete/set_enabled/sync_enabled` 均已修正。
|
||||
- 新增 `services/config.rs` 提供 `ConfigService`,统一处理配置导入导出、备份与 live 同步;命令层迁移至 `commands/import_export.rs`,在落盘操作前释放锁并复用现有集成测试。
|
||||
- 新增 `services/speedtest.rs` 并实现 `SpeedtestService::test_endpoints`,将 URL 校验、超时裁剪与网络请求封装在服务层,命令改为薄封装;补充单元测试覆盖空列表与非法 URL 分支。
|
||||
- 后续可选:应用设置(Store)命令仍较薄,可按需评估是否抽象;当前阶段四核心服务已基本齐备。
|
||||
- **阶段 5:锁与阻塞优化 ✅(首轮)**
|
||||
- `AppState` 已由 `Mutex<MultiAppConfig>` 切换为 `RwLock<MultiAppConfig>`,托盘、命令与测试均按读写语义区分 `read()` / `write()`;`cargo test` 全量通过验证并未破坏现有流程。
|
||||
- 针对高开销 IO 的配置导入/导出命令提取 `load_config_for_import`,并通过 `tauri::async_runtime::spawn_blocking` 将文件读写与备份迁至阻塞线程,保持命令处理线程轻量。
|
||||
- 其余命令梳理后确认仍属轻量同步操作,暂不额外引入 `spawn_blocking`;若后续出现新的长耗时流程,再按同一模式扩展。
|
||||
|
||||
## 渐进式重构路线
|
||||
|
||||
### 阶段 1:统一错误处理(高收益 / 低风险)
|
||||
- 新增 `src-tauri/src/error.rs`,定义 `AppError`。
|
||||
- 底层文件 IO、配置解析等函数返回 `Result<T, AppError>`。
|
||||
- 命令层通过 `?` 自动传播,最终 `.map_err(Into::into)`。
|
||||
- 预估 3-5 天,立即启动。
|
||||
|
||||
### 阶段 2:拆分 `commands.rs`(高收益 / 中风险)
|
||||
- 按业务拆分为 `commands/provider.rs`、`commands/mcp.rs`、`commands/config.rs`、`commands/settings.rs`、`commands/misc.rs`。
|
||||
- `commands/mod.rs` 统一导出和注册。
|
||||
- 文件行数降低到 200-300 行/文件,职责单一。
|
||||
- 预估 5-7 天,可并行进行部分重构。
|
||||
|
||||
### 阶段 3:补充测试(中收益 / 中风险)
|
||||
- 引入 `tests/` 或 `src-tauri/tests/` 集成测试,覆盖供应商切换、MCP 同步、配置迁移。
|
||||
- 使用 `tempfile`/`tempdir` 隔离文件系统,组合少量回归脚本。
|
||||
- 预估 5-7 天,为后续重构提供安全网。
|
||||
|
||||
### 阶段 4:提取轻量服务层(中收益 / 中风险)
|
||||
- 新增 `services/provider_service.rs`、`services/mcp_service.rs`。
|
||||
- 不强制使用 trait;直接以自由函数/结构体实现业务流程。
|
||||
```rust
|
||||
pub struct ProviderService;
|
||||
impl ProviderService {
|
||||
pub fn switch(config: &mut MultiAppConfig, app: AppType, id: &str) -> Result<(), AppError> {
|
||||
// 业务流程:验证、回填、落盘、更新 current、触发事件
|
||||
}
|
||||
}
|
||||
```
|
||||
- 命令层负责参数解析,服务层处理业务逻辑,托盘逻辑重用同一接口。
|
||||
- 预估 7-10 天,可在测试补齐后执行。
|
||||
|
||||
### 阶段 5:锁与阻塞优化(低收益 / 低风险)
|
||||
- ✅ `AppState` 已从 `Mutex` 切换为 `RwLock`,命令与托盘读写按需区分,现有测试全部通过。
|
||||
- ✅ 配置导入/导出命令通过 `spawn_blocking` 处理高开销文件 IO;其他命令维持同步执行以避免不必要调度。
|
||||
- 🔄 持续监控:若后续引入新的批量迁移或耗时任务,再按相同模式扩展到阻塞线程;观察运行时锁竞争情况,必要时考虑进一步拆分状态或引入缓存。
|
||||
|
||||
## 测试策略
|
||||
- **优先覆盖场景**
|
||||
- 供应商切换:状态更新 + live 配置同步
|
||||
- MCP 同步:enabled 服务器快照与落盘
|
||||
- 配置迁移:归档、备份与版本升级
|
||||
- **推荐结构**
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod integration {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn switch_provider_updates_live_config() { /* ... */ }
|
||||
#[test]
|
||||
fn sync_mcp_to_codex_updates_claude_config() { /* ... */ }
|
||||
#[test]
|
||||
fn migration_preserves_backup() { /* ... */ }
|
||||
}
|
||||
```
|
||||
- 目标覆盖率:关键路径 >80%,文件 IO/迁移 >70%。
|
||||
|
||||
## 风险与对策
|
||||
- **测试不足** → 阶段 3 强制补齐,建立基础集成测试。
|
||||
- **重构跨度大** → 按阶段在独立分支推进(如 `refactor/backend-step1` 等)。
|
||||
- **回滚困难** → 每阶段结束打 tag(如 `v3.6.0-backend-step1`),保留回滚点。
|
||||
- **功能回归** → 重构后执行手动冒烟流程:供应商切换、托盘操作、MCP 同步、配置导入导出。
|
||||
|
||||
## 总结
|
||||
- 当前规模下不建议整体引入完整 DDD/四层架构,避免过度设计。
|
||||
- 建议遵循“错误统一 → 命令拆分 → 补测试 → 服务层抽象 → 锁优化”的渐进式策略。
|
||||
- 完成阶段 1-3 后即可显著提升可维护性与可靠性;阶段 4-5 可根据资源灵活安排。
|
||||
- 重构过程中同步维护文档与测试,确保团队成员对架构演进保持一致认知。
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,490 +0,0 @@
|
||||
# CC Switch 重构实施清单
|
||||
|
||||
> 用于跟踪重构进度的详细检查清单
|
||||
|
||||
**开始日期**: ___________
|
||||
**预计完成**: ___________
|
||||
**当前阶段**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 0: 准备阶段 (预计 1 天)
|
||||
|
||||
### 环境准备
|
||||
|
||||
- [ ] 创建新分支 `refactor/modernization`
|
||||
- [ ] 创建备份标签 `git tag backup-before-refactor`
|
||||
- [ ] 备份用户配置文件 `~/.cc-switch/config.json`
|
||||
- [ ] 通知团队成员重构开始
|
||||
|
||||
### 依赖安装
|
||||
|
||||
```bash
|
||||
pnpm add @tanstack/react-query
|
||||
pnpm add react-hook-form @hookform/resolvers
|
||||
pnpm add zod
|
||||
pnpm add sonner
|
||||
pnpm add next-themes
|
||||
pnpm add @radix-ui/react-dialog @radix-ui/react-dropdown-menu
|
||||
pnpm add @radix-ui/react-label @radix-ui/react-select
|
||||
pnpm add @radix-ui/react-slot @radix-ui/react-switch @radix-ui/react-tabs
|
||||
pnpm add class-variance-authority clsx tailwind-merge tailwindcss-animate
|
||||
```
|
||||
|
||||
- [ ] 安装核心依赖 (上述命令)
|
||||
- [ ] 验证依赖安装成功 `pnpm install`
|
||||
- [ ] 验证编译通过 `pnpm typecheck`
|
||||
|
||||
### 配置文件
|
||||
|
||||
- [ ] 创建 `components.json`
|
||||
- [ ] 更新 `tsconfig.json` 添加路径别名
|
||||
- [ ] 更新 `vite.config.mts` 添加路径解析
|
||||
- [ ] 验证开发服务器启动 `pnpm dev`
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 1: 基础设施 (预计 2-3 天)
|
||||
|
||||
### 1.1 工具函数和基础组件
|
||||
|
||||
- [ ] 创建 `src/lib/utils.ts` (cn 函数)
|
||||
- [ ] 创建 `src/components/ui/button.tsx`
|
||||
- [ ] 创建 `src/components/ui/dialog.tsx`
|
||||
- [ ] 创建 `src/components/ui/input.tsx`
|
||||
- [ ] 创建 `src/components/ui/label.tsx`
|
||||
- [ ] 创建 `src/components/ui/textarea.tsx`
|
||||
- [ ] 创建 `src/components/ui/select.tsx`
|
||||
- [ ] 创建 `src/components/ui/switch.tsx`
|
||||
- [ ] 创建 `src/components/ui/tabs.tsx`
|
||||
- [ ] 创建 `src/components/ui/sonner.tsx`
|
||||
- [ ] 创建 `src/components/ui/form.tsx`
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证所有 UI 组件可以正常导入
|
||||
- [ ] 创建一个测试页面验证组件样式
|
||||
|
||||
### 1.2 Query Client 设置
|
||||
|
||||
- [ ] 创建 `src/lib/query/queryClient.ts`
|
||||
- [ ] 配置默认选项 (retry, staleTime 等)
|
||||
- [ ] 导出 queryClient 实例
|
||||
|
||||
### 1.3 API 层
|
||||
|
||||
- [ ] 创建 `src/lib/api/providers.ts`
|
||||
- [ ] getAll
|
||||
- [ ] getCurrent
|
||||
- [ ] add
|
||||
- [ ] update
|
||||
- [ ] delete
|
||||
- [ ] switch
|
||||
- [ ] importDefault
|
||||
- [ ] updateTrayMenu
|
||||
|
||||
- [ ] 创建 `src/lib/api/settings.ts`
|
||||
- [ ] get
|
||||
- [ ] save
|
||||
|
||||
- [ ] 创建 `src/lib/api/mcp.ts`
|
||||
- [ ] getConfig
|
||||
- [ ] upsertServer
|
||||
- [ ] deleteServer
|
||||
|
||||
- [ ] 创建 `src/lib/api/index.ts` (聚合导出)
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证 API 调用不会出现运行时错误
|
||||
- [ ] 确认类型定义正确
|
||||
|
||||
### 1.4 Query Hooks
|
||||
|
||||
- [ ] 创建 `src/lib/query/queries.ts`
|
||||
- [ ] useProvidersQuery
|
||||
- [ ] useSettingsQuery
|
||||
- [ ] useMcpConfigQuery
|
||||
|
||||
- [ ] 创建 `src/lib/query/mutations.ts`
|
||||
- [ ] useAddProviderMutation
|
||||
- [ ] useSwitchProviderMutation
|
||||
- [ ] useDeleteProviderMutation
|
||||
- [ ] useUpdateProviderMutation
|
||||
- [ ] useSaveSettingsMutation
|
||||
|
||||
- [ ] 创建 `src/lib/query/index.ts` (聚合导出)
|
||||
|
||||
**测试**:
|
||||
- [ ] 在临时组件中测试每个 hook
|
||||
- [ ] 验证 loading/error 状态正确
|
||||
- [ ] 验证缓存和自动刷新工作
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 2: 核心功能重构 (预计 3-4 天)
|
||||
|
||||
### 2.1 主题系统
|
||||
|
||||
- [ ] 创建 `src/components/theme-provider.tsx`
|
||||
- [ ] 创建 `src/components/mode-toggle.tsx`
|
||||
- [ ] 更新 `src/index.css` 添加主题变量
|
||||
- [ ] 删除 `src/hooks/useDarkMode.ts`
|
||||
- [ ] 更新所有组件使用新的主题系统
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证主题切换正常工作
|
||||
- [ ] 验证系统主题跟随功能
|
||||
- [ ] 验证主题持久化
|
||||
|
||||
### 2.2 更新 main.tsx
|
||||
|
||||
- [ ] 引入 QueryClientProvider
|
||||
- [ ] 引入 ThemeProvider
|
||||
- [ ] 添加 Toaster 组件
|
||||
- [ ] 移除旧的 API 导入
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证应用可以正常启动
|
||||
- [ ] 验证 Context 正确传递
|
||||
|
||||
### 2.3 重构 App.tsx
|
||||
|
||||
- [ ] 使用 useProvidersQuery 替代手动状态管理
|
||||
- [ ] 移除所有 loadProviders 相关代码
|
||||
- [ ] 移除手动 notification 状态
|
||||
- [ ] 简化事件监听逻辑
|
||||
- [ ] 更新对话框为新的 Dialog 组件
|
||||
|
||||
**目标**: 将 412 行代码减少到 ~100 行
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证供应商列表正常加载
|
||||
- [ ] 验证切换 Claude/Codex 正常工作
|
||||
- [ ] 验证事件监听正常工作
|
||||
|
||||
### 2.4 重构 ProviderList
|
||||
|
||||
- [ ] 创建 `src/components/providers/ProviderList.tsx`
|
||||
- [ ] 使用 mutation hooks 处理操作
|
||||
- [ ] 移除 onNotify prop
|
||||
- [ ] 移除手动状态管理
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证供应商列表渲染
|
||||
- [ ] 验证切换操作
|
||||
- [ ] 验证删除操作
|
||||
|
||||
### 2.5 重构表单系统
|
||||
|
||||
- [ ] 创建 `src/lib/schemas/provider.ts` (Zod schema)
|
||||
- [ ] 创建 `src/components/providers/ProviderForm.tsx`
|
||||
- [ ] 使用 react-hook-form
|
||||
- [ ] 使用 zodResolver
|
||||
- [ ] 字段级验证
|
||||
|
||||
- [ ] 创建 `src/components/providers/AddProviderDialog.tsx`
|
||||
- [ ] 使用新的 Dialog 组件
|
||||
- [ ] 集成 ProviderForm
|
||||
- [ ] 使用 useAddProviderMutation
|
||||
|
||||
- [ ] 创建 `src/components/providers/EditProviderDialog.tsx`
|
||||
- [ ] 使用新的 Dialog 组件
|
||||
- [ ] 集成 ProviderForm
|
||||
- [ ] 使用 useUpdateProviderMutation
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证表单验证正常工作
|
||||
- [ ] 验证错误提示显示正确
|
||||
- [ ] 验证提交操作成功
|
||||
- [ ] 验证表单重置功能
|
||||
|
||||
### 2.6 清理旧组件
|
||||
|
||||
- [x] 删除 `src/components/AddProviderModal.tsx`
|
||||
- [x] 删除 `src/components/EditProviderModal.tsx`
|
||||
- [x] 更新所有引用这些组件的地方
|
||||
- [x] 删除 `src/components/ProviderForm.tsx` 及 `src/components/ProviderForm/`
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 3: 设置和辅助功能 (预计 2-3 天)
|
||||
|
||||
### 3.1 重构 SettingsDialog
|
||||
|
||||
- [ ] 创建 `src/components/settings/SettingsDialog.tsx`
|
||||
- [ ] 使用 Tabs 组件
|
||||
- [ ] 集成各个设置子组件
|
||||
|
||||
- [ ] 创建 `src/components/settings/GeneralSettings.tsx`
|
||||
- [ ] 语言设置
|
||||
- [ ] 配置目录设置
|
||||
- [ ] 其他通用设置
|
||||
|
||||
- [ ] 创建 `src/components/settings/AboutSection.tsx`
|
||||
- [ ] 版本信息
|
||||
- [ ] 更新检查
|
||||
- [ ] 链接
|
||||
|
||||
- [ ] 创建 `src/components/settings/ImportExportSection.tsx`
|
||||
- [ ] 导入功能
|
||||
- [ ] 导出功能
|
||||
|
||||
**目标**: 将 643 行拆分为 4-5 个小组件,每个 100-150 行
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证设置保存功能
|
||||
- [ ] 验证导入导出功能
|
||||
- [ ] 验证更新检查功能
|
||||
|
||||
### 3.2 重构通知系统
|
||||
|
||||
- [ ] 在所有 mutations 中使用 `toast` 替代 `showNotification`
|
||||
- [ ] 移除 App.tsx 中的 notification 状态
|
||||
- [ ] 移除自定义通知组件
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证成功通知显示
|
||||
- [ ] 验证错误通知显示
|
||||
- [ ] 验证通知自动消失
|
||||
|
||||
### 3.3 重构确认对话框
|
||||
|
||||
- [ ] 更新 `src/components/ConfirmDialog.tsx` 使用新的 Dialog
|
||||
- [ ] 或者直接使用 shadcn/ui 的 AlertDialog
|
||||
|
||||
**测试**:
|
||||
- [ ] 验证删除确认对话框
|
||||
- [ ] 验证其他确认场景
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 4: 清理和优化 (预计 1-2 天)
|
||||
|
||||
### 4.1 移除旧代码
|
||||
|
||||
- [x] 删除 `src/lib/styles.ts`
|
||||
- [x] 从 `src/lib/tauri-api.ts` 移除 `window.api` 绑定
|
||||
- [x] 精简 `src/lib/tauri-api.ts`,只保留事件监听相关
|
||||
- [x] 删除或更新 `src/vite-env.d.ts` 中的过时类型
|
||||
|
||||
### 4.2 代码审查
|
||||
|
||||
- [ ] 检查所有 TODO 注释
|
||||
- [x] 检查是否还有 `window.api` 调用
|
||||
- [ ] 检查是否还有手动状态管理
|
||||
- [x] 统一代码风格
|
||||
|
||||
### 4.3 类型检查
|
||||
|
||||
- [x] 运行 `pnpm typecheck` 确保无错误
|
||||
- [x] 修复所有类型错误
|
||||
- [x] 更新类型定义
|
||||
|
||||
### 4.4 性能优化
|
||||
|
||||
- [ ] 检查是否有不必要的重渲染
|
||||
- [ ] 添加必要的 React.memo
|
||||
- [ ] 优化 Query 缓存配置
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 阶段 5: 测试和修复 (预计 2-3 天)
|
||||
|
||||
### 5.1 功能测试
|
||||
|
||||
#### 供应商管理
|
||||
- [ ] 添加供应商 (Claude)
|
||||
- [ ] 添加供应商 (Codex)
|
||||
- [ ] 编辑供应商
|
||||
- [ ] 删除供应商
|
||||
- [ ] 切换供应商
|
||||
- [ ] 导入默认配置
|
||||
|
||||
#### 应用切换
|
||||
- [ ] Claude <-> Codex 切换
|
||||
- [ ] 切换后数据正确加载
|
||||
- [ ] 切换后托盘菜单更新
|
||||
|
||||
#### 设置
|
||||
- [ ] 保存通用设置
|
||||
- [ ] 切换语言
|
||||
- [ ] 配置目录选择
|
||||
- [ ] 导入配置
|
||||
- [ ] 导出配置
|
||||
|
||||
#### UI 交互
|
||||
- [ ] 主题切换 (亮色/暗色)
|
||||
- [ ] 对话框打开/关闭
|
||||
- [ ] 表单验证
|
||||
- [ ] Toast 通知
|
||||
|
||||
#### MCP 管理
|
||||
- [ ] 列表显示
|
||||
- [ ] 添加 MCP
|
||||
- [ ] 编辑 MCP
|
||||
- [ ] 删除 MCP
|
||||
- [ ] 启用/禁用 MCP
|
||||
|
||||
### 5.2 边界情况测试
|
||||
|
||||
- [ ] 空供应商列表
|
||||
- [ ] 无效配置文件
|
||||
- [ ] 网络错误
|
||||
- [ ] 后端错误响应
|
||||
- [ ] 并发操作
|
||||
- [ ] 表单输入边界值
|
||||
|
||||
### 5.3 兼容性测试
|
||||
|
||||
- [ ] Windows 测试
|
||||
- [ ] macOS 测试
|
||||
- [ ] Linux 测试
|
||||
|
||||
### 5.4 性能测试
|
||||
|
||||
- [ ] 100+ 供应商加载速度
|
||||
- [ ] 快速切换供应商
|
||||
- [ ] 内存使用情况
|
||||
- [ ] CPU 使用情况
|
||||
|
||||
### 5.5 Bug 修复
|
||||
|
||||
**Bug 列表** (发现后记录):
|
||||
|
||||
1. ___________
|
||||
- [ ] 已修复
|
||||
- [ ] 已验证
|
||||
|
||||
2. ___________
|
||||
- [ ] 已修复
|
||||
- [ ] 已验证
|
||||
|
||||
**完成时间**: ___________
|
||||
**遇到的问题**: ___________
|
||||
|
||||
---
|
||||
|
||||
## 📋 最终检查
|
||||
|
||||
### 代码质量
|
||||
|
||||
- [ ] 所有 TypeScript 错误已修复
|
||||
- [ ] 运行 `pnpm format` 格式化代码
|
||||
- [ ] 运行 `pnpm typecheck` 通过
|
||||
- [ ] 代码审查完成
|
||||
|
||||
### 文档更新
|
||||
|
||||
- [ ] 更新 `CLAUDE.md` 反映新架构
|
||||
- [ ] 更新 `README.md` (如有必要)
|
||||
- [ ] 添加 Migration Guide (可选)
|
||||
|
||||
### 性能基准
|
||||
|
||||
记录性能数据:
|
||||
|
||||
**旧版本**:
|
||||
- 启动时间: _____ms
|
||||
- 供应商加载: _____ms
|
||||
- 内存占用: _____MB
|
||||
|
||||
**新版本**:
|
||||
- 启动时间: _____ms
|
||||
- 供应商加载: _____ms
|
||||
- 内存占用: _____MB
|
||||
|
||||
### 代码统计
|
||||
|
||||
**代码行数对比**:
|
||||
|
||||
| 文件 | 旧版本 | 新版本 | 减少 |
|
||||
|------|--------|--------|------|
|
||||
| App.tsx | 412 | ~100 | -76% |
|
||||
| tauri-api.ts | 712 | ~50 | -93% |
|
||||
| ProviderForm.tsx | 271 | ~150 | -45% |
|
||||
| settings 模块 | 1046 | ~470 (拆分) | -55% |
|
||||
| **总计** | 2038 | ~700 | **-66%** |
|
||||
|
||||
---
|
||||
|
||||
## 📦 发布准备
|
||||
|
||||
### Pre-release 测试
|
||||
|
||||
- [ ] 创建 beta 版本 `v4.0.0-beta.1`
|
||||
- [ ] 在测试环境验证
|
||||
- [ ] 收集用户反馈
|
||||
|
||||
### 正式发布
|
||||
|
||||
- [ ] 合并到 main 分支
|
||||
- [ ] 创建 Release Tag `v4.0.0`
|
||||
- [ ] 更新 Changelog
|
||||
- [ ] 发布 GitHub Release
|
||||
- [ ] 通知用户更新
|
||||
|
||||
---
|
||||
|
||||
## 🚨 回滚触发条件
|
||||
|
||||
如果出现以下情况,考虑回滚:
|
||||
|
||||
- [ ] 重大功能无法使用
|
||||
- [ ] 用户数据丢失
|
||||
- [ ] 严重性能问题
|
||||
- [ ] 无法修复的兼容性问题
|
||||
|
||||
**回滚命令**:
|
||||
```bash
|
||||
git reset --hard backup-before-refactor
|
||||
# 或
|
||||
git revert <commit-range>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 总结报告
|
||||
|
||||
### 成功指标
|
||||
|
||||
- [ ] 所有现有功能正常工作
|
||||
- [ ] 代码量减少 40%+
|
||||
- [ ] 无用户数据丢失
|
||||
- [ ] 性能未下降
|
||||
|
||||
### 经验教训
|
||||
|
||||
**遇到的主要挑战**:
|
||||
1. ___________
|
||||
2. ___________
|
||||
3. ___________
|
||||
|
||||
**解决方案**:
|
||||
1. ___________
|
||||
2. ___________
|
||||
3. ___________
|
||||
|
||||
**未来改进**:
|
||||
1. ___________
|
||||
2. ___________
|
||||
3. ___________
|
||||
|
||||
---
|
||||
|
||||
**重构完成日期**: ___________
|
||||
**总耗时**: _____ 天
|
||||
**参与人员**: ___________
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,834 +0,0 @@
|
||||
# 重构快速参考指南
|
||||
|
||||
> 常见模式和代码示例的速查表
|
||||
|
||||
---
|
||||
|
||||
## 📑 目录
|
||||
|
||||
1. [React Query 使用](#react-query-使用)
|
||||
2. [react-hook-form 使用](#react-hook-form-使用)
|
||||
3. [shadcn/ui 组件使用](#shadcnui-组件使用)
|
||||
4. [代码迁移示例](#代码迁移示例)
|
||||
|
||||
---
|
||||
|
||||
## React Query 使用
|
||||
|
||||
### 基础查询
|
||||
|
||||
```typescript
|
||||
// 定义查询 Hook
|
||||
export const useProvidersQuery = (appId: AppId) => {
|
||||
return useQuery({
|
||||
queryKey: ['providers', appId],
|
||||
queryFn: async () => {
|
||||
const data = await providersApi.getAll(appId)
|
||||
return data
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 在组件中使用
|
||||
function MyComponent() {
|
||||
const { data, isLoading, error } = useProvidersQuery('claude')
|
||||
|
||||
if (isLoading) return <div>Loading...</div>
|
||||
if (error) return <div>Error: {error.message}</div>
|
||||
|
||||
return <div>{/* 使用 data */}</div>
|
||||
}
|
||||
```
|
||||
|
||||
### Mutation (变更操作)
|
||||
|
||||
```typescript
|
||||
// 定义 Mutation Hook
|
||||
export const useAddProviderMutation = (appId: AppId) => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (provider: Provider) => {
|
||||
return await providersApi.add(provider, appId)
|
||||
},
|
||||
onSuccess: () => {
|
||||
// 重新获取数据
|
||||
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||
toast.success('添加成功')
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
toast.error(`添加失败: ${error.message}`)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 在组件中使用
|
||||
function AddProviderDialog() {
|
||||
const mutation = useAddProviderMutation('claude')
|
||||
|
||||
const handleSubmit = (data: Provider) => {
|
||||
mutation.mutate(data)
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => handleSubmit(formData)}
|
||||
disabled={mutation.isPending}
|
||||
>
|
||||
{mutation.isPending ? '添加中...' : '添加'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 乐观更新
|
||||
|
||||
```typescript
|
||||
export const useSwitchProviderMutation = (appId: AppId) => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (providerId: string) => {
|
||||
return await providersApi.switch(providerId, appId)
|
||||
},
|
||||
// 乐观更新: 在请求发送前立即更新 UI
|
||||
onMutate: async (providerId) => {
|
||||
// 取消正在进行的查询
|
||||
await queryClient.cancelQueries({ queryKey: ['providers', appId] })
|
||||
|
||||
// 保存当前数据(以便回滚)
|
||||
const previousData = queryClient.getQueryData(['providers', appId])
|
||||
|
||||
// 乐观更新
|
||||
queryClient.setQueryData(['providers', appId], (old: any) => ({
|
||||
...old,
|
||||
currentProviderId: providerId,
|
||||
}))
|
||||
|
||||
return { previousData }
|
||||
},
|
||||
// 如果失败,回滚
|
||||
onError: (err, providerId, context) => {
|
||||
queryClient.setQueryData(['providers', appId], context?.previousData)
|
||||
toast.error('切换失败')
|
||||
},
|
||||
// 无论成功失败,都重新获取数据
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### 依赖查询
|
||||
|
||||
```typescript
|
||||
// 第二个查询依赖第一个查询的结果
|
||||
const { data: providers } = useProvidersQuery(appId)
|
||||
const currentProviderId = providers?.currentProviderId
|
||||
|
||||
const { data: currentProvider } = useQuery({
|
||||
queryKey: ['provider', currentProviderId],
|
||||
queryFn: () => providersApi.getById(currentProviderId!),
|
||||
enabled: !!currentProviderId, // 只有当 ID 存在时才执行
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## react-hook-form 使用
|
||||
|
||||
### 基础表单
|
||||
|
||||
```typescript
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { z } from 'zod'
|
||||
|
||||
// 定义验证 schema
|
||||
const schema = z.object({
|
||||
name: z.string().min(1, '请输入名称'),
|
||||
email: z.string().email('邮箱格式不正确'),
|
||||
age: z.number().min(18, '年龄必须大于18'),
|
||||
})
|
||||
|
||||
type FormData = z.infer<typeof schema>
|
||||
|
||||
function MyForm() {
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
age: 0,
|
||||
},
|
||||
})
|
||||
|
||||
const onSubmit = (data: FormData) => {
|
||||
console.log(data)
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<input {...form.register('name')} />
|
||||
{form.formState.errors.name && (
|
||||
<span>{form.formState.errors.name.message}</span>
|
||||
)}
|
||||
|
||||
<button type="submit">提交</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 使用 shadcn/ui Form 组件
|
||||
|
||||
```typescript
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
function MyForm() {
|
||||
const form = useForm<FormData>({
|
||||
resolver: zodResolver(schema),
|
||||
})
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>名称</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="请输入名称" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="submit">提交</Button>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 动态表单验证
|
||||
|
||||
```typescript
|
||||
// 根据条件动态验证
|
||||
const schema = z.object({
|
||||
type: z.enum(['official', 'custom']),
|
||||
apiKey: z.string().optional(),
|
||||
baseUrl: z.string().optional(),
|
||||
}).refine(
|
||||
(data) => {
|
||||
// 如果是自定义供应商,必须填写 baseUrl
|
||||
if (data.type === 'custom') {
|
||||
return !!data.baseUrl
|
||||
}
|
||||
return true
|
||||
},
|
||||
{
|
||||
message: '自定义供应商必须填写 Base URL',
|
||||
path: ['baseUrl'],
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### 手动触发验证
|
||||
|
||||
```typescript
|
||||
function MyForm() {
|
||||
const form = useForm<FormData>()
|
||||
|
||||
const handleBlur = async () => {
|
||||
// 验证单个字段
|
||||
await form.trigger('name')
|
||||
|
||||
// 验证多个字段
|
||||
await form.trigger(['name', 'email'])
|
||||
|
||||
// 验证所有字段
|
||||
const isValid = await form.trigger()
|
||||
}
|
||||
|
||||
return <form>...</form>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## shadcn/ui 组件使用
|
||||
|
||||
### Dialog (对话框)
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
function MyDialog() {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>标题</DialogTitle>
|
||||
<DialogDescription>描述信息</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{/* 内容 */}
|
||||
<div>对话框内容</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={handleConfirm}>确认</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Select (选择器)
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
|
||||
function MySelect() {
|
||||
const [value, setValue] = useState('')
|
||||
|
||||
return (
|
||||
<Select value={value} onValueChange={setValue}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="请选择" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="option1">选项1</SelectItem>
|
||||
<SelectItem value="option2">选项2</SelectItem>
|
||||
<SelectItem value="option3">选项3</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Tabs (标签页)
|
||||
|
||||
```typescript
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
|
||||
function MyTabs() {
|
||||
return (
|
||||
<Tabs defaultValue="tab1">
|
||||
<TabsList>
|
||||
<TabsTrigger value="tab1">标签1</TabsTrigger>
|
||||
<TabsTrigger value="tab2">标签2</TabsTrigger>
|
||||
<TabsTrigger value="tab3">标签3</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="tab1">
|
||||
<div>标签1的内容</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tab2">
|
||||
<div>标签2的内容</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tab3">
|
||||
<div>标签3的内容</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Toast 通知 (Sonner)
|
||||
|
||||
```typescript
|
||||
import { toast } from 'sonner'
|
||||
|
||||
// 成功通知
|
||||
toast.success('操作成功')
|
||||
|
||||
// 错误通知
|
||||
toast.error('操作失败')
|
||||
|
||||
// 加载中
|
||||
const toastId = toast.loading('处理中...')
|
||||
// 完成后更新
|
||||
toast.success('处理完成', { id: toastId })
|
||||
// 或
|
||||
toast.dismiss(toastId)
|
||||
|
||||
// 自定义持续时间
|
||||
toast.success('消息', { duration: 5000 })
|
||||
|
||||
// 带操作按钮
|
||||
toast('确认删除?', {
|
||||
action: {
|
||||
label: '删除',
|
||||
onClick: () => handleDelete(),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 代码迁移示例
|
||||
|
||||
### 示例 1: 状态管理迁移
|
||||
|
||||
**旧代码** (手动状态管理):
|
||||
|
||||
```typescript
|
||||
const [providers, setProviders] = useState<Record<string, Provider>>({})
|
||||
const [currentProviderId, setCurrentProviderId] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<Error | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
const data = await window.api.getProviders(appType)
|
||||
const currentId = await window.api.getCurrentProvider(appType)
|
||||
setProviders(data)
|
||||
setCurrentProviderId(currentId)
|
||||
} catch (err) {
|
||||
setError(err as Error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
load()
|
||||
}, [appId])
|
||||
```
|
||||
|
||||
**新代码** (React Query):
|
||||
|
||||
```typescript
|
||||
const { data, isLoading, error } = useProvidersQuery(appId)
|
||||
const providers = data?.providers || {}
|
||||
const currentProviderId = data?.currentProviderId || ''
|
||||
```
|
||||
|
||||
**减少**: 从 20+ 行到 3 行
|
||||
|
||||
---
|
||||
|
||||
### 示例 2: 表单验证迁移
|
||||
|
||||
**旧代码** (手动验证):
|
||||
|
||||
```typescript
|
||||
const [name, setName] = useState('')
|
||||
const [nameError, setNameError] = useState('')
|
||||
const [apiKey, setApiKey] = useState('')
|
||||
const [apiKeyError, setApiKeyError] = useState('')
|
||||
|
||||
const validate = () => {
|
||||
let valid = true
|
||||
|
||||
if (!name.trim()) {
|
||||
setNameError('请输入名称')
|
||||
valid = false
|
||||
} else {
|
||||
setNameError('')
|
||||
}
|
||||
|
||||
if (!apiKey.trim()) {
|
||||
setApiKeyError('请输入 API Key')
|
||||
valid = false
|
||||
} else if (apiKey.length < 10) {
|
||||
setApiKeyError('API Key 长度不足')
|
||||
valid = false
|
||||
} else {
|
||||
setApiKeyError('')
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (validate()) {
|
||||
// 提交
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<input value={name} onChange={e => setName(e.target.value)} />
|
||||
{nameError && <span>{nameError}</span>}
|
||||
|
||||
<input value={apiKey} onChange={e => setApiKey(e.target.value)} />
|
||||
{apiKeyError && <span>{apiKeyError}</span>}
|
||||
|
||||
<button onClick={handleSubmit}>提交</button>
|
||||
</form>
|
||||
)
|
||||
```
|
||||
|
||||
**新代码** (react-hook-form + zod):
|
||||
|
||||
```typescript
|
||||
const schema = z.object({
|
||||
name: z.string().min(1, '请输入名称'),
|
||||
apiKey: z.string().min(10, 'API Key 长度不足'),
|
||||
})
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(schema),
|
||||
})
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="apiKey"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Button type="submit">提交</Button>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
```
|
||||
|
||||
**减少**: 从 40+ 行到 30 行,且更健壮
|
||||
|
||||
---
|
||||
|
||||
### 示例 3: 通知系统迁移
|
||||
|
||||
**旧代码** (自定义通知):
|
||||
|
||||
```typescript
|
||||
const [notification, setNotification] = useState<{
|
||||
message: string
|
||||
type: 'success' | 'error'
|
||||
} | null>(null)
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
|
||||
const showNotification = (message: string, type: 'success' | 'error') => {
|
||||
setNotification({ message, type })
|
||||
setIsVisible(true)
|
||||
setTimeout(() => {
|
||||
setIsVisible(false)
|
||||
setTimeout(() => setNotification(null), 300)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{notification && (
|
||||
<div className={`notification ${isVisible ? 'visible' : ''} ${notification.type}`}>
|
||||
{notification.message}
|
||||
</div>
|
||||
)}
|
||||
{/* 其他内容 */}
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
**新代码** (Sonner):
|
||||
|
||||
```typescript
|
||||
import { toast } from 'sonner'
|
||||
|
||||
// 在需要的地方直接调用
|
||||
toast.success('操作成功')
|
||||
toast.error('操作失败')
|
||||
|
||||
// 在 main.tsx 中只需添加一次
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
|
||||
<Toaster />
|
||||
```
|
||||
|
||||
**减少**: 从 20+ 行到 1 行调用
|
||||
|
||||
---
|
||||
|
||||
### 示例 4: 对话框迁移
|
||||
|
||||
**旧代码** (自定义 Modal):
|
||||
|
||||
```typescript
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setIsOpen(true)}>打开</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="modal-backdrop" onClick={() => setIsOpen(false)}>
|
||||
<div className="modal-content" onClick={e => e.stopPropagation()}>
|
||||
<div className="modal-header">
|
||||
<h2>标题</h2>
|
||||
<button onClick={() => setIsOpen(false)}>×</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
{/* 内容 */}
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
<button onClick={() => setIsOpen(false)}>取消</button>
|
||||
<button onClick={handleConfirm}>确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
**新代码** (shadcn/ui Dialog):
|
||||
|
||||
```typescript
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button onClick={() => setIsOpen(true)}>打开</Button>
|
||||
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>标题</DialogTitle>
|
||||
</DialogHeader>
|
||||
{/* 内容 */}
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setIsOpen(false)}>取消</Button>
|
||||
<Button onClick={handleConfirm}>确认</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 无需自定义样式
|
||||
- 内置无障碍支持
|
||||
- 自动管理焦点和 ESC 键
|
||||
|
||||
---
|
||||
|
||||
### 示例 5: API 调用迁移
|
||||
|
||||
**旧代码** (window.api):
|
||||
|
||||
```typescript
|
||||
// 添加供应商
|
||||
const handleAdd = async (provider: Provider) => {
|
||||
try {
|
||||
await window.api.addProvider(provider, appType)
|
||||
await loadProviders()
|
||||
showNotification('添加成功', 'success')
|
||||
} catch (error) {
|
||||
showNotification('添加失败', 'error')
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**新代码** (React Query Mutation):
|
||||
|
||||
```typescript
|
||||
// 在组件中
|
||||
const addMutation = useAddProviderMutation(appId)
|
||||
|
||||
const handleAdd = (provider: Provider) => {
|
||||
addMutation.mutate(provider)
|
||||
// 成功和错误处理已在 mutation 定义中处理
|
||||
}
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- 自动处理 loading 状态
|
||||
- 统一的错误处理
|
||||
- 自动刷新数据
|
||||
- 更少的样板代码
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何在 mutation 成功后关闭对话框?
|
||||
|
||||
```typescript
|
||||
const mutation = useAddProviderMutation(appId)
|
||||
|
||||
const handleSubmit = (data: Provider) => {
|
||||
mutation.mutate(data, {
|
||||
onSuccess: () => {
|
||||
setIsOpen(false) // 关闭对话框
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### Q: 如何在表单中使用异步验证?
|
||||
|
||||
```typescript
|
||||
const schema = z.object({
|
||||
name: z.string().refine(
|
||||
async (name) => {
|
||||
// 检查名称是否已存在
|
||||
const exists = await checkNameExists(name)
|
||||
return !exists
|
||||
},
|
||||
{ message: '名称已存在' }
|
||||
),
|
||||
})
|
||||
```
|
||||
|
||||
### Q: 如何手动刷新 Query 数据?
|
||||
|
||||
```typescript
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
// 方式1: 使缓存失效,触发重新获取
|
||||
queryClient.invalidateQueries({ queryKey: ['providers', appId] })
|
||||
|
||||
// 方式2: 直接刷新
|
||||
queryClient.refetchQueries({ queryKey: ['providers', appId] })
|
||||
|
||||
// 方式3: 更新缓存数据
|
||||
queryClient.setQueryData(['providers', appId], newData)
|
||||
```
|
||||
|
||||
### Q: 如何在组件外部使用 toast?
|
||||
|
||||
```typescript
|
||||
// 直接导入并使用即可
|
||||
import { toast } from 'sonner'
|
||||
|
||||
export const someUtil = () => {
|
||||
toast.success('工具函数中的通知')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 调试技巧
|
||||
|
||||
### React Query DevTools
|
||||
|
||||
```typescript
|
||||
// 在 main.tsx 中添加
|
||||
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
|
||||
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</QueryClientProvider>
|
||||
```
|
||||
|
||||
### 查看表单状态
|
||||
|
||||
```typescript
|
||||
const form = useForm()
|
||||
|
||||
// 在开发模式下打印表单状态
|
||||
console.log('Form values:', form.watch())
|
||||
console.log('Form errors:', form.formState.errors)
|
||||
console.log('Is valid:', form.formState.isValid)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
### 1. 避免不必要的重渲染
|
||||
|
||||
```typescript
|
||||
// 使用 React.memo
|
||||
export const ProviderCard = React.memo(({ provider, onEdit }: Props) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// 或使用 useMemo
|
||||
const sortedProviders = useMemo(
|
||||
() => Object.values(providers).sort(...),
|
||||
[providers]
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Query 配置优化
|
||||
|
||||
```typescript
|
||||
const { data } = useQuery({
|
||||
queryKey: ['providers', appId],
|
||||
queryFn: fetchProviders,
|
||||
staleTime: 1000 * 60 * 5, // 5分钟内不重新获取
|
||||
gcTime: 1000 * 60 * 10, // 10分钟后清除缓存
|
||||
})
|
||||
```
|
||||
|
||||
### 3. 表单性能优化
|
||||
|
||||
```typescript
|
||||
// 使用 mode 控制验证时机
|
||||
const form = useForm({
|
||||
mode: 'onBlur', // 失去焦点时验证
|
||||
// mode: 'onChange', // 每次输入都验证(较慢)
|
||||
// mode: 'onSubmit', // 提交时验证(最快)
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**提示**: 将此文档保存在浏览器书签或编辑器中,方便随时查阅!
|
||||
@@ -1,73 +0,0 @@
|
||||
# 前端测试开发计划
|
||||
|
||||
## 1. 背景与目标
|
||||
- **背景**:v3.5.0 起前端功能快速扩张(供应商管理、MCP、导入导出、端点测速、国际化),缺失系统化测试导致回归风险与人工验证成本攀升。
|
||||
- **目标**:在 3 个迭代内建立覆盖关键业务的自动化测试体系,形成稳定的手动冒烟流程,并将测试执行纳入 CI/CD。
|
||||
|
||||
## 2. 范围与优先级
|
||||
| 范围 | 内容 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| 供应商管理 | 列表、排序、预设/自定义表单、切换、复制、删除 | P0 |
|
||||
| 配置导入导出 | JSON 校验、备份、进度反馈、失败回滚 | P0 |
|
||||
| MCP 管理 | 列表、启停、模板、命令校验 | P1 |
|
||||
| 设置面板 | 主题/语言切换、目录设置、关于、更新检查 | P1 |
|
||||
| 端点速度测试 & 使用脚本 | 启动测试、状态指示、脚本保存 | P2 |
|
||||
| 国际化 | 中英切换、缺省文案回退 | P2 |
|
||||
|
||||
## 3. 测试分层策略
|
||||
- **单元测试(Vitest)**:纯函数与 Hook(`useProviderActions`、`useSettingsForm`、`useDragSort`、`useImportExport` 等)验证数据处理、错误分支、排序逻辑。
|
||||
- **组件测试(React Testing Library)**:关键组件(`ProviderList`、`AddProviderDialog`、`SettingsDialog`、`McpPanel`)模拟交互、校验、提示;结合 MSW 模拟 API。
|
||||
- **集成测试(App 级别)**:挂载 `App.tsx`,覆盖应用切换、编辑模式、导入导出回调、语言切换,验证状态同步与 toast 提示。
|
||||
- **端到端测试(Playwright)**:依赖 `pnpm dev:renderer`,串联供应商 CRUD、排序拖拽、MCP 启停、语言切换即时刷新、更新检查跳转。
|
||||
- **手动冒烟**:Tauri 桌面包 + dev server 双通道,验证托盘、系统权限、真实文件写入。
|
||||
|
||||
## 4. 环境与工具
|
||||
- 依赖:Node 18+、pnpm 8+、Vitest、React Testing Library、MSW、Playwright、Testing Library User Event、Playwright Trace Viewer。
|
||||
- 配置要点:
|
||||
- 在 `tsconfig` 中共享别名,Vitest 配合 `vite.config.mts`。
|
||||
- `setupTests.ts` 统一注册 MSW/RTL、自定义 matcher。
|
||||
- Playwright 使用多浏览器矩阵(Chromium 必选,WebKit 可选),并共享 `.env.test`。
|
||||
- Mock `@tauri-apps/api` 与 `providersApi`/`settingsApi`,隔离 Rust 层。
|
||||
|
||||
## 5. 自动化建设里程碑
|
||||
| 周期 | 目标 | 交付 |
|
||||
| --- | --- | --- |
|
||||
| Sprint 1 | Vitest 基础设施、核心 Hook 单测(P0) | `pnpm test:unit`、覆盖率报告、10+ 用例 |
|
||||
| Sprint 2 | 组件/集成测试、MSW Mock 层 | `pnpm test:component`、App 主流程用例 |
|
||||
| Sprint 3 | Playwright E2E、CI 接入 | `pnpm test:e2e`、CI job、冒烟脚本 |
|
||||
| 持续 | 回归用例补齐、视觉比对探索 | Playwright Trace、截图基线 |
|
||||
|
||||
## 6. 用例规划概览
|
||||
- **供应商管理**:新增(预设+自定义)、编辑校验、复制排序、切换失败回退、删除确认、使用脚本保存。
|
||||
- **导入导出**:成功、重复导入、校验失败、备份失败提示、导入后托盘刷新。
|
||||
- **MCP**:模板应用、协议切换(stdio/http)、命令校验、启停状态持久化。
|
||||
- **设置**:主题/语言即时生效、目录路径更新、更新检查按钮外链、关于信息渲染。
|
||||
- **端点速度测试**:触发测试、loading/成功/失败状态、指示器颜色、测速数据排序。
|
||||
- **国际化**:默认中文、切换英文后主界面/对话框文案变化、缺失 key fallback。
|
||||
|
||||
## 7. 数据与 Mock 策略
|
||||
- 在 `tests/fixtures/` 维护标准供应商、MCP、设置数据集。
|
||||
- 使用 MSW 拦截 `providersApi`、`settingsApi`、`providersApi.onSwitched` 等调用;提供延迟/错误注入接口以覆盖异常分支。
|
||||
- Playwright 端提供临时用户目录(`TMP_CC_SWITCH_HOME`)+ 伪配置文件,以验证真实文件交互路径。
|
||||
|
||||
## 8. 质量门禁与指标
|
||||
- 覆盖率目标:单元 ≥75%,分支 ≥70%,逐步提升至 80%+。
|
||||
- CI 阶段:`pnpm typecheck` → `pnpm format:check` → `pnpm test:unit` → `pnpm test:component` → `pnpm test:e2e`(可在 nightly 执行)。
|
||||
- 缺陷处理:修复前补充最小复现测试;E2E 冒烟必须陪跑重大功能发布。
|
||||
|
||||
## 9. 工作流与职责
|
||||
- **测试负责人**:前端工程师轮值;负责测试计划维护、PR 流水线健康。
|
||||
- **开发者职责**:提交功能需附新增/更新测试、列出手动验证步骤、如涉及 UI 提交截图。
|
||||
- **Code Review 检查**:测试覆盖说明、mock 合理性、易读性。
|
||||
|
||||
## 10. 风险与缓解
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| --- | --- | --- |
|
||||
| Tauri API Mock 难度高 | 单测无法稳定 | 抽象 API 适配层 + MSW 统一模拟 |
|
||||
| Playwright 运行时间长 | CI 变慢 | 拆分冒烟/完整版,冒烟只跑关键路径 |
|
||||
| 国际化文案频繁变化 | 用例脆弱 | 优先断言 data-testid/结构,文案使用翻译 key |
|
||||
|
||||
## 11. 输出与维护
|
||||
- 文档维护者:前端团队;每个版本更新后检查测试覆盖清单。
|
||||
- 交付物:测试报告(CI artifact)、Playwright Trace、覆盖率摘要。
|
||||
- 复盘:每次发布后召开 30 分钟测试复盘,记录缺陷、补齐用例。
|
||||
@@ -1,485 +0,0 @@
|
||||
# OpenCode 第四应用支持实现计划
|
||||
|
||||
> **范围说明**:本计划暂不包含统一供应商(UniversalProvider)对 OpenCode 的支持,以降低初期实现复杂度。
|
||||
|
||||
## 概述
|
||||
|
||||
为 CC Switch 添加 OpenCode 支持,这是第四个受管理的 CLI 应用。OpenCode 的核心差异在于采用**累加式**供应商管理(多供应商共存,应用内热切换),而非现有三应用的**替换式**管理。
|
||||
|
||||
## 关键设计决策
|
||||
|
||||
| 特性 | Claude/Codex/Gemini | OpenCode |
|
||||
|------|---------------------|----------|
|
||||
| 供应商模式 | 替换式(单一活跃) | 累加式(多供应商共存) |
|
||||
| UI 按钮 | 启用/切换 | 添加/删除 |
|
||||
| is_current | 需要 | 不需要 |
|
||||
| 代理/故障转移 | 支持 | 不支持 |
|
||||
| API 格式字段 | 无 | 需要(npm 包名) |
|
||||
| 配置文件 | 各自独立 | `~/.config/opencode/opencode.json` |
|
||||
|
||||
## 配置文件格式
|
||||
|
||||
### 供应商配置
|
||||
```json
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"provider": {
|
||||
"provider-id": {
|
||||
"npm": "@ai-sdk/openai-compatible",
|
||||
"name": "Provider Name",
|
||||
"options": {
|
||||
"baseURL": "https://api.example.com/v1",
|
||||
"apiKey": "{env:API_KEY}"
|
||||
},
|
||||
"models": {
|
||||
"model-id": { "name": "Model Name" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MCP 配置
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"remote-server": {
|
||||
"type": "remote",
|
||||
"url": "https://example.com/mcp",
|
||||
"enabled": true
|
||||
},
|
||||
"local-server": {
|
||||
"type": "local",
|
||||
"command": ["npx", "-y", "my-mcp-command"],
|
||||
"enabled": true,
|
||||
"environment": { "KEY": "value" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实现步骤
|
||||
|
||||
### Phase 1: 后端数据结构扩展
|
||||
|
||||
#### 1.1 AppType 枚举扩展
|
||||
**文件**: `src-tauri/src/app_config.rs`
|
||||
|
||||
```rust
|
||||
pub enum AppType {
|
||||
Claude,
|
||||
Codex,
|
||||
Gemini,
|
||||
OpenCode, // 新增
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 McpApps / SkillApps 扩展
|
||||
**文件**: `src-tauri/src/app_config.rs`
|
||||
|
||||
```rust
|
||||
pub struct McpApps {
|
||||
pub claude: bool,
|
||||
pub codex: bool,
|
||||
pub gemini: bool,
|
||||
pub opencode: bool, // 新增
|
||||
}
|
||||
|
||||
pub struct SkillApps {
|
||||
pub claude: bool,
|
||||
pub codex: bool,
|
||||
pub gemini: bool,
|
||||
pub opencode: bool, // 新增
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 数据库 Schema 迁移
|
||||
**文件**: `src-tauri/src/database/schema.rs`
|
||||
|
||||
- `SCHEMA_VERSION` 递增
|
||||
- 添加迁移:
|
||||
```sql
|
||||
ALTER TABLE mcp_servers ADD COLUMN enabled_opencode BOOLEAN NOT NULL DEFAULT 0;
|
||||
ALTER TABLE skills ADD COLUMN enabled_opencode BOOLEAN NOT NULL DEFAULT 0;
|
||||
```
|
||||
|
||||
### Phase 2: OpenCode 供应商数据结构
|
||||
|
||||
#### 2.1 OpenCode 专属配置结构
|
||||
**文件**: `src-tauri/src/provider.rs`(或新建 `opencode_provider.rs`)
|
||||
|
||||
```rust
|
||||
/// OpenCode 供应商的 settings_config 结构
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenCodeProviderConfig {
|
||||
/// AI SDK 包名,如 "@ai-sdk/openai-compatible"
|
||||
pub npm: String,
|
||||
/// 供应商选项
|
||||
pub options: OpenCodeProviderOptions,
|
||||
/// 模型定义
|
||||
pub models: HashMap<String, OpenCodeModel>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenCodeProviderOptions {
|
||||
#[serde(rename = "baseURL", skip_serializing_if = "Option::is_none")]
|
||||
pub base_url: Option<String>,
|
||||
#[serde(rename = "apiKey", skip_serializing_if = "Option::is_none")]
|
||||
pub api_key: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub headers: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenCodeModel {
|
||||
pub name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub limit: Option<OpenCodeModelLimit>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenCodeModelLimit {
|
||||
pub context: Option<u64>,
|
||||
pub output: Option<u64>,
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: OpenCode Live 配置读写
|
||||
|
||||
#### 3.1 新建 OpenCode 配置模块
|
||||
**文件**: `src-tauri/src/opencode_config.rs`
|
||||
|
||||
核心功能:
|
||||
- `get_opencode_config_path()` → `~/.config/opencode/opencode.json`
|
||||
- `read_opencode_config()` → 读取整个配置文件
|
||||
- `write_opencode_config()` → 原子写入配置文件
|
||||
- `get_providers()` → 获取 `provider` 对象
|
||||
- `set_provider(id, config)` → 添加/更新供应商
|
||||
- `remove_provider(id)` → 删除供应商
|
||||
- `get_mcp_servers()` → 获取 `mcp` 对象
|
||||
- `set_mcp_server(id, config)` → 添加/更新 MCP 服务器
|
||||
- `remove_mcp_server(id)` → 删除 MCP 服务器
|
||||
|
||||
### Phase 4: MCP 同步模块
|
||||
|
||||
#### 4.1 新建 OpenCode MCP 同步
|
||||
**文件**: `src-tauri/src/mcp/opencode.rs`
|
||||
|
||||
```rust
|
||||
/// 同步所有 enabled_opencode=true 的服务器到 OpenCode 配置
|
||||
pub fn sync_enabled_to_opencode(config: &MultiAppConfig) -> Result<(), AppError>
|
||||
|
||||
/// 同步单个服务器
|
||||
pub fn sync_single_server_to_opencode(
|
||||
config: &MultiAppConfig,
|
||||
id: &str,
|
||||
server_spec: &Value
|
||||
) -> Result<(), AppError>
|
||||
|
||||
/// 从 OpenCode 配置移除服务器
|
||||
pub fn remove_server_from_opencode(id: &str) -> Result<(), AppError>
|
||||
|
||||
/// 从 OpenCode 配置导入服务器
|
||||
pub fn import_from_opencode(config: &mut MultiAppConfig) -> Result<usize, AppError>
|
||||
```
|
||||
|
||||
**格式转换**:
|
||||
| CC Switch 统一格式 | OpenCode 格式 |
|
||||
|-------------------|---------------|
|
||||
| `type: "stdio"` | `type: "local"` |
|
||||
| `command` + `args` | `command: [cmd, ...args]` |
|
||||
| `env` | `environment` |
|
||||
| `type: "sse"/"http"` | `type: "remote"` |
|
||||
| `url` | `url` |
|
||||
|
||||
### Phase 5: 供应商服务层
|
||||
|
||||
#### 5.1 OpenCode 供应商服务
|
||||
**文件**: `src-tauri/src/services/provider/opencode.rs`
|
||||
|
||||
核心方法:
|
||||
```rust
|
||||
/// 获取所有 OpenCode 供应商
|
||||
pub fn list(state: &AppState) -> Result<IndexMap<String, Provider>, AppError>
|
||||
|
||||
/// 添加供应商(同时写入 live 配置)
|
||||
pub fn add(state: &AppState, provider: Provider) -> Result<bool, AppError>
|
||||
|
||||
/// 更新供应商
|
||||
pub fn update(state: &AppState, provider: Provider) -> Result<bool, AppError>
|
||||
|
||||
/// 删除供应商(同时从 live 配置移除)
|
||||
pub fn delete(state: &AppState, id: &str) -> Result<(), AppError>
|
||||
|
||||
/// 从 live 配置导入供应商到数据库
|
||||
pub fn import_from_live(state: &AppState) -> Result<usize, AppError>
|
||||
```
|
||||
|
||||
**关键差异**:
|
||||
- 不需要 `switch()` 方法
|
||||
- 不需要 `is_current` 管理
|
||||
- `add()` 自动写入 live
|
||||
- `delete()` 自动从 live 移除
|
||||
|
||||
### Phase 6: Tauri 命令扩展
|
||||
|
||||
#### 6.1 更新现有命令
|
||||
**文件**: `src-tauri/src/commands/providers.rs`
|
||||
|
||||
- 所有命令支持 `app_type = "opencode"`
|
||||
- OpenCode 特定逻辑分支
|
||||
|
||||
#### 6.2 新增 OpenCode 专属命令(如需要)
|
||||
```rust
|
||||
#[tauri::command]
|
||||
pub async fn opencode_sync_all_providers(state: State<'_, AppState>) -> Result<(), AppError>
|
||||
```
|
||||
|
||||
### Phase 7: 前端类型定义
|
||||
|
||||
#### 7.1 TypeScript 类型扩展
|
||||
**文件**: `src/types.ts`
|
||||
|
||||
```typescript
|
||||
// AppId 扩展
|
||||
type AppId = "claude" | "codex" | "gemini" | "opencode";
|
||||
|
||||
// OpenCode 专属配置
|
||||
interface OpenCodeProviderConfig {
|
||||
npm: string; // AI SDK 包名
|
||||
options: {
|
||||
baseURL?: string;
|
||||
apiKey?: string;
|
||||
headers?: Record<string, string>;
|
||||
};
|
||||
models: Record<string, OpenCodeModel>;
|
||||
}
|
||||
|
||||
interface OpenCodeModel {
|
||||
name: string;
|
||||
limit?: {
|
||||
context?: number;
|
||||
output?: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### 7.2 MCP 应用状态扩展
|
||||
**文件**: `src/types.ts`
|
||||
|
||||
```typescript
|
||||
interface McpApps {
|
||||
claude: boolean;
|
||||
codex: boolean;
|
||||
gemini: boolean;
|
||||
opencode: boolean; // 新增
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 8: 前端预设配置
|
||||
|
||||
#### 8.1 新建 OpenCode 供应商预设
|
||||
**文件**: `src/config/opencodeProviderPresets.ts`
|
||||
|
||||
```typescript
|
||||
export const opencodeProviderPresets: ProviderPreset[] = [
|
||||
{
|
||||
name: "OpenAI",
|
||||
npmPackage: "@ai-sdk/openai",
|
||||
settingsConfig: {
|
||||
npm: "@ai-sdk/openai",
|
||||
options: { apiKey: "{env:OPENAI_API_KEY}" },
|
||||
models: {
|
||||
"gpt-4o": { name: "GPT-4o" },
|
||||
"gpt-4o-mini": { name: "GPT-4o Mini" },
|
||||
},
|
||||
},
|
||||
theme: { icon: "openai", iconColor: "#00A67E" },
|
||||
},
|
||||
{
|
||||
name: "Anthropic",
|
||||
npmPackage: "@ai-sdk/anthropic",
|
||||
settingsConfig: {
|
||||
npm: "@ai-sdk/anthropic",
|
||||
options: { apiKey: "{env:ANTHROPIC_API_KEY}" },
|
||||
models: {
|
||||
"claude-sonnet-4-20250514": { name: "Claude Sonnet 4" },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "OpenAI Compatible",
|
||||
npmPackage: "@ai-sdk/openai-compatible",
|
||||
settingsConfig: {
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
options: {
|
||||
baseURL: "",
|
||||
apiKey: "{env:API_KEY}",
|
||||
},
|
||||
models: {},
|
||||
},
|
||||
isCustomTemplate: true,
|
||||
},
|
||||
// ... 更多预设
|
||||
];
|
||||
|
||||
// npm 包选项
|
||||
export const opencodeNpmPackages = [
|
||||
{ value: "@ai-sdk/openai", label: "OpenAI" },
|
||||
{ value: "@ai-sdk/anthropic", label: "Anthropic" },
|
||||
{ value: "@ai-sdk/openai-compatible", label: "OpenAI Compatible" },
|
||||
{ value: "@ai-sdk/google", label: "Google" },
|
||||
{ value: "@ai-sdk/azure", label: "Azure OpenAI" },
|
||||
{ value: "@ai-sdk/amazon-bedrock", label: "Amazon Bedrock" },
|
||||
// ... 更多选项
|
||||
];
|
||||
```
|
||||
|
||||
### Phase 9: 前端 UI 组件
|
||||
|
||||
#### 9.1 OpenCode 供应商表单
|
||||
**文件**: `src/components/providers/forms/OpenCodeFormFields.tsx`
|
||||
|
||||
新增字段:
|
||||
- npm 包选择器(下拉框 + 自定义输入)
|
||||
- options 编辑器(baseURL, apiKey, headers)
|
||||
- models 编辑器(动态添加/删除模型)
|
||||
|
||||
#### 9.2 供应商卡片按钮适配
|
||||
**文件**: `src/components/providers/ProviderActions.tsx`
|
||||
|
||||
```tsx
|
||||
// OpenCode 使用不同的主按钮
|
||||
if (appId === "opencode") {
|
||||
return (
|
||||
<Button onClick={onAdd}>
|
||||
{isInConfig ? t("provider.removeFromConfig") : t("provider.addToConfig")}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 9.3 隐藏 OpenCode 不需要的功能
|
||||
|
||||
在以下组件中检查 `appId !== "opencode"`:
|
||||
- 代理设置面板
|
||||
- 故障转移队列
|
||||
- 供应商切换逻辑
|
||||
|
||||
### Phase 10: 国际化
|
||||
|
||||
#### 10.1 新增翻译 Key
|
||||
**文件**: `src/locales/zh/translation.json` & `en/translation.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"app.opencode": "OpenCode",
|
||||
"provider.addToConfig": "添加到配置",
|
||||
"provider.removeFromConfig": "从配置移除",
|
||||
"provider.inConfig": "已添加",
|
||||
"provider.npmPackage": "AI SDK 包",
|
||||
"provider.models": "模型配置",
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 关键文件清单
|
||||
|
||||
### 后端(Rust)
|
||||
| 操作 | 文件路径 |
|
||||
|------|---------|
|
||||
| 修改 | `src-tauri/src/app_config.rs` |
|
||||
| 修改 | `src-tauri/src/database/schema.rs` |
|
||||
| 修改 | `src-tauri/src/database/dao/mcp.rs` |
|
||||
| 修改 | `src-tauri/src/database/dao/providers.rs` |
|
||||
| 修改 | `src-tauri/src/services/provider/mod.rs` |
|
||||
| 修改 | `src-tauri/src/services/mcp.rs` |
|
||||
| 修改 | `src-tauri/src/commands/providers.rs` |
|
||||
| 修改 | `src-tauri/src/commands/mcp.rs` |
|
||||
| 修改 | `src-tauri/src/mcp/mod.rs` |
|
||||
| 新建 | `src-tauri/src/opencode_config.rs` |
|
||||
| 新建 | `src-tauri/src/mcp/opencode.rs` |
|
||||
| 新建 | `src-tauri/src/services/provider/opencode.rs` |
|
||||
|
||||
### 前端(TypeScript/React)
|
||||
| 操作 | 文件路径 |
|
||||
|------|---------|
|
||||
| 修改 | `src/types.ts` |
|
||||
| 修改 | `src/lib/api/types.ts` |
|
||||
| 修改 | `src/lib/api/providers.ts` |
|
||||
| 修改 | `src/components/providers/ProviderActions.tsx` |
|
||||
| 修改 | `src/components/providers/ProviderCard.tsx` |
|
||||
| 修改 | `src/components/providers/AddProviderDialog.tsx` |
|
||||
| 修改 | `src/components/providers/forms/ProviderForm.tsx` |
|
||||
| 修改 | `src/App.tsx` |
|
||||
| 新建 | `src/config/opencodeProviderPresets.ts` |
|
||||
| 新建 | `src/components/providers/forms/OpenCodeFormFields.tsx` |
|
||||
|
||||
### 国际化
|
||||
| 操作 | 文件路径 |
|
||||
|------|---------|
|
||||
| 修改 | `src/locales/zh/translation.json` |
|
||||
| 修改 | `src/locales/en/translation.json` |
|
||||
| 修改 | `src/locales/ja/translation.json` |
|
||||
|
||||
---
|
||||
|
||||
## 验证计划
|
||||
|
||||
### 单元测试
|
||||
1. OpenCode 配置读写测试
|
||||
2. MCP 格式转换测试(stdio ↔ local, sse ↔ remote)
|
||||
3. 供应商 CRUD 操作测试
|
||||
|
||||
### 集成测试
|
||||
1. 添加 OpenCode 供应商 → 验证写入 `~/.config/opencode/opencode.json`
|
||||
2. 删除供应商 → 验证从配置文件移除
|
||||
3. MCP 同步测试 → 验证格式正确转换
|
||||
4. 从 live 配置导入 → 验证正确解析
|
||||
|
||||
### 手动测试
|
||||
1. UI 流程:添加预设 → 编辑 → 删除
|
||||
2. 切换应用 Tab → OpenCode 显示正确的 UI(无代理/故障转移)
|
||||
3. 托盘菜单正确显示 OpenCode 供应商
|
||||
4. 深链接导入 OpenCode 供应商
|
||||
|
||||
---
|
||||
|
||||
## 风险评估
|
||||
|
||||
1. **数据库迁移**:需要在升级时自动执行 `ALTER TABLE` 语句
|
||||
2. **配置文件冲突**:OpenCode 可能有自己的配置,需要合并而非覆盖
|
||||
3. **MCP 格式差异**:`stdio` → `local` 转换需要处理边界情况
|
||||
4. **UI 一致性**:OpenCode 的"添加/删除"模式需要与其他应用的"启用/切换"清晰区分
|
||||
|
||||
---
|
||||
|
||||
## 补充说明
|
||||
|
||||
### 托盘菜单特殊处理
|
||||
|
||||
由于 OpenCode 采用累加式管理,托盘菜单行为需要调整:
|
||||
|
||||
- **现有三应用**:托盘菜单显示 `CheckMenuItem`(单选,切换当前供应商)
|
||||
- **OpenCode**:显示当前所有启用的供应商(普通 MenuItem,无勾选逻辑),点击打开主界面
|
||||
|
||||
**修改文件**:`src-tauri/src/tray.rs`(`TRAY_SECTIONS` 常量)
|
||||
|
||||
### 数据库约束更新
|
||||
|
||||
`proxy_config` 表的 CHECK 约束需要扩展:
|
||||
```sql
|
||||
CHECK (app_type IN ('claude','codex','gemini','opencode'))
|
||||
```
|
||||
|
||||
### Settings 结构体扩展
|
||||
|
||||
**文件**:`src-tauri/src/settings.rs`
|
||||
|
||||
需要添加:
|
||||
- `current_provider_opencode: Option<String>` - 对 OpenCode 可能无意义,但保持结构一致
|
||||
- `opencode_config_dir: Option<String>` - 自定义配置目录
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenCode Support, Global Proxy, Claude Rectifier & Multi-App Experience Enhancements
|
||||
|
||||
**[中文版 →](release-note-v3.10.0-zh.md) | [日本語版 →](release-note-v3.10.0-ja.md)**
|
||||
**[中文版 →](v3.10.0-zh.md) | [日本語版 →](v3.10.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenCode サポート、グローバルプロキシ、Claude Rectifier とマルチアプリ体験の強化
|
||||
|
||||
**[中文版 →](release-note-v3.10.0-zh.md) | [English →](release-note-v3.10.0-en.md)**
|
||||
**[中文版 →](v3.10.0-zh.md) | [English →](v3.10.0-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenCode 支持、全局代理、Claude Rectifier 与多应用体验增强
|
||||
|
||||
**[English →](release-note-v3.10.0-en.md) | [日本語版 →](release-note-v3.10.0-ja.md)**
|
||||
**[English →](v3.10.0-en.md) | [日本語版 →](v3.10.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenClaw Support, Session Manager, Backup Management & 50+ Improvements
|
||||
|
||||
**[中文版 →](release-note-v3.11.0-zh.md) | [日本語版 →](release-note-v3.11.0-ja.md)**
|
||||
**[中文版 →](v3.11.0-zh.md) | [日本語版 →](v3.11.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenClaw サポート、セッションマネージャー、バックアップ管理と 50 以上の改善
|
||||
|
||||
**[中文版 →](release-note-v3.11.0-zh.md) | [English →](release-note-v3.11.0-en.md)**
|
||||
**[中文版 →](v3.11.0-zh.md) | [English →](v3.11.0-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> OpenClaw 支持、会话管理器、备份管理与 50+ 项改进
|
||||
|
||||
**[English →](release-note-v3.11.0-en.md) | [日本語版 →](release-note-v3.11.0-ja.md)**
|
||||
**[English →](v3.11.0-en.md) | [日本語版 →](v3.11.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Revert Partial Key-Field Merging, Restore Common Config Snippet & Bug Fixes
|
||||
|
||||
**[中文版 →](release-note-v3.11.1-zh.md) | [日本語版 →](release-note-v3.11.1-ja.md)**
|
||||
**[中文版 →](v3.11.1-zh.md) | [日本語版 →](v3.11.1-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 部分キーフィールドマージの撤回、共通設定スニペットの復元とバグ修正
|
||||
|
||||
**[中文版 →](release-note-v3.11.1-zh.md) | [English →](release-note-v3.11.1-en.md)**
|
||||
**[中文版 →](v3.11.1-zh.md) | [English →](v3.11.1-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 回退部分键值合并、恢复通用配置片段与多项修复
|
||||
|
||||
**[English →](release-note-v3.11.1-en.md) | [日本語版 →](release-note-v3.11.1-ja.md)**
|
||||
**[English →](v3.11.1-en.md) | [日本語版 →](v3.11.1-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## Major architecture refactoring with enhanced config sync and data protection
|
||||
|
||||
**[中文更新说明 Chinese Documentation →](https://github.com/farion1231/cc-switch/blob/main/docs/release-note-v3.6.0-zh.md)**
|
||||
**[中文更新说明 Chinese Documentation →](https://github.com/farion1231/cc-switch/blob/main/docs/release-notes/v3.6.0-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 全栈架构重构,增强配置同步与数据保护
|
||||
|
||||
**[English Version →](../release-note-v3.6.0.md)**
|
||||
**[English Version →](v3.6.0-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Stability improvements and user experience optimization (based on v3.6.0)
|
||||
|
||||
**[中文更新说明 Chinese Documentation →](https://github.com/farion1231/cc-switch/blob/main/docs/release-note-v3.6.1-zh.md)**
|
||||
**[中文更新说明 Chinese Documentation →](https://github.com/farion1231/cc-switch/blob/main/docs/release-notes/v3.6.1-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 稳定性提升与用户体验优化(基于 v3.6.0)
|
||||
|
||||
**[English Version →](../release-note-v3.6.1.md)**
|
||||
**[English Version →](v3.6.1-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> From Provider Switcher to All-in-One AI CLI Management Platform
|
||||
|
||||
**[中文更新说明 Chinese Documentation →](release-note-v3.7.0-zh.md)**
|
||||
**[中文更新说明 Chinese Documentation →](v3.7.0-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 从供应商切换器到 AI CLI 一体化管理平台
|
||||
|
||||
**[English Version →](release-note-v3.7.0-en.md)**
|
||||
**[English Version →](v3.7.0-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Stability Enhancements and User Experience Improvements
|
||||
|
||||
**[中文更新说明 Chinese Documentation →](release-note-v3.7.1-zh.md)**
|
||||
**[中文更新说明 Chinese Documentation →](v3.7.1-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 稳定性增强与用户体验改进
|
||||
|
||||
**[English Version →](release-note-v3.7.1-en.md)**
|
||||
**[English Version →](v3.7.1-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Persistence Architecture Upgrade, Laying the Foundation for Cloud Sync
|
||||
|
||||
**[中文版 →](release-note-v3.8.0-zh.md) | [日本語版 →](release-note-v3.8.0-ja.md)**
|
||||
**[中文版 →](v3.8.0-zh.md) | [日本語版 →](v3.8.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 永続化アーキテクチャを刷新し、クラウド同期の土台を構築
|
||||
|
||||
**[English →](release-note-v3.8.0-en.md) | [中文版 →](release-note-v3.8.0-zh.md)**
|
||||
**[English →](v3.8.0-en.md) | [中文版 →](v3.8.0-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 持久化架构升级,为云同步奠定基础
|
||||
|
||||
**[English Version →](release-note-v3.8.0-en.md)**
|
||||
**[English Version →](v3.8.0-en.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Local API Proxy, Auto Failover, Universal Provider, and a more complete multi-app workflow
|
||||
|
||||
**[中文版 →](release-note-v3.9.0-zh.md) | [日本語版 →](release-note-v3.9.0-ja.md)**
|
||||
**[中文版 →](v3.9.0-zh.md) | [日本語版 →](v3.9.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> ローカル API プロキシ、自動フェイルオーバー、Universal Provider、多アプリ対応の強化
|
||||
|
||||
**[English →](release-note-v3.9.0-en.md) | [中文版 →](release-note-v3.9.0-zh.md)**
|
||||
**[English →](v3.9.0-en.md) | [中文版 →](v3.9.0-zh.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> 本地 API 代理、自动故障切换、统一供应商与多应用工作流增强
|
||||
|
||||
**[English →](release-note-v3.9.0-en.md) | [日本語版 →](release-note-v3.9.0-ja.md)**
|
||||
**[English →](v3.9.0-en.md) | [日本語版 →](v3.9.0-ja.md)**
|
||||
|
||||
---
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
- 自动升级自定义路径 ✅
|
||||
- win 绿色版报毒问题 ✅
|
||||
- mcp 管理器 ✅
|
||||
- i18n ✅
|
||||
- gemini cli
|
||||
- homebrew 支持 ✅
|
||||
- memory 管理
|
||||
- codex 更多预设供应商
|
||||
- 云同步
|
||||
- 本地代理
|
||||
@@ -1,863 +0,0 @@
|
||||
# v3.7.0 统一 MCP 管理重构计划
|
||||
|
||||
## 📋 项目概述
|
||||
|
||||
**目标**:将原有的按应用分离的 MCP 管理(Claude/Codex/Gemini 各自独立管理)重构为统一管理面板,每个 MCP 服务器通过多选框控制应用到哪些客户端。
|
||||
|
||||
**版本**:v3.6.2 → v3.7.0
|
||||
|
||||
**开始时间**:2025-11-14
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心需求
|
||||
|
||||
### 原有架构(v3.6.x)
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ Claude面板 │ │ Codex面板 │ │ Gemini面板 │
|
||||
│ MCP管理 │ │ MCP管理 │ │ MCP管理 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
↓ ↓ ↓
|
||||
mcp.claude mcp.codex mcp.gemini
|
||||
{servers} {servers} {servers}
|
||||
```
|
||||
|
||||
### 新架构(v3.7.0)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────┐
|
||||
│ 统一 MCP 管理面板 │
|
||||
│ ┌────────┬────────┬────────┬────┐ │
|
||||
│ │ 服务器 │ Claude │ Codex │Gem │ │
|
||||
│ ├────────┼────────┼────────┼────┤ │
|
||||
│ │ mcp-1 │ ✓ │ ✓ │ │ │
|
||||
│ │ mcp-2 │ ✓ │ │ ✓ │ │
|
||||
│ └────────┴────────┴────────┴────┘ │
|
||||
└───────────────────────────────────────┘
|
||||
↓
|
||||
mcp.servers
|
||||
{
|
||||
"mcp-1": {
|
||||
apps: {claude: true, codex: true, gemini: false}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📐 技术架构
|
||||
|
||||
### 数据结构设计
|
||||
|
||||
#### 新增:McpApps(应用启用状态)
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
|
||||
pub struct McpApps {
|
||||
pub claude: bool,
|
||||
pub codex: bool,
|
||||
pub gemini: bool,
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新:McpServer(统一服务器定义)
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct McpServer {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub server: serde_json::Value, // 连接配置(stdio/http)
|
||||
pub apps: McpApps, // 新增:标记应用到哪些客户端
|
||||
pub description: Option<String>,
|
||||
pub homepage: Option<String>,
|
||||
pub docs: Option<String>,
|
||||
pub tags: Vec<String>,
|
||||
}
|
||||
```
|
||||
|
||||
#### 更新:McpRoot(新旧结构并存)
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
pub struct McpRoot {
|
||||
// v3.7.0 新结构
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub servers: Option<HashMap<String, McpServer>>,
|
||||
|
||||
// v3.6.x 旧结构(保留用于迁移)
|
||||
#[serde(default, skip_serializing_if = "McpConfig::is_empty")]
|
||||
pub claude: McpConfig,
|
||||
#[serde(default, skip_serializing_if = "McpConfig::is_empty")]
|
||||
pub codex: McpConfig,
|
||||
#[serde(default, skip_serializing_if = "McpConfig::is_empty")]
|
||||
pub gemini: McpConfig,
|
||||
}
|
||||
```
|
||||
|
||||
### 迁移策略
|
||||
|
||||
```
|
||||
旧配置 (v3.6.x) 新配置 (v3.7.0)
|
||||
───────────────── ─────────────────
|
||||
mcp: mcp:
|
||||
claude: servers:
|
||||
servers: mcp-fetch:
|
||||
mcp-fetch: {...} → id: "mcp-fetch"
|
||||
codex: server: {...}
|
||||
servers: apps:
|
||||
mcp-filesystem: {...} claude: true
|
||||
codex: true
|
||||
gemini: false
|
||||
```
|
||||
|
||||
**迁移逻辑**:
|
||||
1. 检测 `mcp.servers` 是否存在
|
||||
2. 若不存在,从 `mcp.claude/codex/gemini.servers` 收集所有服务器
|
||||
3. 合并同 id 服务器的 apps 字段
|
||||
4. 清空旧结构字段
|
||||
5. 保存配置(自动触发)
|
||||
|
||||
---
|
||||
|
||||
## ✅ 开发进度
|
||||
|
||||
### Phase 1: 后端数据结构与迁移 ✅ 已完成
|
||||
|
||||
#### 1.1 修改数据结构(app_config.rs)✅
|
||||
|
||||
**文件**:`src-tauri/src/app_config.rs`
|
||||
|
||||
**变更**:
|
||||
- ✅ 新增 `McpApps` 结构体(lines 30-62)
|
||||
- ✅ 新增 `McpServer` 结构体(lines 64-79)
|
||||
- ✅ 更新 `McpRoot` 支持新旧结构(lines 81-96)
|
||||
- ✅ 添加辅助方法:`is_enabled_for`, `set_enabled_for`, `enabled_apps`
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
|
||||
#### 1.2 实现迁移逻辑 ✅
|
||||
|
||||
**文件**:`src-tauri/src/app_config.rs`
|
||||
|
||||
**实现**:
|
||||
- ✅ `migrate_mcp_to_unified()` 方法(lines 380-509)
|
||||
- 从旧结构收集所有服务器
|
||||
- 按 id 合并重复服务器
|
||||
- 处理冲突(合并 apps 字段)
|
||||
- 清空旧结构
|
||||
- ✅ 集成到 `MultiAppConfig::load()` 方法(lines 252-257)
|
||||
- 自动检测并执行迁移
|
||||
- 迁移后保存配置
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 后端服务层重构 ✅ 已完成
|
||||
|
||||
#### 2.1 重写 McpService ✅
|
||||
|
||||
**文件**:`src-tauri/src/services/mcp.rs`
|
||||
|
||||
**新增方法**:
|
||||
- ✅ `get_all_servers()` - 获取所有服务器(lines 13-27)
|
||||
- ✅ `upsert_server()` - 添加/更新服务器(lines 30-52)
|
||||
- ✅ `delete_server()` - 删除服务器(lines 55-75)
|
||||
- ✅ `toggle_app()` - 切换应用启用状态(lines 78-111)
|
||||
- ✅ `sync_all_enabled()` - 同步所有启用的服务器(lines 180-188)
|
||||
|
||||
**兼容层方法**(已废弃):
|
||||
- ✅ `get_servers()` - 按应用过滤服务器(lines 196-210)
|
||||
- ✅ `set_enabled()` - 委托到 toggle_app(lines 213-222)
|
||||
- ✅ `sync_enabled()` - 同步特定应用(lines 225-236)
|
||||
- ✅ `import_from_claude/codex/gemini()` - 导入包装(lines 239-266)
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
|
||||
#### 2.2 新增同步函数(mcp.rs)✅
|
||||
|
||||
**文件**:`src-tauri/src/mcp.rs`
|
||||
|
||||
**新增函数**(lines 800-965):
|
||||
- ✅ `json_server_to_toml_table()` - JSON → TOML 转换助手(lines 828-889)
|
||||
- ✅ `sync_single_server_to_claude()` - 同步单个服务器到 Claude(lines 800-814)
|
||||
- ✅ `remove_server_from_claude()` - 从 Claude 移除服务器(lines 817-826)
|
||||
- ✅ `sync_single_server_to_codex()` - 同步单个服务器到 Codex(lines 891-936)
|
||||
- ✅ `remove_server_from_codex()` - 从 Codex 移除服务器(lines 939-965)
|
||||
- ✅ `sync_single_server_to_gemini()` - 同步单个服务器到 Gemini(lines 967-977)
|
||||
- ✅ `remove_server_from_gemini()` - 从 Gemini 移除服务器(lines 980-989)
|
||||
|
||||
**关键修复**:
|
||||
- ✅ 修复 toml_edit 类型转换(使用手动构建而非 serde 转换)
|
||||
- ✅ 修复 get_codex_config_path() 调用(返回 PathBuf 而非 Result)
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
**修复提交**:`7ae2a9f` - "fix(mcp): resolve compilation errors and add backward compatibility"
|
||||
|
||||
#### 2.3 新增 Tauri Commands ✅
|
||||
|
||||
**文件**:`src-tauri/src/commands/mcp.rs`
|
||||
|
||||
**新增命令**(lines 147-196):
|
||||
- ✅ `get_mcp_servers()` - 获取所有服务器(lines 154-159)
|
||||
- ✅ `upsert_mcp_server()` - 添加/更新服务器(lines 162-168)
|
||||
- ✅ `delete_mcp_server()` - 删除服务器(lines 171-177)
|
||||
- ✅ `toggle_mcp_app()` - 切换应用状态(lines 180-189)
|
||||
- ✅ `sync_all_mcp_servers()` - 同步所有服务器(lines 192-195)
|
||||
|
||||
**更新旧命令**(兼容层):
|
||||
- ✅ `upsert_mcp_server_in_config()` - 转换为统一结构(lines 68-131)
|
||||
- ✅ `delete_mcp_server_in_config()` - 忽略 app 参数(lines 134-141)
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
**修复提交**:`7ae2a9f` - "fix(mcp): resolve compilation errors and add backward compatibility"
|
||||
|
||||
#### 2.4 注册新命令(lib.rs)✅
|
||||
|
||||
**文件**:`src-tauri/src/lib.rs`
|
||||
|
||||
**变更**:
|
||||
- ✅ 导出 `McpServer` 类型(line 21)
|
||||
- ✅ 导出新增的 mcp 同步函数(lines 26-31)
|
||||
- ✅ 注册 5 个新命令到 invoke_handler(lines 550-555)
|
||||
|
||||
**提交**:`c7b235b` - "feat(mcp): implement unified MCP management for v3.7.0"
|
||||
|
||||
#### 2.5 添加缺失的函数(claude_mcp.rs & gemini_mcp.rs)✅
|
||||
|
||||
**文件**:
|
||||
- `src-tauri/src/claude_mcp.rs` (lines 234-253)
|
||||
- `src-tauri/src/gemini_mcp.rs` (lines 160-179)
|
||||
|
||||
**新增**:
|
||||
- ✅ `read_mcp_servers_map()` - 读取现有 MCP 服务器映射
|
||||
|
||||
**提交**:`7ae2a9f` - "fix(mcp): resolve compilation errors and add backward compatibility"
|
||||
|
||||
#### 2.6 编译验证 ✅
|
||||
|
||||
**状态**:✅ 编译成功
|
||||
- ⚠️ 16 个警告(8 个废弃警告 + 8 个未使用函数警告 - 预期内)
|
||||
- ✅ 0 个错误
|
||||
|
||||
**提交**:`7ae2a9f` - "fix(mcp): resolve compilation errors and add backward compatibility"
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: 前端开发 ⚠️ 部分完成
|
||||
|
||||
#### 3.1 TypeScript 类型定义 ✅
|
||||
|
||||
**文件**:`src/types.ts`
|
||||
|
||||
**变更**:
|
||||
- ✅ 新增 `McpApps` 接口(lines 129-133)
|
||||
- ✅ 更新 `McpServer` 接口(lines 136-149)
|
||||
- 新增 `apps: McpApps` 字段
|
||||
- `name` 改为必填
|
||||
- 标记 `enabled` 为废弃
|
||||
- ✅ 新增 `McpServersMap` 类型别名(line 152)
|
||||
- ✅ 保持向后兼容(保留 `enabled`, `source` 等旧字段)
|
||||
|
||||
**提交**:`ac09551` - "feat(frontend): add unified MCP types and API layer for v3.7.0"
|
||||
|
||||
#### 3.2 API 层更新 ✅
|
||||
|
||||
**文件**:`src/lib/api/mcp.ts`
|
||||
|
||||
**新增方法**(lines 99-141):
|
||||
- ✅ `getAllServers()` - 获取所有服务器(lines 106-108)
|
||||
- ✅ `upsertUnifiedServer()` - 添加/更新服务器(lines 113-115)
|
||||
- ✅ `deleteUnifiedServer()` - 删除服务器(lines 120-122)
|
||||
- ✅ `toggleApp()` - 切换应用状态(lines 127-133)
|
||||
- ✅ `syncAllServers()` - 同步所有服务器(lines 138-140)
|
||||
|
||||
**导入更新**:
|
||||
- ✅ 导入 `McpServersMap` 类型(line 6)
|
||||
|
||||
**提交**:`ac09551` - "feat(frontend): add unified MCP types and API layer for v3.7.0"
|
||||
|
||||
#### 3.3 React Query Hooks 📝 待开发
|
||||
|
||||
**计划文件**:`src/hooks/useMcp.ts`
|
||||
|
||||
**需要实现的 Hooks**:
|
||||
|
||||
```typescript
|
||||
// 查询 hooks
|
||||
export function useAllMcpServers() {
|
||||
return useQuery({
|
||||
queryKey: ['mcp', 'all'],
|
||||
queryFn: () => mcpApi.getAllServers(),
|
||||
});
|
||||
}
|
||||
|
||||
// 变更 hooks
|
||||
export function useUpsertMcpServer() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (server: McpServer) => mcpApi.upsertUnifiedServer(server),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useToggleMcpApp() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ serverId, app, enabled }: {
|
||||
serverId: string;
|
||||
app: AppId;
|
||||
enabled: boolean;
|
||||
}) => mcpApi.toggleApp(serverId, app, enabled),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteMcpServer() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => mcpApi.deleteUnifiedServer(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['mcp', 'all'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSyncAllMcpServers() {
|
||||
return useMutation({
|
||||
mutationFn: () => mcpApi.syncAllServers(),
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**依赖**:
|
||||
- `@tanstack/react-query` (已安装)
|
||||
- `src/lib/api/mcp.ts` (✅ 已完成)
|
||||
- `src/types.ts` (✅ 已完成)
|
||||
|
||||
#### 3.4 统一 MCP 面板组件 📝 待开发
|
||||
|
||||
**计划文件**:`src/components/mcp/UnifiedMcpPanel.tsx`
|
||||
|
||||
**组件结构**:
|
||||
|
||||
```typescript
|
||||
interface UnifiedMcpPanelProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function UnifiedMcpPanel({ className }: UnifiedMcpPanelProps) {
|
||||
const { t } = useTranslation();
|
||||
const { data: servers, isLoading } = useAllMcpServers();
|
||||
const toggleApp = useToggleMcpApp();
|
||||
const deleteServer = useDeleteMcpServer();
|
||||
const syncAll = useSyncAllMcpServers();
|
||||
|
||||
// 组件实现...
|
||||
}
|
||||
```
|
||||
|
||||
**UI 设计**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ MCP 服务器管理 ┌──────────┐ │
|
||||
│ │ 添加服务器 │ │
|
||||
│ ┌─────┐ ┌──────────────┐ ┌─────────┐ └──────────┘ │
|
||||
│ │ 搜索 │ │ 导入自...▼ │ │ 同步全部 │ │
|
||||
│ └─────┘ └──────────────┘ └─────────┘ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────┐ │
|
||||
│ │ 名称 │ Claude │ Codex │ Gemini │操作│ │
|
||||
│ ├─────────────────────────────────────────────┤ │
|
||||
│ │ mcp-fetch │ ✓ │ ✓ │ │ ⚙️ │ │
|
||||
│ │ filesystem │ ✓ │ │ ✓ │ ⚙️ │ │
|
||||
│ │ brave-search │ │ ✓ │ ✓ │ ⚙️ │ │
|
||||
│ └─────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**功能特性**:
|
||||
- 📋 服务器列表展示(名称、描述、标签)
|
||||
- ☑️ 三个复选框控制应用启用状态(Claude/Codex/Gemini)
|
||||
- ➕ 添加新服务器(表单模态框)
|
||||
- ✏️ 编辑服务器(表单模态框)
|
||||
- 🗑️ 删除服务器(确认对话框)
|
||||
- 📥 导入功能(从 Claude/Codex/Gemini 导入)
|
||||
- 🔄 同步全部(手动触发同步到 live 配置)
|
||||
- 🔍 搜索过滤
|
||||
- 🏷️ 标签过滤
|
||||
|
||||
**子组件**:
|
||||
|
||||
1. **McpServerTable** (`McpServerTable.tsx`)
|
||||
- 服务器列表表格
|
||||
- 应用复选框
|
||||
- 操作按钮(编辑、删除)
|
||||
|
||||
2. **McpServerFormModal** (`McpServerFormModal.tsx`)
|
||||
- 添加/编辑表单
|
||||
- stdio/http 类型切换
|
||||
- 应用选择(多选)
|
||||
- 元信息编辑(描述、标签、链接)
|
||||
|
||||
3. **McpImportDialog** (`McpImportDialog.tsx`)
|
||||
- 选择导入来源(Claude/Codex/Gemini)
|
||||
- 服务器预览
|
||||
- 批量导入
|
||||
|
||||
**依赖组件**(来自 shadcn/ui):
|
||||
- `Table`, `TableBody`, `TableCell`, `TableHead`, `TableHeader`, `TableRow`
|
||||
- `Checkbox`
|
||||
- `Button`
|
||||
- `Dialog`, `DialogContent`, `DialogHeader`, `DialogTitle`
|
||||
- `Input`, `Textarea`, `Label`
|
||||
- `Select`, `SelectContent`, `SelectItem`, `SelectTrigger`, `SelectValue`
|
||||
- `Badge`
|
||||
- `Tooltip`
|
||||
|
||||
#### 3.5 主界面集成 📝 待开发
|
||||
|
||||
**文件**:`src/App.tsx`
|
||||
|
||||
**变更计划**:
|
||||
|
||||
```typescript
|
||||
// 原有代码(v3.6.x)
|
||||
{currentApp === 'claude' && <ClaudeMcpPanel />}
|
||||
{currentApp === 'codex' && <CodexMcpPanel />}
|
||||
{currentApp === 'gemini' && <GeminiMcpPanel />}
|
||||
|
||||
// 新代码(v3.7.0)
|
||||
<UnifiedMcpPanel />
|
||||
```
|
||||
|
||||
**移除的组件**:
|
||||
- `ClaudeMcpPanel.tsx`
|
||||
- `CodexMcpPanel.tsx`
|
||||
- `GeminiMcpPanel.tsx`
|
||||
|
||||
**注意**:保留旧组件文件备份,以便回滚
|
||||
|
||||
#### 3.6 国际化文本更新 📝 待开发
|
||||
|
||||
**文件**:
|
||||
- `src/locales/zh/translation.json`
|
||||
- `src/locales/en/translation.json`
|
||||
|
||||
**需要添加的翻译键**:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"unifiedPanel": {
|
||||
"title": "MCP 服务器管理 / MCP Server Management",
|
||||
"addServer": "添加服务器 / Add Server",
|
||||
"editServer": "编辑服务器 / Edit Server",
|
||||
"deleteServer": "删除服务器 / Delete Server",
|
||||
"deleteConfirm": "确定要删除此服务器吗?/ Are you sure to delete this server?",
|
||||
"syncAll": "同步全部 / Sync All",
|
||||
"syncAllSuccess": "已同步所有启用的服务器 / All enabled servers synced",
|
||||
"importFrom": "导入自... / Import from...",
|
||||
"search": "搜索服务器... / Search servers...",
|
||||
"noServers": "暂无服务器 / No servers yet",
|
||||
"enabledApps": "启用的应用 / Enabled Apps",
|
||||
"apps": {
|
||||
"claude": "Claude",
|
||||
"codex": "Codex",
|
||||
"gemini": "Gemini"
|
||||
},
|
||||
"form": {
|
||||
"id": "服务器 ID / Server ID",
|
||||
"name": "显示名称 / Display Name",
|
||||
"type": "类型 / Type",
|
||||
"stdio": "本地进程 / Local Process",
|
||||
"http": "远程服务 / Remote Service",
|
||||
"command": "命令 / Command",
|
||||
"args": "参数 / Arguments",
|
||||
"env": "环境变量 / Environment Variables",
|
||||
"cwd": "工作目录 / Working Directory",
|
||||
"url": "URL",
|
||||
"headers": "请求头 / Headers",
|
||||
"description": "描述 / Description",
|
||||
"tags": "标签 / Tags",
|
||||
"homepage": "主页 / Homepage",
|
||||
"docs": "文档 / Documentation",
|
||||
"selectApps": "选择应用 / Select Apps",
|
||||
"selectAppsHint": "勾选此服务器要应用到哪些客户端 / Check which clients this server applies to"
|
||||
},
|
||||
"table": {
|
||||
"name": "名称 / Name",
|
||||
"type": "类型 / Type",
|
||||
"apps": "应用 / Apps",
|
||||
"actions": "操作 / Actions",
|
||||
"edit": "编辑 / Edit",
|
||||
"delete": "删除 / Delete"
|
||||
},
|
||||
"import": {
|
||||
"title": "导入 MCP 服务器 / Import MCP Servers",
|
||||
"fromClaude": "从 Claude 导入 / Import from Claude",
|
||||
"fromCodex": "从 Codex 导入 / Import from Codex",
|
||||
"fromGemini": "从 Gemini 导入 / Import from Gemini",
|
||||
"success": "成功导入 {{count}} 个服务器 / Successfully imported {{count}} server(s)",
|
||||
"noServersFound": "未找到可导入的服务器 / No servers found to import"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 迁移流程
|
||||
|
||||
### 用户体验
|
||||
|
||||
```
|
||||
1. 用户升级到 v3.7.0
|
||||
↓
|
||||
2. 首次启动应用
|
||||
↓
|
||||
3. 后端自动执行迁移
|
||||
- 检测旧结构 (mcp.claude/codex/gemini.servers)
|
||||
- 合并到统一结构 (mcp.servers)
|
||||
- 保存迁移后的配置
|
||||
- 日志记录迁移详情
|
||||
↓
|
||||
4. 前端加载新面板
|
||||
- 显示所有服务器
|
||||
- 三个复选框显示各应用启用状态
|
||||
↓
|
||||
5. 用户无缝使用
|
||||
```
|
||||
|
||||
### 数据完整性保证
|
||||
|
||||
1. **迁移前验证**:
|
||||
- ✅ 校验旧结构合法性
|
||||
- ✅ 记录迁移前状态
|
||||
|
||||
2. **迁移中处理**:
|
||||
- ✅ 合并同 id 服务器的 apps 字段
|
||||
- ✅ 处理 id 冲突(保留第一个,记录警告)
|
||||
- ✅ 保留所有元信息(描述、标签、链接)
|
||||
|
||||
3. **迁移后清理**:
|
||||
- ✅ 清空旧结构(claude/codex/gemini)
|
||||
- ✅ 自动保存新配置
|
||||
- ✅ 日志记录迁移完成
|
||||
|
||||
4. **回滚机制**:
|
||||
- 配置文件有备份(`config.v1.backup.<timestamp>.json`)
|
||||
- 迁移失败时可手动回滚
|
||||
|
||||
---
|
||||
|
||||
## 🧪 测试计划
|
||||
|
||||
### 后端测试 ✅ 已验证
|
||||
|
||||
- [x] 编译测试(cargo check)
|
||||
- [x] 数据结构序列化/反序列化
|
||||
- [ ] 迁移逻辑单元测试
|
||||
- [ ] 服务层方法测试
|
||||
- [ ] 同步函数测试
|
||||
|
||||
### 前端测试 ⏳ 待进行
|
||||
|
||||
- [ ] TypeScript 类型检查
|
||||
- [ ] API 调用测试
|
||||
- [ ] 组件渲染测试
|
||||
- [ ] 用户交互测试
|
||||
- [ ] 国际化文本检查
|
||||
|
||||
### 集成测试 ⏳ 待进行
|
||||
|
||||
- [ ] 完整迁移流程测试
|
||||
- [ ] 从空配置启动
|
||||
- [ ] 从 v3.6.x 配置升级
|
||||
- [ ] 多服务器合并场景
|
||||
- [ ] 冲突处理验证
|
||||
- [ ] 多应用同步测试
|
||||
- [ ] 启用单个应用
|
||||
- [ ] 启用多个应用
|
||||
- [ ] 动态切换应用
|
||||
- [ ] 同步到 live 配置验证
|
||||
- [ ] 边界情况测试
|
||||
- [ ] 空服务器列表
|
||||
- [ ] 超长服务器名称
|
||||
- [ ] 特殊字符处理
|
||||
- [ ] 并发操作
|
||||
|
||||
---
|
||||
|
||||
## 📦 交付清单
|
||||
|
||||
### 代码文件
|
||||
|
||||
#### 后端(Rust)✅ 已完成
|
||||
|
||||
- [x] `src-tauri/src/app_config.rs` - 数据结构定义与迁移
|
||||
- [x] `src-tauri/src/services/mcp.rs` - 服务层重构
|
||||
- [x] `src-tauri/src/mcp.rs` - 同步函数实现
|
||||
- [x] `src-tauri/src/commands/mcp.rs` - Tauri 命令
|
||||
- [x] `src-tauri/src/lib.rs` - 命令注册
|
||||
- [x] `src-tauri/src/claude_mcp.rs` - Claude MCP 操作
|
||||
- [x] `src-tauri/src/gemini_mcp.rs` - Gemini MCP 操作
|
||||
|
||||
#### 前端(TypeScript/React)⚠️ 部分完成
|
||||
|
||||
- [x] `src/types.ts` - 类型定义更新
|
||||
- [x] `src/lib/api/mcp.ts` - API 层更新
|
||||
- [ ] `src/hooks/useMcp.ts` - React Query Hooks
|
||||
- [ ] `src/components/mcp/UnifiedMcpPanel.tsx` - 统一面板组件
|
||||
- [ ] `src/components/mcp/McpServerTable.tsx` - 服务器表格
|
||||
- [ ] `src/components/mcp/McpServerFormModal.tsx` - 表单模态框
|
||||
- [ ] `src/components/mcp/McpImportDialog.tsx` - 导入对话框
|
||||
- [ ] `src/App.tsx` - 主界面集成
|
||||
- [ ] `src/locales/zh/translation.json` - 中文翻译
|
||||
- [ ] `src/locales/en/translation.json` - 英文翻译
|
||||
|
||||
### 文档
|
||||
|
||||
- [x] 本重构计划文档 (`docs/v3.7.0-unified-mcp-refactor.md`)
|
||||
- [ ] 用户升级指南 (`docs/upgrade-to-v3.7.0.md`)
|
||||
- [ ] API 变更说明 (`docs/api-changes-v3.7.0.md`)
|
||||
|
||||
### Git 提交记录 ✅
|
||||
|
||||
- [x] `c7b235b` - feat(mcp): implement unified MCP management for v3.7.0
|
||||
- [x] `7ae2a9f` - fix(mcp): resolve compilation errors and add backward compatibility
|
||||
- [x] `ac09551` - feat(frontend): add unified MCP types and API layer for v3.7.0
|
||||
|
||||
---
|
||||
|
||||
## 🎯 下一步行动
|
||||
|
||||
### 立即任务(优先级 P0)
|
||||
|
||||
1. ⬜ **实现 useMcp Hook**
|
||||
- 文件:`src/hooks/useMcp.ts`
|
||||
- 估时:1-2 小时
|
||||
- 依赖:API 层(已完成)
|
||||
|
||||
2. ⬜ **创建 UnifiedMcpPanel 核心组件**
|
||||
- 文件:`src/components/mcp/UnifiedMcpPanel.tsx`
|
||||
- 估时:3-4 小时
|
||||
- 依赖:useMcp Hook
|
||||
|
||||
3. ⬜ **添加国际化文本**
|
||||
- 文件:`src/locales/{zh,en}/translation.json`
|
||||
- 估时:30 分钟
|
||||
|
||||
4. ⬜ **集成到主界面**
|
||||
- 文件:`src/App.tsx`
|
||||
- 估时:30 分钟
|
||||
- 依赖:UnifiedMcpPanel 组件
|
||||
|
||||
### 次要任务(优先级 P1)
|
||||
|
||||
5. ⬜ **实现子组件**
|
||||
- McpServerTable
|
||||
- McpServerFormModal
|
||||
- McpImportDialog
|
||||
- 估时:4-6 小时
|
||||
|
||||
6. ⬜ **编写测试用例**
|
||||
- 后端单元测试
|
||||
- 前端组件测试
|
||||
- 集成测试
|
||||
- 估时:6-8 小时
|
||||
|
||||
7. ⬜ **编写用户文档**
|
||||
- 升级指南
|
||||
- API 变更说明
|
||||
- 估时:2-3 小时
|
||||
|
||||
### 优化任务(优先级 P2)
|
||||
|
||||
8. ⬜ **性能优化**
|
||||
- 服务器列表虚拟滚动
|
||||
- 批量操作优化
|
||||
- 估时:2-3 小时
|
||||
|
||||
9. ⬜ **用户体验增强**
|
||||
- 添加加载状态
|
||||
- 添加错误提示
|
||||
- 添加操作确认
|
||||
- 估时:2-3 小时
|
||||
|
||||
10. ⬜ **代码清理**
|
||||
- 移除旧的分应用面板组件
|
||||
- 清理废弃代码
|
||||
- 代码格式化
|
||||
- 估时:1-2 小时
|
||||
|
||||
---
|
||||
|
||||
## 💡 技术亮点
|
||||
|
||||
### 1. 平滑迁移机制
|
||||
|
||||
- ✅ 自动检测旧配置并迁移
|
||||
- ✅ 新旧结构并存(过渡期)
|
||||
- ✅ 无需用户手动操作
|
||||
- ✅ 保留所有历史数据
|
||||
|
||||
### 2. 向后兼容
|
||||
|
||||
- ✅ 旧命令继续可用(带废弃警告)
|
||||
- ✅ 前端可增量更新
|
||||
- ✅ 渐进式重构策略
|
||||
|
||||
### 3. 类型安全
|
||||
|
||||
- ✅ Rust 强类型保证数据完整性
|
||||
- ✅ TypeScript 类型定义与后端一致
|
||||
- ✅ serde 序列化/反序列化自动处理
|
||||
|
||||
### 4. 清晰的架构分层
|
||||
|
||||
```
|
||||
Frontend (React)
|
||||
↓ (Tauri IPC)
|
||||
Commands Layer
|
||||
↓
|
||||
Services Layer
|
||||
↓
|
||||
Data Layer (Config + Live Sync)
|
||||
```
|
||||
|
||||
### 5. SSOT 原则
|
||||
|
||||
- 单一配置源:`~/.cc-switch/config.json`
|
||||
- 统一管理:`mcp.servers` 字段
|
||||
- 按需同步:写入各应用 live 配置
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
|
||||
### 内部文档
|
||||
|
||||
- [项目 README](../README.md)
|
||||
- [CLAUDE.md](../CLAUDE.md) - Claude Code 工作指南
|
||||
- [架构文档](../CLAUDE.md#架构概述)
|
||||
|
||||
### 相关 Issues/PRs
|
||||
|
||||
- 无(新功能开发)
|
||||
|
||||
### 技术栈文档
|
||||
|
||||
- [Tauri 2.0](https://tauri.app/v1/guides/)
|
||||
- [React 18](https://react.dev/)
|
||||
- [TanStack Query](https://tanstack.com/query/latest)
|
||||
- [shadcn/ui](https://ui.shadcn.com/)
|
||||
- [serde](https://serde.rs/)
|
||||
|
||||
---
|
||||
|
||||
## 📝 变更日志
|
||||
|
||||
### 2025-11-14
|
||||
|
||||
- ✅ 完成后端 Phase 1 & 2(数据结构、服务层、命令层)
|
||||
- ✅ 修复所有编译错误
|
||||
- ✅ 完成前端类型定义和 API 层
|
||||
- ✅ 创建本重构计划文档
|
||||
|
||||
### 待更新...
|
||||
|
||||
---
|
||||
|
||||
## 👥 团队协作
|
||||
|
||||
**开发者**:Claude Code (AI Assistant) + User
|
||||
|
||||
**审查者**:User
|
||||
|
||||
**测试者**:User
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 风险与对策
|
||||
|
||||
### 风险 1:迁移数据丢失
|
||||
|
||||
**概率**:低
|
||||
**影响**:高
|
||||
**对策**:
|
||||
- ✅ 迁移前自动备份配置
|
||||
- ✅ 详细日志记录
|
||||
- ✅ 测试各种边界情况
|
||||
|
||||
### 风险 2:性能问题(大量服务器)
|
||||
|
||||
**概率**:中
|
||||
**影响**:中
|
||||
**对策**:
|
||||
- ⬜ 实现虚拟滚动
|
||||
- ⬜ 分页或懒加载
|
||||
- ⬜ 性能测试
|
||||
|
||||
### 风险 3:兼容性问题
|
||||
|
||||
**概率**:中
|
||||
**影响**:中
|
||||
**对策**:
|
||||
- ✅ 保留旧命令兼容层
|
||||
- ✅ 前端增量更新
|
||||
- ⬜ 多版本测试
|
||||
|
||||
### 风险 4:用户学习成本
|
||||
|
||||
**概率**:低
|
||||
**影响**:低
|
||||
**对策**:
|
||||
- ⬜ 清晰的 UI 设计
|
||||
- ⬜ 详细的升级指南
|
||||
- ⬜ 操作提示和引导
|
||||
|
||||
---
|
||||
|
||||
## 🎉 预期收益
|
||||
|
||||
### 用户体验提升
|
||||
|
||||
- ⭐ **简化操作**:不再需要在不同应用面板切换
|
||||
- ⭐ **统一视图**:一目了然看到所有 MCP 配置
|
||||
- ⭐ **灵活配置**:轻松控制每个 MCP 应用到哪些客户端
|
||||
|
||||
### 代码质量提升
|
||||
|
||||
- ⭐ **架构优化**:统一数据源,消除冗余
|
||||
- ⭐ **维护性**:单一面板组件,代码更简洁
|
||||
- ⭐ **扩展性**:未来添加新应用(如 Cursor)更容易
|
||||
|
||||
### 性能提升
|
||||
|
||||
- ⭐ **减少重复加载**:统一管理减少配置文件读写
|
||||
- ⭐ **更快同步**:批量操作更高效
|
||||
|
||||
---
|
||||
|
||||
## 📞 联系方式
|
||||
|
||||
**问题反馈**:[GitHub Issues](https://github.com/jasonyoungyang/cc-switch/issues)
|
||||
|
||||
**功能建议**:[GitHub Discussions](https://github.com/jasonyoungyang/cc-switch/discussions)
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.0
|
||||
**最后更新**:2025-11-14
|
||||
**状态**:🟡 开发中(后端完成 ✅,前端进行中 ⚠️)
|
||||
@@ -7,7 +7,6 @@
|
||||
//! - Prompts
|
||||
//! - Skills
|
||||
//!
|
||||
//! See docs/ccswitch-deeplink-design.md for detailed design.
|
||||
|
||||
mod mcp;
|
||||
mod parser;
|
||||
|
||||
Reference in New Issue
Block a user