Files
KimiCodeSwitch/docs/PROPOSAL-settings-global-page.md
T
KimiSwitch Dev 38dbb5527a
Release / Version consistency (push) Canceled after 0s
Release / Build (macos-latest) (push) Canceled after 0s
Release / Build (ubuntu-latest) (push) Canceled after 0s
Release / Build (windows-latest) (push) Canceled after 0s
Release / Attach macOS install script (push) Canceled after 0s
feat(settings): 设置弹窗改为全局设置页面(全局/高级/关于三 Tab)+ 体检账单缺口与更新下载修复 — bump to v0.8.5
2026-10-16 08:50:45 +08:00

12 KiB
Raw Permalink Blame History

提案:设置改为全局页面(设置 + 高级功能 合一)

状态:待评审(本文档为主代理自查 + 交叉审核用,未开始实现) 日期: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 接线

  1. view 类型加 "settings";删除 showSettings state 与 <SettingsModal> 挂载、import;删除 SettingsModal.tsx。
  2. 新增 settingsTab state("global" | "advanced" | "general")。
  3. 齿轮按钮(App.tsx:670)→ setSettingsTab("global"); setView("settings")(第一个 Tab 即全局设置);绿点徽标保留。
  4. header「高级设置」按钮 → setSettingsTab("advanced"); setView("settings"),文案键不变。
  5. 渲染分支加(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 }))}
      />
    
  6. 兼容分支(决策 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. 实施步骤(依赖顺序)

  1. 并行抽取(两个子代理,文件不相交): a. SettingsGeneralTab.tsx(含下载回退修复逐字保留) b. SettingsAdvancedTab.tsx + SubagentSettingsPage.tsx 改薄壳
  2. 接线(主代理):SettingsPage.tsx + App.tsx + ProviderEdit.tsx + i18n 两键 + 删除 SettingsModal.tsx。
  3. 验证:npx tsc --noEmit 零错误 → npm test(150 例基线)→ cargo test(应无影响,跑一遍确认 208 例基线)。
  4. 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 即可整体回退,不碰数据格式