12 KiB
提案:设置改为全局页面(设置 + 高级功能 合一)
状态:待评审(本文档为主代理自查 + 交叉审核用,未开始实现) 日期:2026-10-15
1. 目标
取消「设置」弹窗,改为带返回按钮的全局页面;三个 Tab 从同一入口打开同一页面(顺序 全局设置 | 高级设置 | 关于):
- 全局设置 Tab:全局默认模型 / 供应商级别的默认项
- 高级设置 Tab(原「高级设置」页内容 + 代理设置):代理设置(thinking/循环控制/权限规则/hooks 等)、Kimi Code WebUI 快速打开、models.dev 模型数据、子代理副模型/模型池、实验开关
- 关于 Tab(id 仍为
general,原SettingsModal内容):外观(主题/语言)、更新(版本/检查/下载/安装)、Kimi Code CLI 版本与兼容性、关于
已确认的三个决策点:
| 决策 | 选择 | 含义 |
|---|---|---|
| A | 1 | AgentSettingsPanel 从供应商编辑页(ProviderEdit.tsx)移除,只保留在设置页高级 Tab —— 单一入口 |
| B | 2 | 保留旧 view === "subagent" 渲染分支作为兼容外壳,不删除 |
| C | 1 | 所有 section 抽成独立可复用组件,不复制内容,避免两处分叉 |
2. 现状核实(已完成调研,行号为当前 main 上的位置)
| 文件 | 关键事实 |
|---|---|
src/App.tsx:83-85 |
view 类型:"list" | "edit" | "subagent" | "dashboard" | "sessions" | "plugins",需新增 "settings" |
src/App.tsx:89 |
showSettings state(改造后删除) |
src/App.tsx:600-638 |
顶部导航(供应商/数据看板/会话),无 dirty 拦截,直接 setView |
src/App.tsx:652-665 |
齿轮按钮 → setShowSettings(true);含「有更新」绿点徽标(保留) |
src/App.tsx:682-696 |
渲染分支:subagent → <SubagentSettingsPage>(保留,即决策 B) |
src/App.tsx:697-724 |
ProviderEdit 入参含 rawOther / onRawOtherChange / defaultModel |
src/App.tsx:906-914 |
footer「高级设置」按钮(仅 view === "list" 显示)→ setView("subagent"),改为打开设置页高级 Tab |
src/App.tsx:919-926 |
<SettingsModal> 挂载(改造后删除) |
src/components/SettingsModal.tsx(589 行) |
4 个 section:外观 :257、更新 :302、Kimi Code :434、关于 :500;弹窗专属:createPortal、fixed inset-0 蒙层、Esc 关闭、body.overflow 锁定;handleDownload(:103-130)含「无安装包 → 打开 release 页」回退(近期 bug 修复,必须逐字保留);全仓库仅 App.tsx 引用 |
src/components/SubagentSettingsPage.tsx(710 行) |
props { rawOther, models, onChange, onBack };返回头 :402-414,内容区 :416-707(masterOn 提示条 + WebUI Card + models.dev Card + 副模型/模型池 Card + 实验开关 Card);仅 App.tsx 引用 |
src/components/AgentSettingsPanel.tsx(451 行) |
props { rawOther, onChange, models?, defaultModel? };读写全局 config.raw_other;自带 mt-6 外壳和 agentSettings 小标题,页面内可直接复用,该文件本身不改;目前唯一挂载点 ProviderEdit.tsx:478-484(agent === "kimi_code" 时) |
src/i18n/zh.ts / en.ts |
已有:settings、settingsTitle、settingsAppearance、settingsUpdates、settingsAbout、subagentSettings、back;三个 Tab 直接复用 settingsTabGlobal / subagentSettings / settingsAbout |
数据流结论(合并无数据层改动):AgentSettingsPanel、SubagentSettingsPage 都只读写全局 config.raw_other,App.tsx 已有现成回调 onRawOtherChange={(next) => updateConfig((cfg) => ({ ...cfg, raw_other: next }))}(App.tsx:722-724)。
3. 目标结构
src/components/settings/
├── SettingsPage.tsx 新增:返回头 + Tab 切换 + 内容容器(本次唯一的"页面")
├── SettingsGeneralTab.tsx 新增:SettingsModal 的 4 个 section,去掉弹窗外壳
└── SettingsAdvancedTab.tsx 新增:AgentSettingsPanel + SubagentSettingsPage 内容区,去掉返回头
src/components/
├── SettingsModal.tsx 删除(仅 App.tsx 引用,一并摘除)
└── SubagentSettingsPage.tsx 改为薄壳:返回头 + <SettingsAdvancedTab>(决策 B 的兼容入口)
3.1 组件接口契约
// SettingsGeneralTab.tsx —— 自包含(主题/语言/版本/下载状态都内置)
interface SettingsGeneralTabProps {
updateInfo: UpdateInfo | null;
checking: boolean;
onCheckUpdate: () => void;
lastChecked: number | null;
}
// SettingsAdvancedTab.tsx —— 纯内容,无返回头
interface SettingsAdvancedTabProps {
rawOther: unknown;
models: Record<string, Model>;
defaultModel: string | null; // 供 AgentSettingsPanel 判断 effort 支持
onChange: (nextRawOther: unknown) => void;
}
// SettingsPage.tsx
// 返回头与 SubagentSettingsPage 同款:`← {t("back")}` + 标题 `t("settingsTitle")`("设置")
interface SettingsPageProps {
// Tab 受控(不用 initialTab + 内部 state)——齿轮按钮在 header 常驻,
// 停留在设置页时再点齿轮,必须能切回第一个 Tab(全局设置);非受控则 prop 变化不生效。
activeTab: "global" | "advanced" | "general";
onTabChange: (tab: "global" | "advanced" | "general") => void;
onBack: () => void;
// 透传给三个 Tab:
updateInfo / checking / onCheckUpdate / lastChecked
rawOther / models / defaultModel / onRawOtherChange
}
3.2 Tab 挂载策略(审核补充的关键点)
原 SettingsModal 常驻挂载(open=false 时返回 null 但 hooks 仍在跑),下载进度监听、downloadedPath 状态在弹窗开关间不丢失。改造后若用条件渲染 {tab === "general" && ...},下载进行中切到高级 Tab(或按返回离开页面)会丢失 downloadedPath,「安装」按钮消失——这是行为回归。
方案:三个 Tab 面板都常驻挂载,非激活 Tab 用 className="hidden" 隐藏。 代价仅是高级 Tab 提前挂载(一次 getAgentSettings IPC 读,无副作用),换来下载状态与滚动位置不丢。
4. App.tsx 接线
view类型加"settings";删除showSettingsstate 与<SettingsModal>挂载、import;删除SettingsModal.tsx。- 新增
settingsTabstate("global" | "advanced" | "general")。 - 齿轮按钮(
App.tsx:670)→setSettingsTab("global"); setView("settings")(第一个 Tab 即全局设置);绿点徽标保留。 - header「高级设置」按钮 →
setSettingsTab("advanced"); setView("settings"),文案键不变。 - 渲染分支加(Tab 受控,state 放 App):
) : view === "settings" ? ( <SettingsPage activeTab={settingsTab} onTabChange={setSettingsTab} onBack={() => setView("list")} updateInfo={updateInfo} checking={checking} onCheckUpdate={checkNow} lastChecked={lastChecked} rawOther={config.raw_other} models={config.models} defaultModel={config.default_model} onRawOtherChange={(next) => updateConfig((cfg) => ({ ...cfg, raw_other: next }))} /> - 兼容分支(决策 B):
view === "subagent"保留,仅多传defaultModel={config.default_model}(SubagentSettingsPage新增可选 prop)。此后 UI 上无入口指向它,代码保留。
5. ProviderEdit.tsx 摘除(决策 A)
- 删除
AgentSettingsPanel挂载块(:477-484)与 import(:16)。 - 连锁清理(
noUnusedLocals/noUnusedParameters开启,tsc 会逼出来):rawOther/onRawOtherChange两个 props 若无其他使用则从ProviderEditProps(:93附近)和App.tsx:721-724调用处一并删除;modelsByAlias等仅为其存在的中间变量同步清理。以npx tsc --noEmit零错误为准。
6. i18n
三个 Tab 文案键(zh / en):
| key | zh | en |
|---|---|---|
settingsTabGlobal |
全局设置 | Global Settings |
subagentSettings |
高级设置 | Advanced Settings |
settingsAbout |
关于 | About |
不新增其他 key;SubagentSettingsPage 薄壳继续复用 subagentSettings / back。
7. 行为差异清单(有意为之,评审确认)
| # | 差异 | 说明 |
|---|---|---|
| 1 | Esc 不再关闭设置 | 弹窗行为;页面用「← 返回」。如需保留可加,默认不加 |
| 2 | 设置不再遮挡底层界面 | 弹窗 → 页面,符合本次需求本意 |
| 3 | footer 出现在设置页 | footer 显示条件不含 settings,保存/重载按钮仍可用(高级 Tab 改 raw_other 会标 dirty,能在页内直接保存,是改善而非回归) |
| 4 | 顶部导航「供应商」高亮条件 | list/edit/subagent 高亮(App.tsx:604),settings 不加入高亮,返回后仍正确 |
| 5 | 齿轮/高级按钮进入设置页不做 dirty 拦截 | 与现有 header 导航(数据看板/会话)一致;配置在内存中不丢失 |
| 6 | 停留在设置页时再点齿轮 | 切回第一个 Tab(全局设置),受控 Tab 保证 prop 变化生效,见 3.1 |
| 7 | footer 按钮文案 | 与高级设置 Tab 共用 subagentSettings(高级设置),文案一致 |
8. 不做的事(边界)
- 不动 dashboard / sessions / plugins 页面,不动 Rust 侧任何代码。
- 不改
AgentSettingsPanel.tsx内部(含mt-6外壳与「代理设置」小标题,在高级 Tab 中观感正常);高级 Tab 不再按agent === "kimi_code"判断——当前仅支持 kimi_code(getInitialAgent恒返回"kimi_code"),判断无意义。 - Tab 选择不持久化到 localStorage:每次进入按入口决定初始 Tab(齿轮 → 全局设置,header「高级设置」→ 高级设置)。
- 不新增组件测试:项目测试先例仅覆盖
src/lib/src/hooks,无组件测试;本次跟随先例。 SettingsGeneralTab只在设置页打开时挂载(原弹窗常驻挂载)——下载监听只能由设置页内的按钮发起,无实际影响。- 工作区里已有的未提交改动(更新下载修复、OpenTerminal 链接、models-dev 数据)与本提案无关,实现时不混入,分开提交。
9. 实施步骤(依赖顺序)
- 并行抽取(两个子代理,文件不相交):
a.
SettingsGeneralTab.tsx(含下载回退修复逐字保留) b.SettingsAdvancedTab.tsx+SubagentSettingsPage.tsx改薄壳 - 接线(主代理):
SettingsPage.tsx+App.tsx+ProviderEdit.tsx+ i18n 两键 + 删除SettingsModal.tsx。 - 验证:
npx tsc --noEmit零错误 →npm test(150 例基线)→cargo test(应无影响,跑一遍确认 208 例基线)。 - dev 实测(注意先
taskkill关正式版,单实例锁冲突):见 10。
10. 验收标准
自动化:npx tsc --noEmit 零错误;npm test 全绿;cargo test 全绿。
手动清单(npm run tauri-dev):
- 齿轮 → 打开设置页,初始停在「全局设置」Tab,四个 section(外观/更新/KimiCode/关于)在「关于」Tab 中原弹窗内容一致(含 OpenTerminal 链接)
- header「高级设置」→ 同一页面,初始停在「高级设置」Tab,含代理设置 + 原 4 个 Card
- 「← 返回」回到供应商列表
- 供应商编辑页模型 Tab 不再出现代理设置区块
- 高级 Tab 改动(如实验开关)→ footer 出现未保存标记 → 可在页内保存 → 配置文件落盘正确
- 检查更新 / 下载 / 安装流程可用;下载中切到高级 Tab 再切回,进度与「安装」按钮不丢
- 主题、语言切换即时生效
- 有新版本时齿轮绿点徽标仍显示
- 停留在设置页时再点齿轮 → 切回第一个 Tab(全局设置)
- header「高级设置」按钮文案与高级设置 Tab 名一致
11. 风险与回退
| 风险 | 缓解 |
|---|---|
| 抽取时漏搬/改错下载回退逻辑 | 验收清单专项覆盖;diff 逐行 review |
| 高级 Tab 提前挂载引发意外 IPC | 仅一次只读 getAgentSettings,无写操作 |
ProviderEdit props 清理波及其他用法 |
已核实 rawOther 仅 AgentSettingsPanel 消费,tsc 兜底 |
| 回退 | 单独 commit,git revert 即可整体回退,不碰数据格式 |