91 lines
13 KiB
Markdown
91 lines
13 KiB
Markdown
# 对话区模型选择器:源码级恢复与验收(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
|
||
```
|