Files
ycode/docs/对话区模型选择器对齐-2026-10-16.md

91 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 对话区模型选择器:源码级恢复与验收(2026-10-16)
对话区的模型列表已从 YCode 自写下拉换成 REA 恢复的 WorkBuddy 5.8.2 当前 cr `ModelSelector`。原版触发按钮、Popover、列表、分组、子菜单与键盘行为由恢复体自身所有,YCode 只提供模型目录并执行真实写入。已构建并更新用户 YCode;实窗停在原会话、选中 `gpt-6-luna`、草稿为空。
## 原版来源
WorkBuddy 5.8.2,安装包根 SHA-256 `035254bc76310ea46317433e4840602094543ef323d2d5c5aff7c131729de918`。全部原版脚本、CSS 与画面经 REA 固定导出读取,未直接访问原版文件系统,未运行原 bundle。
| 证据 | 内容 |
| --- | --- |
| `ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb` | 固定运行脚本,`.tmp/rea-runtime-scripts.json` |
| `ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94` | 固定 CSS 与 token,`.tmp/rea-css-recovered/` |
| `ev_1c3853a9a4dd6f99cf76d1874ad57af7ad6fb56c33e6b4e5c2a2e770f91f462e` | 1336×901 原版模型菜单展开态:触发按钮计算样式、340×368 菜单、选中行 |
| `ev_330f3e738f84f51e0bc5beb5a2ee5647a8bd567407048d1ff239384929ba324f` | 同版本第二次触发按钮计算样式(用于交叉核对) |
关键源 SHA-256:`src-DbE1_mgM.js`(对话面板 chunk)`6c6ce63c93854783270e855572797d75e536e3b8d4548410e31fe9d147e1feb4`;`src-DPDj7_qD.js`(WB 原语)`747e41d215375e1214c366213aea7e077d85421923029c61cbb6e9562be58de0`;`src-CyXHA0IC.css` `a5e26b537bb50323aefbf25abddad52e288dd3868598be8d255d70886fe77cfe`;`src-CBaV_A3S.css` `2090ed21045c8f8b0fc388d76424c8751bf18fcd065cbbd5d0ef485163a2e36c`;`src-Bh3YZ35H.css` `88dbdc1cd3f59d0e8b75824512a465a2be714bc7af481d790fe3a9484f85e01a`;`contexts-BkjwYSZT.css` `50cbd673f7f35fc7f966bdddc6c9f5677fdfe944cb904888d214619fca259380`。
## 恢复范围
`scripts/rea/recover-composer-model-selector.mjs` 从固定导出抽取 **192 个正文定义**(含 UTF-16 区间与 SHA)与 **7 份源文件**,写出 `source.mjs` / `source.d.mts` / `source.css` / `manifest.json`,并对 3665 处标识符引用做未绑定审计(0 处未解析)。`--check` 幂等通过。
恢复体包含:`ModelSelectorImpl`、`ModelSelectPopover`、`ModelSubMenu`、`ModelItem`、`ModelGroupMenuItem`、`ModelTierBadge`、`ModelBadge`、`OverflowTitleSpan`、`ReasoningEffortPanel`、`ContextWindowPanel`、`collectModelOptions`、`resolveSelectedModelId/Label`、`buildGroupedModels`、`getDefaultIsThinking`、`isReasoningConfigurable`、`formatCredits`、`formatContextWindow`、`getSupportedContextWindows`、`createModelSelection`、`resolveModelIcon` / `getModelIconByName` 及 14 个厂商图标、所需 Menu/MenuGroup/MenuItem/SubMenu/Popover/Tooltip/ScrollArea/WB Switch 原语。
**6 处宿主接缝**(逐条记于 manifest `adaptations`,不是自造组件):输入框 store/selector、渲染诊断 sink、cr 配置 Context(从约 250 KB 各语言表裁到 `prefixCls/mode/locale/t`)、FloatingUI 走宿主固定版本 `@floating-ui/react 0.27.20`、DOMPurify 走宿主 `3.4.17`(原包内嵌 3.4.11,sanitize 配置逐项保留)、`FloatingPortal` 主题包装。
文案取自 `src-DbE1_mgM.js` UTF-16 offset 172945 的 zh-CN locale 字面量,26 条 `chatInput.modelSelector.*` 逐条解码。复核确认解码结果全为简体(`思考强度`/`开启`/`极致`),恢复器早先注释称其为繁体有误,已更正。
## 生产接线
新增 `src/renderer/src/workbuddy/composer/model-selector/`:
- `SourceModelSelector.tsx`:把 YCode `CustomModelProfile[]` 映射为源 provider 契约,并实现真实 `onSelect` / `onManage`。恢复体一次选择会同时调用 store 与 host `onChange`,适配层只让 store 做投影、由 `onChange` 执行写入,避免重复提交。
- `source-adapter.css`:唯一一处宿主适配。WorkBuddy 文档没有全局 `button` 字体重置,触发按钮沿用 UA 默认 Arial;YCode `app.css` 的 `button{font-family:inherit}` 会让标签继承 PingFang SC 并改变度量,因此用 `font-family:revert` 还原到 UA 默认,而不是写死某个平台字体。
- 类别图标不再强行套 provider 图标:不传 `icon`/`iconUrl` 时恢复体按模型名走品牌图标表(与截图一致)。
**推理强度子菜单已接线**:源实现在 `supportsReasoning` 时显示思考开关与强度档位。YCode 侧新增可持久化的会话级 `thinkingLevel`(`off|low|medium|high|xhigh|max`,见 `src/shared/workspaces.ts`),主进程经 `session:create` / `session:update-context` 透传,OMP 回合在 `setHostTools` 之后调用内核 RPC `set_thinking_level`(每回合应用,无需重启内核)。适配层把模型档案映射为源 provider 契约的 `supportsReasoning` / `onlyReasoning` / `canDisableThinking` / `defaultEffort` / `supportedEfforts`,源组件自身的开关与档位面板负责交互,写入由 `onChange` 上报后落到会话上下文。写入链路已端到端验证;`set_thinking_level` 到模型的真实推理档位变化属 OMP 内核行为,本轮只核到命令接线与 OMP 自身支持,未做真实回合的档位效果比对。
适配层对强度做逐项校验而非直接 `??` 回退:模型从未设置默认强度时档案里是 `defaultEffort: ''`,空串既不是合法档位、也会被主进程拒绝,因此只有落在五档白名单内的值才参与回退,最终回退顺序是「本次上报值 → 模型默认 → 模型支持档位首项 → `high`」。开启思考时源实现不携带强度(强度保存在它自己的按模型偏好里),此时回退到模型默认档位;若解析结果与当前持久化值相同则短路,重选已选中的行仍是空操作。
共享 cr 样式还依赖三条 shell 规则,已一并恢复并记录:`.wb-icon` 的 flex 基线、`.wb-home-composer__input-slot [class*=icon]` 的 50% 圆角、`.wb-home-composer__input-slot .cr-input-toolbar` 的 `--cr-text-primary` 桥接。前两条按后代关系落到 scope;第三条目标在 composer 工具栏(scope 的祖先),按 `\.cr-input-toolbar .yc-source-model-selector` 保留原元素关系。另修正 CSS 过滤器:此前漏掉裸 `.cr-menu-item__icon/__content/__extra`,导致 footer 图标没有 16px 盒与 8px 右边距。
`dompurify` 原为 mermaid 的传递依赖,已显式声明为 `3.4.17` 根依赖。`package.json`/`package-lock.json` 相应更新;`npm install` 在本仓库因既有 vite 8 与 electron-vite peer 冲突失败,属于既有问题,未用 `--force` 绕过。
## 验证
| 检查 | 结果 |
| --- | --- |
| 恢复幂等 | `node scripts/rea/recover-composer-model-selector.mjs --check` → PASS(7 sources / 192 bodies / 0 未解析) |
| 组件原生(隔离 Electron) | 73/73,`.tmp/model-selector-native-pTsrpd/report.json`:新增 1336×901/DPR1 打开态与 hover 态逐元素 bbox/计算样式采样,含 popover、菜单、滚动列表、全部模型行/图标/文本 Range、footer;截图为 `open.png` / `hovered.png` |
| 触发按钮 108 项计算属性 | 对 `ev_1c3853a9…` 原版节点逐项比对,**0 差异** |
| 真实应用 product flow 两阶段 | PASS,`.tmp/product-flows-0r0i1O/`:UI→preload→IPC→真实 OMP 创建/编辑模型、主界面选择与持久化、重启恢复 |
| 真实应用推理强度写入 | 三连切换往返一致(`off → max → off → max` 落库),`.tmp/verify-live-thinking-write.mjs`;切换中会话与草稿均未变 |
| 用户实窗 | 340×91 菜单、行高 32、选中 `gpt-6-luna`、Escape 关闭(150ms 退场后卸载)、会话与草稿未变 |
实窗检查发送 0 条消息、增删 0 个模型。进程替换后 PID 69300,原会话 `c261f03b-2efa-4816-b07f-9245f8f7e2c7` 恢复、草稿为空。
## 打开态逐属性核对(2026-10-16)
YCode 隔离 Electron fixture 已按原版视口 1336×901、`devicePixelRatio=1` 采样。每次运行会在 `.tmp/model-selector-native-*/report.json` 保存 `openState`、`hoverState` 的计算样式、元素框与文字 Range 框,并保存 `open.png` / `hovered.png`。这次报告是 `.tmp/model-selector-native-pTsrpd/`,73 项通过。恢复 CSS 与 token 的依据为 WorkBuddy 5.8.2 的 REA Evidence `ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94`;既有开态截图帧来自 `ev_1c3853a9a4dd6f99cf76d1874ad57af7ad6fb56c33e6b4e5c2a2e770f91f462e` 的 visual probe(帧 SHA-256 `ee80b6f2aee823de4ed48f7d7788ad01be059bdde981d51428c19fc3878da991`)。
本轮尝试对当前 WorkBuddy 窗口取新开态证据时,REA `list_electron_targets` 对 `http://127.0.0.1:9223` 返回 `provider_unavailable / endpoint_unreachable`;REA 原生窗口观察与场景操作返回 `target_unavailable`。因此没有在 WorkBuddy 中执行点击,也没有改变其窗口状态。现存 Evidence 的 viewport 为 1336×901,配套截图像素也为 1336×901(DPR 1 是据此推断);但它的最终 layout 在点击 footer 后采集,模型菜单已关闭,只含触发按钮的节点样式。下面的“原版”列将 REA 恢复的 CSS/token 与此前截图几何分开标记;它不代表本轮在原版打开态下取到了逐节点的运行时 computed style。
| 属性 | WorkBuddy 5.8.2 的 REA 来源 | YCode 打开态 fixture | 差异与判定 |
| --- | --- | --- | --- |
| viewport / DPR | 截图与 CSS viewport 1336×901;DPR 1 由两者像素尺寸相同推断 | 1336×901,`devicePixelRatio=1` | 对齐 |
| Popover 容器 / 菜单高度 | 既有开态截图记录菜单 `340×368px` | popover 与菜单实际 `340×193px`,`max-height:368px`,固定定位 `translate(981px, 265px)` | 实际内容高度不同;YCode 当前 4 个模型,没有补造行来凑高度 |
| 菜单宽度 / 圆角 / 内衬 | 恢复 CSS:340px / 16px / 8px | 340px / 16px / 8px | 对齐恢复样式 |
| 菜单底色 / 边框 / 阴影 | 恢复的 light token:白底、`#e6e6e6` 边框、`--wb-shadow-popover` 为黑色 4% 的两层阴影 | `rgb(255,255,255)`、1 device px 的 `rgb(230,230,230)`、相同的 4% 两层阴影 | YCode 计算结果符合恢复 token;原版 open-state computed 值仍待 REA 动态采集 |
| 滚动列表 / 行 | 恢复 CSS:列表 gap 2px;行高32px、圆角8px、左右内衬8px | 列表 322×134px、gap 2px;4 行均 322×32px、圆角8px、左右8px | 几何样式符合恢复 CSS;行数是目录数据差异 |
| 模型图标 / 标签 | 恢复 CSS:图标16px;标签14px/22px,当前项600字重 | 4 个图标均16×16px;标签均14px/22px,选中项600;文字 Range 高19px | 字体度量与恢复规则一致;每个文本宽度由本地模型名决定 |
| Footer | 恢复 CSS:32px 行高、圆角8px、14px/22px文字、16px图标和8px右边距 | 322×32px;16×16px图标、margin-right 8px;文本 Range 98×19px | 对齐恢复样式 |
| Hover | 恢复 CSS 使用 light `--cr-item-hover-bg`(`#f2f2f2`) | 悬停的选中思考模型行为 `rgb(242,242,242)` | YCode 与恢复 token 一致;当前 WorkBuddy 的真实 hover 样式尚未采到 |
这轮没有发现能由 YCode 样式修复的偏差,所以没有改动恢复 CSS 或宿主样式;新增的采样与断言用于锁住菜单表面、边框/阴影、内衬、文字度量、图标、footer 和 hover 色。与 WorkBuddy 的菜单高度、模型名称/图标宽度、渠道分组、徽标、倍率和繁忙状态差异来自目录数据,保持为数据差异,不填造模型元数据。要完成原版逐像素验收,还需要 REA 能访问当前 WorkBuddy renderer 的打开态与 hover 态,并在菜单打开时返回 `.cr-model-selector__popover`、`.cr-model-selector__menu`、`.cr-model-selector__list`、每个 `.cr-model-selector__item` 及其图标/文字、footer 的 bbox 与 computed style。
## 未完成
- **上下文窗口子菜单**:恢复体自身逻辑已就位(`getSupportedContextWindows` 过滤排序 + `ContextWindowPanel`),但它要求选项带 `supportedContextWindows` 且 ≥2 项才渲染该行;YCode 模型档案只有单个 `maxInputTokens`,属数据形态差异,单窗口模型在原版同样不出现该行。要真正提供可切换窗口,还需先决定自定义模型如何声明窗口集合,并确认 OMP 侧是否存在运行时切换通道(RPC 命令表里未见 context-window 设置项,ACP 侧是 `session/set_config_option`)。
- **推理强度未做像素验收**:思考/强度面板的结构与行为已对齐(开关、档位列表、当前项标记、回调写入),但没有对原版该面板做逐像素计算属性比对,与触发按钮那 108 项同级的证据尚缺。
- **分组与营销元数据**:YCode 模型目录是本地自定义模型,没有 WorkBuddy 的渠道分组、折扣/企业徽标、倍率与繁忙度;恢复体对这些字段的支持已就位,但当前目录不产生这些行。因此菜单行数与内容天然不同,这部分是数据差异而非样式差异。
- **原版打开态像素**:YCode 侧现已采集打开态和 row hover 态逐元素数据;当前 WorkBuddy 的 REA target 不可达,缺少原版菜单每个节点的实时 bbox/computed style 与 hover 帧,因此还未完成两侧逐像素比较。
- **暗色与动效全状态**:仅验证 marker 与 Escape 退场路径,未做全状态像素比对。
可复现:
```powershell
node scripts/rea/recover-composer-model-selector.mjs --check
node scripts/rea/test-model-selector-native.mjs
node scripts/verify-product-flows.cjs
```