设计文档-CC-Switch-CLI-TUI设计文档
CC-Switch CLI TUI 前端设计文档
这个是Claude阅读完https://github.com/SaladDay/cc-switch-cli代码后,提取的tui设计文档。这个tui设计比较简洁好用,后续相关设计可以参考。
基于
src-tauri/src/cli/tui/目录源码 v5.8.2。
目录
1. 概述
CC-Switch TUI 是一个基于 Ratatui + Crossterm 的终端文本用户界面,用于管理多 AI 客户端(Claude Code、Codex、Gemini、OpenCode、Hermes、OpenClaw)的 Provider 配置、MCP 服务器、Prompt、Skills、代理设置、用量查询等。
1.1 技术栈
| 层次 | 技术 |
|---|---|
| 终端渲染 | Ratatui (0.x) |
| 事件输入 | Crossterm |
| 动画效果 | TachyonFX |
| 数据结构 | 纯 Rust struct/enum |
| 并发模型 | mpsc channel + Worker 系统 |
| 国际化 | 内置 i18n(中/英) |
1.2 文件结构
1 | tui/ |
2. 架构概览
2.1 整体数据流
1 | 用户输入 (Key/Mouse) |
2.2 主循环 (run())
1 | // mod.rs 中的主事件循环 (约 200ms tick) |
2.3 TCA 架构
TUI 遵循 The Composable Architecture 模式:
- State:
App(视图状态 + 导航) +UiData(加载数据) +Overlay(模态叠加层) - Action:
Action枚举 (80+ 变体) - View:
render(Frame, App, UiData)— 纯函数,按路由分发到页面渲染器 - Data Flow: 事件 → Action → handle_action → Worker channel 或直接状态变更 → 重渲染
2.4 视图布局
1 | ┌──────────────────────────────────────────────────────┐ ← 3 行 |
- 三行垂直布局: Header (3) → Body (min) → Footer (1)
- Body 水平分割: Nav (固定宽度) → Content (剩余空间)
- Overlay/Toast 覆盖在最上层
3. 数据层设计
3.1 UiData 快照
UiData 是渲染层唯一的只读数据源,所有页面渲染均从它读取:1
2
3
4
5
6
7
8
9
10
11
12pub struct UiData {
pub providers: ProvidersSnapshot, // 供应商列表
pub mcp: McpSnapshot, // MCP 服务器列表
pub prompts: PromptsSnapshot, // Prompt 列表
pub config: ConfigSnapshot, // 配置信息 (路径/备份/WebDAV/OpenClaw/Hermes)
pub skills: SkillsSnapshot, // Skills 安装列表/仓库/同步方式
pub proxy: ProxySnapshot, // 本地代理状态
pub usage: UsageSnapshot, // 用量统计 (today/7d/30d/custom)
pub pricing: ModelPricingSnapshot, // 模型定价表
pub quota: QuotaSnapshot, // 配额状态
pub reload_token: UiDataReloadToken, // 重新加载标记
}
3.2 各模块数据行
ProviderRow
1 | pub struct ProviderRow { |
McpRow
1 | pub struct McpRow { |
PromptRow
1 | pub struct PromptRow { |
Usage 数据
1 | pub struct UsageSummarySnapshot { |
ProxySnapshot
1 | pub struct ProxySnapshot { |
SkillsSnapshot
1 | pub struct SkillsSnapshot { |
3.3 数据加载模式
| 模式 | 用途 |
|---|---|
ProviderLoadMode::SyncLive | 同步客户端配置到数据库 (OpenClaw 等) |
ProviderLoadMode::SnapshotOnly | 仅从数据库快照加载 (快速切换) |
UiDataByAppCache: 按 App 类型缓存数据,实现应用间瞬时切换- 切换应用时显示 “loading projection” 占位状态
4. 页面设计
4.1 路由系统
Route 枚举 (route.rs)
1 | pub enum Route { |
NavItem 枚举 (route.rs)
导航菜单有 3 种配置,按 AppType 自适应:
| 菜单项 | 通用 | OpenClaw | Hermes |
|---|---|---|---|
| Main (Home) | ✓ | ✓ | ✓ |
| Providers | ✓ | ✓ | ✓ |
| Usage | ✓ | ✓ | ✓ |
| Sessions | ✓ | ✓ | ✓ |
| MCP | ✓ | ✓ | |
| Prompts | ✓ | ||
| HermesMemory | ✓ | ||
| Config | ✓ | ✓ | ✓ |
| Skills | ✓ | ✓ | ✓ |
| OpenClawWorkspace | ✓ | ||
| OpenClawEnv | ✓ | ||
| OpenClawTools | ✓ | ||
| OpenClawAgents | ✓ | ||
| Settings | ✓ | ✓ | ✓ |
| Exit | ✓ | ✓ | ✓ |
4.2 页面详解
4.2.1 首页 (Main / Dashboard)
渲染: ui/main_page.rs
内容: - 本地环境检查 (本地工具状态) — LocalEnvCheck - 代理状态卡片 (Running/Stopped/Taking Over) - 代理活动波形图 (TachyonFX 动画) - 快速操作入口
交互: - P — 启动/停止代理 - r — 刷新本地环境检查
4.2.2 Provider 管理 (Providers)
渲染: ui/providers.rs
列表展示: | 列 | 内容 | |—-|——| | 名称 | Provider 名称 | | Base URL | API 地址 | | 状态 | Current / Saved / In Config | | 模型 | 当前默认模型 |
操作: | 按键 | 操作 | |——|——| | Enter | 查看 Provider 详情 | | s | 切换为当前 Provider | | w | 写入配置到客户端 | | D | 删除 Provider | | d | 设置默认模型 | | t | 速度测试 | | S | 流检查 (Stream Check) | | Q | 配额刷新 | | M | 切换 Failover 队列 | | F | 模型抓取 | | e | 编辑 Provider | | c | 复制 Provider | | A | 添加新 Provider (表单) | | R | 从客户端配置导入 |
ProviderDetail 页面 (Route::ProviderDetail { id }): - 显示 Provider 完整配置 JSON 预览 - Claude 模型分层配置 - API 格式选择 - 用量查询配置 - Codex 本地路由设置 - OpenClaw 模型列表管理
4.2.3 用量统计 (Usage)
渲染: ui/usage.rs
状态管理: UsageState1
2
3
4
5
6
7
8pub struct UsageState {
pub range: UsageRangePreset, // Today / 7d / 30d / Custom
pub metric: UsageMetric, // Cost / Tokens / Requests / Errors
pub pane: UsagePane, // Models / Providers / Recent
pub selected_idx: usize,
pub logs_idx: usize,
// ...
}
三栏布局:1
2
3
4
5┌──────────────┬──────────────┬──────────────────┐
│ Trend Chart │ Provider/ │ Recent Logs │
│ (TachyonFX) │ Model Table │ (scrollable) │
│ │ │ │
└──────────────┴──────────────┴──────────────────┘
Tab 切换: - Tab / BackTab — 在 Models / Providers / Recent 三 Pane 间切换 - 时间选择: 1 Today / 2 7d / 3 30d / C Custom - 指标选择: c Cost / t Tokens / r Requests / e Errors
4.2.4 会话浏览器 (Sessions)
渲染: ui/sessions.rs
双 Pane 布局:1
2
3
4
5
6
7
8┌─────────────────────┬─────────────��──────────────┐
│ Session List │ Session Detail │
│ ── │ Prompt: ... │
│ [x] session1 │ Response: ... (streaming) │
│ [x] session2 │ │
│ session3 │ [Messages with filter] │
│ │ │
└─────────────────────┴────────────────────────────┘
状态管理: SessionsState - pane: SessionsPane::List | Detail - message_filter: TextInput — 消息内容搜索 - selected: HashSet<u64> — 批量删除
操作: - ← / → — 在 List/Detail 间切换焦点 - / — 搜索会话 - m — 加载消息 - R — 刷新会话列表 - Enter — 打开详情 - d — 删除选中会话 - e — 恢复会话 (Resume)
4.2.5 MCP 管理 (MCP)
渲染: ui/mcp.rs
列表展示: | 列 | 内容 | |—-|——| | 名称 | MCP 服务器名称 | | 类型 | stdio / http / sse | | 传输 | Command/URL |
操作: - A — 添加 MCP 服务器 (表单) - e — 编辑 - T — 切换启用/禁用 - w — 设置可用客户端 - D — 删除 - I — 从已安装客户端导入
4.2.6 Prompt 管理 (Prompts)
渲染: ui/prompts.rs
列表展示: | 列 | 内容 | |—-|——| | 名称 | Prompt 名称 | | 描述 | Prompt 描述 | | 状态 | Active / Inactive |
操作: - Enter — 编辑 Prompt - a — 激活 / d — 停用 - e — 编辑元数据 - D — 删除 - I — 导入
首次打开时: 自动检测并提示导入现有 Prompt 文件
4.2.7 Skills 管理 (Skills)
渲染: ui/skills/
子页面: - Skills — 已安装 Skill 列表 - SkillsDiscover — 搜索发现 Skill - SkillsRepos — Skill 仓库管理 - SkillDetail { directory } — Skill 详情
操作: - Enter — 查看详情 / 启用 - i — 安装 - U — 卸载 - T — 切换启用 - w — 设置可用客户端 - F — 发现 (搜索) - S — 同步到客户端 - + — 添加仓库 - - — 移除仓库 - I — 批量导入 (从应用) - s — 设置同步方式
4.2.8 配置管理 (Config)
渲染: ui/config.rs
菜单项 (ConfigItem): | 项 | 说明 | |—-|——| | Path | 配置文件路径 | | Show Full | 显示完整配置 JSON | | Export | 导出配�� | | Import | 导入配置 | | Backup | 创建备份 | | Restore | 恢复备份 | | Validate | 验证配置 | | Common Snippet | 通用配置片段 | | OpenClaw Workspace | 工作区管理 | | OpenClaw Env | 环境变量 | | OpenClaw Tools | 工具配置 | | OpenClaw Agents | 代理配置 | | WebDAV Sync | WebDAV 同步设置 | | Reset | 重置配置 |
4.2.9 设置 (Settings)
渲染: ui/ — settings 对应模块
菜单项 (SettingsItem): | 项 | 说明 | |—-|——| | Managed Accounts | 托管账号管理 | | Language | 语言切换 (中/英) | | Visible Apps Mode | 可见应用模式 | | Visible Apps | 手动选择可见应用 | | OpenClaw Config Dir | OpenClaw 配置目录 | | Skip Claude Onboarding | 跳过 Claude 入门 | | Claude Plugin Integration | Claude 插件集成 | | Proxy | 本地代理设置 | | Check For Updates | 检查更新 |
4.2.10 代理设置 (SettingsProxy)
设置项 (LocalProxySettingsItem): | 项 | 说明 | |—-|——| | ListenAddress | 监听地址 (默认 127.0.0.1) | | ListenPort | 监听端口 (默认 5251) | | AutoFailover | 自动故障转移开关 |
4.2.11 模型定价 (Pricing)
渲染: ui/pricing.rs
列表展示: | 列 | 内容 | |—-|——| | 模型 | Model ID | | 输入价格 | Input cost per million | | 输出价格 | Output cost per million | | 缓存读取 | Cache read cost | | 缓存创建 | Cache creation cost | | 请求数 | Recent request count | | 总成本 | Recent total cost |
4.2.12 用量日志 (UsageLogs)
渲染: ui/ — 由 Usage 页面进入
表格展示最近日志,支持按时间范围筛选。点击行进入 UsageLogDetail 查看完整 JSON 响应。
4.2.13 OpenClaw 工作区 (ConfigOpenClawWorkspace)
展示文件: AGENTS.md, SOUL.md, USER.md, IDENTITY.md, TOOLS.md, MEMORY.md, HEARTBEAT.md, BOOTSTRAP.md, BOOT.md
操作: - Enter — 打开文件编辑 - / — 搜索每日记忆 - D — 删除每日记忆
4.2.14 托管账号 (SettingsManagedAccounts)
Codex OAuth 管理: - 设备码登录流程 (Device Code OAuth) - 账号绑定/解绑 - 默认账号设置 - 登录过期处理
5. 交互逻辑
5.1 焦点系统
1 | pub enum Focus { |
焦点切换规则: - ← — 切换到 Nav - → — 切换到 Content (仅当路由有内容列表时) - Tab / BackTab — 特定路由下的 Pane 切换 (Usage, Sessions)
5.2 选择器面��� (Special Focus Panes)
UsagePane: Models | Providers | Recent - 由 Tab / BackTab 切换
SessionsPane: List | Detail - 由 ← / → 或 Tab / BackTab 切换
5.3 路由导航
1 | Nav 聚焦: |
路由栈: route_stack: Vec<Route> — 支持多级后退
路由转换规则 (set_route_no_history): - 进入 Sessions 时重置时间锚点 - 进入 Main 时清空路由栈、聚焦 Nav - 离开 DailyMemory/OpenClawTools/OpenClawAgents 时清除表单状态
5.4 全局按键
| 按键 | 操作 |
|---|---|
← | 切换到导航栏 |
→ | 切换到内容 |
↑ / ↓ | 上下移动 |
Enter | 确认/进入 |
Esc | 返回上一页 (首页则退出确认) |
/ | 打开过滤输入 |
Ctrl+F | 打开过滤输入 |
Ctrl+C | 退出 TUI |
Ctrl+, | 跳到设置页 |
? | 打开上下文帮助 |
[ / ] | 切换应用 (前/后) |
P | 首页启动/停止代理 |
h / j / k / l | Vim 风格导航 (等效 ←↓↑→) |
5.5 过滤系统 (FilterState)
1 | pub struct FilterState { |
/激活过滤输入- 按 Enter 清除过滤
- 按 Esc 清除并退出
- 过滤作用于当前页面的所有列表项
5.6 应用切换 (SetAppType)
- 顶部 Header 显示所有可见应用的 Tab
- 切换时显示 loading projection
UiDataByAppCache实现快速切换
5.7 Toast 通知
1 | pub struct Toast { |
渲染位置: 屏幕下方居中,带彩色边框
5.8 确认对话框
1 | pub enum ConfirmAction { |
- 通过
Overlay::Confirm(ConfirmOverlay)显示 - Y/N 确认
6. Overlay 系统
6.1 Overlay 枚举
Overlay 是覆盖在主 UI 之上的模态界面:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34pub enum Overlay {
None,
Help(HelpState), // 上下文帮助
Confirm(ConfirmOverlay), // 确认对话框
TextInput(TextInputState), // 文本输入框
BackupPicker { selected: usize }, // 备份选择器
TextView(TextViewState), // 文本查看器
CommonSnippetPicker { selected: usize }, // 通用片段选择
ProviderTestMenu { provider_id, selected },// Provider 测试菜单
FailoverQueueManager { selected: usize }, // Failover 队列管理器
ClaudeModelPicker { selected, editing }, // Claude 模型选择
ClaudeApiFormatPicker { selected }, // API 格式选择
UsageQueryTemplatePicker { selected }, // 用量查询模板选择
ManagedAccountPicker { ... }, // 托管账号选择
ManagedAccountActionPicker { ... }, // 托管账号操作选择
HermesModelsPicker { editing }, // Hermes 模型选择
ModelFetchPicker { ... }, // 模型抓取进度
OpenClawToolsProfilePicker, // OpenClaw 工具配置
OpenClawAgentsFallbackPicker { ... }, // OpenClaw 代理回退
McpAppsPicker { id, name, selected, apps },// MCP 客户端选择
VisibleAppsPicker { selected, apps }, // 可见应用选择
SkillsAppsPicker { ... }, // Skill 客户端选择
SkillsImportPicker { ... }, // Skill 批量导入
SkillsSyncMethodPicker, // Skill 同步方式选择
McpEnvPicker, McpTypePicker, McpEnvEntryEditor,
Loading { kind, title, message }, // 加载指示器
SpeedtestRunning { url }, // 速度测试运行中
SpeedtestResult { url, lines, scroll }, // 速度测试完成
StreamCheckRunning { provider_id, name }, // 流检查运行中
StreamCheckResult { ... }, // 流检查完成
UpdateAvailable { current, latest, selected },
UpdateDownloading { downloaded, total }, // 更新下载中
UpdateResult { success, message }, // 更新结果
}
6.2 Overlay 方法
| 方法 | 说明 |
|---|---|
is_active() | 是否有任何 Overlay 打开 |
can_be_covered_by_help() | 是否可被帮助覆盖 (不关闭现有 Overlay) |
is_editing() | 是否正在接受文本输入 (阻断 Vim 导航) |
6.3 Overlay 层级
1 | 主 UI |
Toast 渲染优先级可在 Overlay 之前或之后 (持久 Toast + 确认登录场景例外)。
6.4 文本输入 Overlay
1 | pub struct TextInputState { |
TextSubmit 决定提交后触发的 Action: - ConfigExport, ConfigImport, ConfigBackupName - SettingsProxyListenAddress, SettingsProxyListenPort - SkillsInstallSpec, SkillsDiscoverQuery, SkillsRepoAdd - UsageCustomRange, WebDavJianguoyunUsername/Password - 等 20+ 种
7. 表单系统
7.1 表单类型
1 | pub enum FormState { |
7.2 表单状态
1 | pub enum FormMode { |
7.3 Provider 表单
子页面 (ProviderFormPage): | 页面 | 说明 | |——|——| | Main | 主表单 (供应商模板 → 字段 → 预览) | | CodexLocalRouting | Codex 本地路由配置 | | CodexModelCatalog | Codex 模型目录映射 | | UsageQuery | 用量查询配置 |
表单字段 (ProviderAddField, 30+ 字段):
| 字段 | 类型 | 适用 App |
|---|---|---|
| Id | TextInput | 所有 |
| Name | TextInput | 所有 |
| WebsiteUrl | TextInput | 所有 |
| Notes | TextInput | 所有 |
| BaseUrl | TextInput | 所有 (按 app 变体) |
| ApiKey | TextInput | 所有 (按 app 变体) |
| ApiFormat | Picker | Claude / Codex |
| ModelConfig | Picker | Claude |
| Model | TextInput | Codex |
| LocalRouting | Toggle | Codex |
| OAuthAccount | Picker | Codex |
| AuthType | Picker | Gemini |
| Models | Picker + Editor | Hermes / OpenClaw |
| ApiMode | Picker | Hermes |
| RateLimitDelay | TextInput | Hermes |
| NpmPackage | Picker | OpenCode |
| UserAgent | Toggle | OpenClaw |
| ApiProtocol | Picker | OpenClaw |
| CommonSnippet | TextInput | 所有 (通用片段) |
| UsageQuery | Toggle | 所有 |
| HideAttribution | Toggle | Claude |
模板系统: 预设模板自动填入常见供应商的 URL、模型、API Key 格式等
7.4 MCP 表单
字段 (McpAddField): | 字段 | 类型 | |——|——| | Id | TextInput | | Name | TextInput | | Type | Picker (stdio/http/sse) | | Command | TextInput (stdio) | | Args | TextInput (stdio) | | Url | TextInput (http/sse) | | Env | 键值对编辑器 | | Apps | 多选 (Claude/Codex/Gemini/OpenCode/Hermes) |
7.5 Prompt 表单
字段 (PromptMetaField): | 字段 | 类型 | |——|——| | Id | TextInput | | Name | TextInput | | Description | TextInput | | Content | EditorState (全屏编辑器) |
8. 编辑器系统
8.1 EditorState
1 | pub struct EditorState { |
8.2 行编辑器 (TextInput)
内联行编辑,支持 readline 风格快捷键:
| 快捷键 | 操作 |
|---|---|
Ctrl+A | 行首 |
Ctrl+E | 行尾 |
Ctrl+B | 左移 |
Ctrl+F | 右移 |
Ctrl+D | 前删字符 |
Ctrl+K | 删到行尾 |
Ctrl+U | 删到行首 |
Ctrl+W | 前删单词 |
Alt+B | 左移单词 |
Alt+F | 右移单词 |
Backspace | 前删单词 (Alt 时) |
Ctrl+H | Mac 兼容 → 等价 Backspace |
支持策略: TextInputPolicy { max_chars, sanitize }
8.3 全屏编辑器
在表单中用于 Prompt 内容等长文本编辑,提供标准光标和文本操作。
9. 主题系统
9.1 主题结构
1 | pub struct Theme { |
9.2 应用强调色
| App | 颜色 | Dracula RGB |
|---|---|---|
| Codex | 绿色 | (80, 250, 123) |
| Claude | 青色 | (139, 233, 253) |
| Gemini | 粉色 | (255, 121, 198) |
| OpenCode | 橙色 | (255, 184, 108) |
| Hermes | 黄色 | (241, 250, 140) |
| OpenClaw | 珊瑚红 | (255, 79, 64) |
9.3 颜色模式
| 模式 | 检测条件 |
|---|---|
NoColor | NO_COLOR 环境变量存在 |
TrueColor | COLORTERM/TERM 含 truecolor/24bit,或未知能力 |
Ansi256 | TERM_PROGRAM=Apple_Terminal 或 SSH + plain xterm |
CC_SWITCH_COLOR_MODE | 环境变量强制覆盖 (none/rgb/ansi256) |
9.4 选择器样式
- NoColor 模式: 反转视频 (reverse-video)
- TrueColor/Ansi256: 强调色背景 + 白字
10. Worker 系统
10.1 后台 Worker
所有耗时 I/O 操作通过 mpsc channel 异步执行,不阻塞渲染循环:
| Worker | 请求类型 | 用途 |
|---|---|---|
app_data | AppDataReq | Provider/Config/MCP/Prompt/Skills/Proxy 数据加载 |
usage_pricing | UsagePricingReq | 用量统计 + 定价数据 |
sessions | SessionReq | 会话扫描/消息加载 |
proxy | ProxyReq | 代理启动/停止/状态 |
speedtest | 无 | Provider 速度测试 |
stream_check | StreamCheckReq | Provider 流检查 |
quota | QuotaReq | 配额刷新 |
skills | SkillsReq | Skills 安装/卸载/同步/发现 |
webdav | WebDevReq | WebDAV 上传/下载/连接测试 |
update | UpdateReq | 版本检查/更新 |
model_fetch | ModelFetchReq | 远程模型列表抓取 |
managed_auth | ManagedAuthReq | Codex OAuth 设备码登录 |
local_env | LocalEnvReq | 本地环境检查 |
10.2 Worker 消息流
1 | UI 层 Worker 后端 |
10.3 App Data 加载
三种加载模式 (AppDataLoadKind): - Initial — 首次加载,全量 - Full — 全量刷新 - Snapshot — 快速快照 (应用切换时)
按 AppType 缓存 (UiDataByAppCache: HashMap<AppType, (AppData, ReloadToken)>)
11. 关键数据结构参考
11.1 App 主状态
1 | pub struct App { |
11.2 Action 枚举 (80+ 变体)
1 | pub enum Action { |
11.3 数据加载函数参考
| 函数 | 模块 | 返回 |
|---|---|---|
UiData::load() | data.rs | 完整 UiData |
UiData::load_fast_snapshot_from_state() | data.rs | 快照 UiData |
load_providers_with_mode() | data.rs | ProvidersSnapshot |
load_mcp() | data.rs | McpSnapshot |
load_prompts() | data.rs | PromptsSnapshot |
load_config_snapshot() | data.rs | ConfigSnapshot |
load_usage_snapshot_for_range() | data.rs | UsageSnapshot |
load_model_pricing_snapshot() | data.rs | ModelPricingSnapshot |
load_skills_snapshot() | data.rs | SkillsSnapshot |
load_proxy_snapshot_from_state() | data.rs | ProxySnapshot |