# UI / 设置 重构 · 预调研

> 状态：**预调研 + 第 0 步「止血」已落地**（见 §7；改动仅在 pre 线本地，未提交未发布）。这份文档的用途不是"决定怎么改"，而是把现状量清楚、把可选路径摆开，**由你决定走哪条路、先走哪一步**。
> 生成时间：2026-09-10 · 对应版本 v2.4.0

---

## 0. 你提出这个问题时说的话（作为验收标准）

> "现在的 UI 和设置的版本还是从最开始一步一步累加的，非常繁琐，以至于已经到了需要在 README 里贴项目文档教用户做事的地步了。"

拆成两条可验收的目标：

1. **界面不再需要 README 教**：README 里"点这里、勾选那里"的段落应当消失（不是挪走，是不需要）。
2. **设置不再繁琐**：找得到、看得懂、改得动，且知道改了会发生什么。

---

## 1. 现状测量（硬数据）

### 1.1 代码体量

| 文件 | 行数 | 体积 | 说明 |
| --- | --- | --- | --- |
| `lib/index.js`（宿主半边） | 7,825 | 468 KB | 记忆引擎 + 配置表 + 全部 HTTP 路由 |
| `lib/client.js`（界面半边） | 4,450 | 409 KB | 全部界面组件（React + 原生 DOM API，零依赖） |
| `README.md` / `README.zh-CN.md` | 308 / 309 | 34 / 31 KB | 面向用户的首页 |
| `docs/USER-GUIDE.{en,zh-CN}.md` | 289 / 289 | 34 / 32 KB | 用户手册（两份共 66 KB） |

### 1.2 README 的结构（这就是"界面速览"长成什么样）

`README.zh-CN.md` 的 `## 界面速览` 一节下，列了 **9 个并列子节**：

```
### 记忆面板 · 概览（暂离问候 + AI 时段总结）
### 记忆中枢 · 三层记忆店与技能晋升审批
### 唤起回顾 · 每次激活决策可打分
### 欢迎向导 · 功能开关与引擎检测
### 外部记忆扫描（欢迎向导内）
### 连接其他 AI 工具
### 日历视图
### 工作区关系图
### 设置页
```

（另有 8 个"她怎么 X"章节 + 安装/配置/结构/架构/限制 —— 全文 20+ 一级二级标题。）

### 1.3 "文档在替界面导航"的量化证据

在 README 正文里检索"教用户操作"的词汇，出现次数：

| 词 | 次数 | 含义 |
| --- | --- | --- |
| 向导 | 16 | 流程入口靠文档指路 |
| 开关 | 12 | 配置项靠文档点名 |
| 设置页 | 10 | 功能位置靠文档定位 |
| 面板 | 8 | 界面区域靠文档描述 |
| 点击 | 6 | 操作步骤靠文档背书 |
| 按钮 / 勾选 | 2 / 2 | 同上 |

**判据**：一个自解释的界面，其 README 里这类词应该趋近于 0（因为界面上自己写着）。

### 1.4 界面与配置的真实规模（逐条带行号，见附录）

| 指标 | 数值 | 说明 |
| --- | --- | --- |
| 面板页签 | **12** | 概览 / 日志 / 唤起回顾 / 记忆中枢 / 存储管理 / 笔记 / 白板 / 反思 / 接续 / 日历 / 检索 / 工作区 |
| 设置页分组 | **8** | 自动记忆引擎 / 记忆中枢 / 外观 / 存储 / 记忆窗口 / 自动化 / 上下文管理 / 维护 |
| 配置键总数 | **85** | 另有嵌套 `externalSources` 13 个子键 |
| 界面可改 | **67** | = 设置页 60 + 白板页 2 + 引擎联动草稿 3 + 首启向导 2 |
| **仅文件可改** | **18** | 15 个全文零引用（`pythonBackendExecutable`、`shadowRetrievalEnabled`、`maxPacketChars`…）+ 3 个只读引用（`autoContinueRefreshRitual`、`autoContinueRefreshTimeoutSeconds`、`unattendedAutoHours`） |
| 弹窗种类 | 7 | 更新说明 / 首启向导 / 模型下载 / 通知 / 总结 / 暂别回来 / 语义安装 |
| **HTTP 端点** | **46** | 全部 loopback-only；其中 **2 个对不上任何界面**：`/activation-inbox-pre`、`/subagent-gc`（`client.js` 零引用，但**不是死端点**：`activation-inbox-pre` 有契约测试与注入入口消费者，`subagent-gc` 有 CLI 消费者 `tools/subagent-gc.mjs`；**别删**，见 §7 更正） |
| `client.js` 具名函数 | 125 | 单文件 4,493 行 |
| 补丁式入口 | **4 处 / 5 个物件** | 见下 |

**4 个被盘点明确标为"补丁"的地方**（原文注释自己写着"后期追加""美学对齐"）：

1. 「🧩 安装向导」+ 环境检测 ⟳ **挤在语义引擎区块的同一行**，没有独立入口；
2. 调试中心 **挂在设置页最末、保存栏之后**，自带 borderTop，与 8 个分组体系并列；
3. 面板标题栏的"悬浮钉" **插在原有 4 个按钮中间**（原注释标着 2026-09-08）；
4. 「接续」页签整块是后期模块，4 个平级函数挂在页签体系之外。

**这四条比任何主观描述都更能说明"逐步累加"**：新功能没有位置，只能往既有缝隙里塞。

### 1.5 结构体感（真正的病根）

盘点里最扎眼的不是"界面多"，而是**加一个功能要动好几处、而且没有任何机制保证它们同步**：

| 现象 | 证据 | 后果 |
| --- | --- | --- |
| **两套路径表手工同步** | `index.js:129-176`（46 条）与 `client.js:858-897`（40 条，键名风格还不同）各存一份同一批路径 | 改一个端点要记得改两处 |
| **20 处裸字符串绕过常量表** | `client.js:1626/1629/1804/1906/3092/3110/3944/3976…` 直接写 `fetch('/api/dsh-auto-memory-pre/…')` | 路径表**形同虚设**，改端点必漏。**2026-09-10 已归零（28 处 → 0，口径含 `apiPost` 形态；先前的"20 处"只数了 `fetch(` 形态），并由路径表一致性锁常驻看守 —— 见 §7** |
| **4 条互不相同的写配置路径** | `SettingsPage.save()`（整对象 POST）、`PlanTab.autoSave`（patch 直发）、首启向导两处 | **加一个配置项要改四个地方** |
| **巨型函数** | `SettingsPage` **582 行**装下 8 分组 + 目录浏览器 + 模型抽屉 + 环境检测 + 向导挂载 + 更新检查 + 调试中心；`DialogHost` **478 行**装下 7 种弹窗 + 首启向导状态机 | 想改一个分组，得在一屏滚不完的函数里找 |
| **逐字重复的实现** | `RefineTab` 的 `badge`/`reasonChip`/`apeRow` 两份逐字相同（`client.js:1644-1662` / `1682-1699`） | 改一处忘一处 |
| **幽灵配置键** | `client.js:4106` 写 `pythonGpu`，而 `DEFAULT_CONFIG` 里没有这个键 | 界面上能改、宿主端不认 |

**结论**：这解释了为什么"只能一步步累加"——**新增功能的边际成本不是 1 处，而是 3-5 处**，于是最省事的做法永远是往现有缝隙里塞。UI 重排如果不动这层，做完还是散的。

---

## 2. 问题解剖：为什么会有"繁琐"的体感

不是"东西多"，是**三类异质内容被平铺在同一层**：

| 类别 | 例子 | 用户的心智模式 |
| --- | --- | --- |
| **状态展示** | 水位/用量、记忆条数、接续倒计时 | "瞄一眼，别烦我" |
| **内容管理** | 三层记忆店、技能晋升审批、日历、关系图 | "我要翻、要找、要改" |
| **流程操作** | 一键接续、外部记忆扫描、安装向导、权限继承 | "我要做一件事，做完就走" |

三者混装后产生三个具体症状：

1. **入口混装**：想改一个开关的人，要先路过"技能审批""关系图"这些一辈子点一次的界面。
2. **配置混装**：37 个键里，一部分是**用户该懂的政策**（开关、阈值、容量），一部分是**实现细节**（文件路径、模型文件名、目录名）。README 必须解释后者 → 文档被实现细节绑架。
3. **文档替界面说话**：README 用"在设置页勾选…"的方式描述界面 —— 这等价于承认界面自己没说清楚。

---

## 3. 判定判据（建议用这四条选路）

| 判据 | 现状 | 目标 |
| --- | --- | --- |
| J1 新用户冷启动 | 需要读 README 才会开引擎/连接 | **不看文档** 5 分钟内完成"开启 → 选引擎 → 看见记忆在工作" |
| J2 老用户找开关 | 靠记忆位置 / 靠 README 索引 | **2 次点击**内到达上次改过的开关 |
| J3 文档负担 | README 大量操作指引 | README 只留"这是什么/为什么值得装"，操作指引归零 |
| J4 交付节奏 | — | **每一步都能单独发版**，不出现"重构期间不可用" |

---

## 4. 四条可选路径

| 路线 | 做什么 | 动到的面 | 成本 | 风险 | 收益 |
| --- | --- | --- | --- | --- | --- |
| **A. IA 重排** | 按"状态 / 内容 / 配置"三层重组入口；一级导航（状态条 → 中枢 → 设置）；把 9 个并列面板收进 3 个容器 | `client.js` 结构性重排（约 40-60% 的界面代码） | 高 | 中（易在重排中丢功能） | 最高：一次性解决"入口混装" |
| **B. 设置台重构** | 37 键分 3-5 组（记忆/引擎/接续/界面/高级）；加"场景预设"（省心/平衡/激进）；仅文件项折叠进"高级"；每项配一句"改了会怎样" | 设置页 + 配置表元数据（宿主加键描述，界面读元数据渲染） | 中 | 低（不碰引擎逻辑） | 高：直接消灭"设置繁琐" |
| **C. 向导优先** | 把"首次使用 3 步"做成默认首屏；老用户可关；引擎安装/权限/连接外部工具都从这里走 | 向导组件 + 首屏路由 | 中 | 中（向导已存在，重做信息层级，改动易被已装用户忽略） | 中：解决 J1，不解决 J2/J3 |
| **D. 最小增量** | 只加：全局搜索（设置项 + 功能跳转）、说明性 tooltip、危险项二次确认 | `client.js` 局部 | 低 | 低 | 低-中：改善可发现性，但架构不变，README 仍要教 |

### 4.1 我倾向的顺序（供你否决）

**B 打底 → A 收口**，理由：

- 设置是**改错成本最低、用户感知最强**的一块：不需要动引擎语义，就能让"繁琐"体感下降最多。
- IA 重排的价值依赖"分组成型"：分组没定就重排，等于把混乱换个地方摆，后面还要再洗一次。
- 两者都能拆成可单独发版的小步（符合 J4）。

**不建议**先做 C：你现在的痛点不是"新用户进不来"，而是"老用户在里面绕"。向导优化的是另一批人。

**D 可作为任意路线的第 0 步**（成本极低、立即可发），但它不构成路线。

---

## 5. 若走 B：第一步的具体形态（等你拍板）

**第 0 步（前置，可与 B 同批做，也可以先单独发一版）—— 先止血，否则 B 做完还是散的**（**2026-09-10 已执行，见 §7；其中第 1、3 条的机制已在 §7.2 更正**）：

1. **路径表单一化**：把 `client.js:858-897` 那份删掉，只留 `index.js` 一份，并把 20 处裸 `fetch('/api/…')` 全部改成走常量。
2. **写配置路径收敛成 1 条**：现在有 4 条（`SettingsPage.save` / `PlanTab.autoSave` / 向导两处），收敛后"加一个配置项"才只需改一处。
3. **顺手清两个死端点**：`/activation-inbox-pre`、`/subagent-gc` 界面上没有任何引用 —— 要么接上界面，要么删掉。

**B 主体（三件事，一次发版）：**

1. **配置分组**：85 键归到 记忆 / 引擎 / 接续 / 界面 / 高级 五组（**分组规则写在宿主 `DEFAULT_CONFIG` 的元数据里，界面按元数据渲染** —— 以后加键自动归位，不再改界面）。
2. **场景预设**：三档（省心 / 平衡 / 激进）一键写入一组值，用户仍可逐项微调并看到"已偏离预设"。
3. **仅文件项**：18 个键默认折叠，展开时明确标注"改这里可能让插件不工作"；同时修掉幽灵键 `pythonGpu`（界面能写、宿主不认）。

**不做**：不改任何引擎行为、不改水位/接续判据、不改端点语义。

**为什么先做第 0 步**：B 的核心价值是"以后加东西不用改 4 个地方"。如果只把界面重排一遍、底下还是 4 条写配置路径 + 3 份路径清单，**下一轮累加会把刚整理好的结构再冲散一次**。

---

## 6. 需要你回答的三个问题

1. **痛点是"找得到但太杂"，还是"根本找不到某个开关"？**（前者指向 B，后者指向 D+A）
2. **目标用户是谁**：只有你自己 / 少数朋友 / npm 上的公开用户？（决定要不要为"陌生用户"付向导成本）
3. **可接受的改动规模**：一次大重构（可能几天不可用）/ 连续小步（每步可发版）？

---

## 附录

- `docs/UI-INVENTORY-RAW.md` —— 界面表层 / 配置键 / 端点逐条盘点（带行号）

---

## 7. 第 0 步「止血」执行记录（2026-09-10）

**状态**：已落地并验证，**仅 pre 线本地，未提交、未发布**（改宿主/界面两个半边后需重启 dsh web 才生效）。
改动文件：`lib/client.js`；新增 `tests/smoke/smoke-test-api-paths-pre.mjs`。

### 7.1 做了什么

| # | 动作 | 结果 |
| --- | --- | --- |
| 1 | 客户端路径表改前缀常量 + 补键 | `var ROUTE_PREFIX` 为唯一前缀；表从 40 键补到 **44 键**（补 `semanticStatus` / `semanticEmit` / `shadowRecent` / `reviewFeedback` / `memoryHub` / `storageManage`） |
| 2 | 裸路径字面量归零 | **28 处 → 0 处**（口径含 `apiPost(...)` 形态；§1.5 记的「20 处」只数了 `fetch(` 形态） |
| 3 | 写配置路径收敛 | 4 条 → **1 个 `saveConfigPatch()`**（设置页整对象 POST / 接续页 patch / 向导两处 patch 全部经它） |
| 4 | 顺带去重 | 10 处逐字重复的「刷新语义状态」收敛成 **`refreshSem()`** |
| 5 | 加回归锁 | 新增 `tests/smoke/smoke-test-api-paths-pre.mjs`，4 条断言全绿 |

### 7.2 更正两处早先判断（重要，别照着旧文做）

1. **「2 个死端点」不成立。** 它们只是**界面零引用**，不是没人用：
   - `activation-inbox-pre` —— 契约测试与注入入口（`docs/M6-CONTRACT.md`、`tests/smoke/smoke-test-m63/m70`）；
   - `subagent-gc` —— CLI 消费者 `tools/subagent-gc.mjs`。

   **删掉会打断它们。** 处理方式改为：在锁里写成显式白名单（宿主独有路径只允许这 2 条），以后谁新增宿主独有端点，谁就得改白名单并注明消费者。
2. **「把 `client.js` 那份路径表删掉、只留宿主一份」做不到。** `client.js` 是 `__ModuleLoader__` 手写 bundle，不能 `import` 宿主模块 —— 两个半边之间没有可共享的模块层。所以「只有一份事实」的正确机制是**测试锁**（客户端 ⊆ 宿主 + 表外无裸字面量），不是删代码。

### 7.3 验证

- 改动文件 `node --check` 通过、**无 BOM**、CRLF 行尾未变；
- 会读 `lib/client.js` 的 6 个 smoke 全绿（autocont-host 50/50、away-popup 12/12、continue-chain 58/58、handoff 53/53、startup-dispatch 12/12、新增路径锁 4/4）；
- 全量 smoke 69 个文件 —— 结果见本轮汇报。

### 7.4 仍未做（留给 B 主体，等路线拍板）

配置分组元数据（宿主 `DEFAULT_CONFIG` 带分组/说明字段，界面按元数据渲染）、场景预设三档、18 个仅文件键折叠、幽灵键 `pythonGpu`、以及任何界面层面的重排 —— 都属 §5「B 主体」。

