496 lines
73 KiB
Markdown
496 lines
73 KiB
Markdown
# WorkBuddy 复刻 TODO(2026-10-17)
|
||
|
||
本清单记录用户最初提出的 20 项问题及后续追加项,作为后续逐项拆解、实现和验收的工作入口。原始编号保持不变,新增需求顺延编号。本清单的建立与追加仅记录需求,不代表已完成对应采集或实现。
|
||
|
||
## 执行约定
|
||
|
||
- 查看 WorkBuddy 的源码、CSS、资源、页面及交互行为一律使用 REA。
|
||
- 已有内容沿用 WorkBuddy 5.8.2 的有效 REA 证据;此前未覆盖的内容再采集,并注明实际版本、Evidence ID、观察范围及限制。
|
||
- 每项依次记录:证据整理 → 差异与方案 → 实现 → 功能验证 → 视觉/交互验收。实现或构建通过不等于完成 1:1 验收。
|
||
- 只有全部验收点通过才勾选完成;记录待拆解的模块保持未完成状态。
|
||
- 优先保留用户会话、草稿、空间、模型配置和已有产物。回退、归档、删除应分别建模,不混用同一操作。
|
||
- 消息时间沿用已确认规则:当天 `HH:mm`;本年非当天 `M月D日 HH:mm`;非本年 `YYYY年M月D日 HH:mm`。显示消息时间,不显示实时钟表时间。
|
||
|
||
## 对话与侧栏
|
||
|
||
### 01. 任务操作菜单
|
||
|
||
- [x] 将侧栏对话条目后的直接删除按钮替换为原版任务操作入口。
|
||
- [x] 通过 REA 对齐菜单的入口、位置、顺序、图标、文案、悬停、关闭、禁用和确认状态。
|
||
- [x] 接入:置顶、取消置顶、重命名、独立窗口中打开、打开文件夹、归档、删除任务。
|
||
- [ ] 验收:置顶顺序和重命名持久化;独立窗口打开正确任务;文件夹指向正确工作目录;归档与删除行为独立;涉及运行中任务时有明确处理。
|
||
|
||
状态:主体已实现;运行中任务的处理与逐像素截图比对未完成。
|
||
|
||
原版证据:WorkBuddy 5.8.3,REA Evidence `ev_f5e9d71a676c453daab40928acfb33aeabae68ba57ffda342380cead7f1a1fe2`(任务菜单打开态),归档 `.tmp/rea-css-evidence/ev_f5e9d71a….json`。观察范围:任务列表首条任务的菜单容器、9 个条目、2 条分隔线、逐项 SVG 图标、几何与 computed style;未覆盖:菜单在窗口边缘翻转、运行中任务状态、分享/保存到工作空间/批量操作三类条目的行为。
|
||
|
||
原版菜单为 9 项:置顶(已置顶时为“取消置顶”)、重命名、分享任务、在独立窗口中打开、保存到工作空间、打开文件夹、批量操作、归档、删除任务。其中分享任务、保存到工作空间、批量操作依赖云端或尚未实现的能力,按“不渲染无效入口”的原则暂不列出,YCode 当前渲染 6 项。
|
||
|
||
原版实测数值:容器 170×352、`left = 按钮 left − 17`、`top = 按钮 bottom + 4`、padding 8、圆角 16、最小宽度 160;条目 32 高 / 14px / 22px 行高 / 圆角 8 / 图标 16×16;分隔线 1px、margin 6px 0;危险色 `#b42c3f`、普通文字 `rgba(0,0,0,0.9)`;卡片内“更多”按钮 16×16 且默认 `display:none`,悬停时替换时间显示。
|
||
|
||
实现:`Sidebar.tsx`(行结构、置顶排序、行内重命名、菜单挂载)、`task-menu.tsx`(菜单容器与条目)、`task-menu.css`(原版规则与深浅色取值)、`task-menu-icons.tsx`(逐项 SVG 原样复刻)、`stores/chat.ts`(setPinned / setArchived / renameSession)、`main/sessions.ts`(pinnedAt / archivedAt 持久化与排序)、`main/ipc.ts`(pin / archive / openFolder / openWindow)、`main/index.ts`(任务独立窗口)、`shared/types.ts`+`shared/ipc.ts`+`preload/index.ts`(契约)。
|
||
|
||
验证:`scripts/verify-task-menu.cjs` 在真实隔离 Electron(真实 preload / IPC / 文件系统)中 33 项断言全部通过,覆盖菜单文案与顺序、两条分隔线、逐项几何与配色、锚点偏移、遮罩关闭、置顶与取消置顶持久化、行内重命名持久化、归档保留会话与消息、删除真正移除、打开文件夹指向真实 cwd、独立窗口创建且携带 `window=task&taskId=…`。报告与截图:`.tmp/task-menu-Moulvm/`。`npm run typecheck`、`npm run test:conversation`、`npm run build` 通过;`npm run test:sessions` 在干净 HEAD 上同样报 `yaml` 动态 require 失败,与本次改动无关。
|
||
|
||
遗留:未覆盖运行中任务的置顶/归档/删除副作用与提示;未做原版逐像素截图比对;置顶标识图标取自同批归档的成员图标,原版置顶行的独立截图证据未采集。
|
||
|
||
### 02. 表格“添加到对话”及引用结果
|
||
|
||
- [x] 对齐表格上方“添加到对话”按钮的字体、图标、背景、边距、出现条件及交互。
|
||
- [x] 对齐点击后输入区内引用表格的呈现,包括结构、预览、移除和与已有草稿的组合方式。
|
||
- [x] 对齐发送后用户消息中的表格引用显示。
|
||
- [x] 验收:引用正确表格内容,不丢失已有草稿;输入区、发送结果及历史重载三种状态一致。
|
||
|
||
状态:主体已实现并做了真实窗口回归;仍有两处原版证据缺口(见“遗留”)。
|
||
|
||
原版证据:
|
||
- 渲染器实现:WorkBuddy 5.8.3 渲染器(REA 回收 `.tmp/wb-src-DbE1_mgM.js`)中的 `TableMessageQuoteAction`、`createTableMessageQuoteSourceId`、`getTableMessageQuoteMessageId`、`MessageQuoteAddIcon`、`MessageQuoteTextIcon`、`ChipCloseIcon`、`QuoteReferenceCard`、`InputBannerRail`、`MessageQuotePhraseTag`、`convertUserFrame`/`ContextChipsRenderer`。
|
||
- 菜单几何:REA Evidence `ev_38cca1ac66606ab07fb495035e3509f5c4d8ec3e5e2b74d0fdcfcfadb120da3b`(`layout.json` 中 `.cr-message-quote-table-menu` 116×38、按钮 106×28、`top:8px right:8px`、padding 4、圆角 8、条目 14px/22px/500、padding 3px 8px、gap 4、圆角 6、图标 16×16 两条 path、`rgba(0,0,0,.08) 0 4px 16px` 阴影)。
|
||
- 样式:REA 回收样式表 `src-CyXHA0IC.css`(SHA-256 `a5e26b537bb50323aefbf25abddad52e288dd3868598be8d255d70886fe77cfe`)40 条引用相关规则,含 `._tag_1krka_19` 及其 message-quote/`:readOnly` 变体、`._iconContainer_1krka_38`、`._icon_1krka_38`、`._text_1krka_66`、`.cr-message-quote-phrase-*`、`.cr-input-quote-card*`、`.cr-input-banner-rail`、`.cr-context-chips`、`.cr-user-content`。
|
||
- 观察范围:桌面端浅色主题、单条助手消息中的 markdown 表格、输入区空白草稿。未覆盖:原版实际点击后输入区卡片与消息气泡标签的截图/几何(见遗留)。
|
||
|
||
实现:
|
||
- `shared/conversation.ts` 新增 `MessageQuote`(id、kind、sourceId、messageId、markdown、title?、preview);`ChatMessage.quotes`、`SendPayload.quotes`、`ComposerSendPayload.quotes` 贯通消息与 IPC。
|
||
- `workbuddy/conversation/MessageQuoteIcons.tsx`:`MessageQuoteAddIcon`、`MessageQuoteTextIcon`、`ChipCloseIcon` 按回收 path 原样复刻。
|
||
- `workbuddy/conversation/TableMessageQuoteAction.tsx`:原版 DOM 结构(`div.cr-message-quote-table-menu` + `data-message-quote-table-added` + `data-message-quote-source-id`、按钮 `aria-label="添加到对话"`),sourceId 用原版 `createTableMessageQuoteSourceId` 组合规则。
|
||
- `workbuddy/conversation/message-quote-source.css`:菜单/按钮、已加入描边、引用标签(含只读态)、引用卡片、短语标签与 tooltip 规则,均照搬回收声明。
|
||
- `workbuddy/conversation/MessageQuoteCard.tsx`、`MessageQuoteTag.tsx`:输入区卡片与消息只读标签。
|
||
- `MarkdownContent`/`ReactMarkdown`:`renderTableAction` 改为输出原版组件,新增 `sourceId`(`${messageId}-text-${index}`)与 `quotedSourceIds`。
|
||
- `ComposerControls`:输入框上方 rail 渲染引用卡片,支持移除与点击定位;发送载荷带 `quotes`;只有引用时也可发送。
|
||
- `ChatPane`:草稿新增 `quotes`;`quoteTable` 改为挂引用而不是把 markdown 追加进正文;点击卡片回到来源表格并临时置 `data-message-quote-anchor-active`;编辑/重发/重新生成沿用历史引用。
|
||
- `MessageBubble`:用户消息内新增只读引用标签(`.cr-context-chips`)。
|
||
- `main/ipc.ts`:引用快照写入 `userMessage.quotes`,引用 markdown 随消息一并进入模型提示;空文本但有引用时允许发送。
|
||
|
||
验证:`scripts/verify-quote-table.cjs` 在真实隔离 Electron(真实 preload/IPC/渲染进程与文件系统)中 48 项断言全部通过,覆盖菜单/按钮几何与配色、`:focus-within` 显隐、已加入描边(`#40d1b3`)、输入区卡片(圆角 10 / padding 8 / gap 8 / 强调条 3px / 16×16 移除按钮 / rail `0 16px 8px` 与 gap 6)、草稿不被覆盖、移除后按钮恢复、`chat:send` 真实 IPC 载荷(正文+引用 markdown+sourceId)、发送后只读标签(22 高 / 圆角 6 / padding 2px 6px / 13px/14px/500 / 只读底色 / `data-content-block-meta-type=message-quote`)、历史重载一致。报告与截图:`.tmp/quote-table-SI0PNC/`(另有 `.tmp/quote-table-yiKDIs/`)。`npm run typecheck`、`npm run test:conversation`、`npm run build` 通过。
|
||
|
||
遗留:
|
||
1. 未取到原版“点击后输入区卡片”和“发送后消息标签”的实际截图与几何:探针在打开的会话里没有找到可引用的表格(`hover .cr-table-block` 返回 "Selected node is absent"),因此这两态是按渲染器源码与回收样式表实现,视觉未与原版逐像素比对。
|
||
2. 原版 `--cr-brand-secondary`(以及 `--cr-bg-brand-subtle`、`--cr-border-brand-subtle`)在回收样式表中只有使用、没有定义;同版本 `contexts-BkjwYSZT.css` 给出的兜底值是 `#40d1b3`,YCode 暂按该兜底值定义并已在注释中标注,待取到原版运行时 token 后替换。
|
||
3. 输入区引用卡片中的“来源标题”在原版由宿主(`message.quote.add` 处理端)生成,尚未采集;YCode 当前不显示标题,只显示内容摘要,不臆造文案。
|
||
|
||
### 03. 编辑已发送消息
|
||
|
||
- [x] 拆解原版编辑入口、编辑容器、文本排版、按钮、焦点与键盘交互。
|
||
- [x] 对齐进入编辑、修改、取消、提交及失败状态。
|
||
- [x] 验收:编辑正确消息;取消恢复原内容;提交后的消息分支与产物处理符合已确认行为。
|
||
|
||
状态:主体已实现,并通过真实窗口回归与真实 IPC 回归;工具条淡入/提示出现时序未逐帧采集。
|
||
|
||
原版证据:
|
||
- 渲染器实现(REA 回收 `.tmp/wb-src-DbE1_mgM.js`):`UserMessageEditProvider`、`UserMessageEditor`/`UserMessageEditorContent`(校验规则 `empty`/`too-long`,上限 `1e4`;`message.edit.submit`;错误码映射 `userMessage.edit.error.retryLater`;`RenderNoHandlerError` → `sendFailed`;外部 mousedown 关闭规则与豁免选择器)、`ToolbarRenderer`(`cr-toolbar` = 时间 + 复制 + 恢复此版本 + 编辑;编辑中或已丢弃时不渲染)、`IconButton`、`RevertToBeforeIcon`、`LoadingSpinnerIcon`、`formatMessageTime`。
|
||
- 样式:REA 回收样式表 `src-CyXHA0IC.css`(SHA-256 `a5e26b537bb50323aefbf25abddad52e288dd3868598be8d255d70886fe77cfe`)中 21 条 `.cr-user-message-editor*` / `.cr-self-bubble--editing` 规则与 13 条 `.cr-toolbar*` 规则。
|
||
- 文案:REA Evidence `ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb` 中 `userMessage.edit.*`、`userMessage.action.*`、`markdown.copy`/`markdown.copied` 的 zh-CN 与 en-US。
|
||
- 运行时几何与样式:`ev_245ea1740cde21869500348e47cf9663a1ea72943ccfe79c163a6838a153bfd3`(工具条 97.23×28、时间 33.23×20、复制/编辑按钮 24×24、图标 14×14;无产物会话不显示恢复按钮;编辑态气泡 535×110、编辑器 511×94、输入容器 50、页脚 44、提示 369×18、取消 62×32、发送 60×30)、`ev_5dd78e06e75b3a29b656d328c2efc2a820ea197e8204a39840207188a6496a0f`(编辑器保持打开)、`ev_ee345b160e5f520ed866c1059b7c72e77b34e49bf1ce407cd266ea886aa86405`(编辑态布局快照与计算样式:容器透明/无边框/无阴影/`min-height:50px`、宿主 `min-height:64px`、可编辑区 15px/1.75、提示 13/18 `rgba(0,0,0,.5)`、取消边框 `1px rgb(235,235,235)`、发送 `rgb(26,26,26)`/白字、InfoIcon path)、`ev_308764954de03fa28e4a980d609d33eabf794c44ce6733bfe1b83cec36ef562a`(取消关闭并恢复原气泡)。
|
||
- 观察范围:桌面浅色主题、单条用户消息;覆盖无产物写入会话(工具条无恢复入口)与含 `write_file` 记录会话(出现恢复入口)。未覆盖:工具条淡入与 tooltip 出现的逐帧时序、未发送/被丢弃消息的“恢复到此版本”状态。
|
||
|
||
实现:
|
||
- `workbuddy/conversation/UserMessageEditor.tsx`:就地编辑器,原版 DOM(`cr-user-message-editor` > `cr-input-container-wrapper`/`cr-input-container`/`cr-input-editor-host` + `[data-slate-editor]` 可编辑区、`__error`、`__footer`/`__hint`/`__hint-icon`/`__actions`/`__cancel`/`__send`);空内容与 10000 字符校验、Enter 提交(Shift+Enter 换行)、提交中禁用按钮并显示 spinner 与 `aria-busy`、失败文案按 `error.retryLater`/`sendFailed` 映射、外部 mousedown 仅在没有改动且未提交时取消。
|
||
- `workbuddy/conversation/MessageEditorIcons.tsx`:`RevertToBeforeIcon`、`EditorInfoIcon`、`EditorSpinnerIcon` 按回收源码与运行时 `svg_nodes` 原样复刻。
|
||
- `workbuddy/conversation/message-edit-source.css`:补齐 18 条缺失回收规则(含编辑容器 `min-height:50px` 与 `:focus-within` 去阴影以覆盖宿主适配规则);`.cr-self-bubble--editing` 与 `:has(.cr-user-message-editor)` 复用 `source-styles.css` 已有声明。
|
||
- `workbuddy/message-actions/UserMessageToolbar.tsx`:`div.cr-toolbar[data-message-id][role=toolbar]`,时间按三档格式显示;复制按钮 2000ms 内切换“已复制”且 tooltip 不因点击隐藏;恢复此版本仅在存在可回退产物时渲染并透传禁用态;编辑按钮受会话忙状态约束;图标按钮为 `button.cr-icon-button[data-action][aria-label]` + `svg.cr-icon-button__icon`。
|
||
- `workbuddy/message-actions/message-copy.ts`:抽出用户消息复制回退链,旧 `MessageActionToolbar` 复用同一实现。
|
||
- `components/MessageBubble.tsx` / `components/ChatPane.tsx`:新增 `editingMessageId` 与 `startEdit`/`cancelEdit`/`submitEdit`;编辑态用编辑器替换气泡内容并卸载工具条;提交走 `send(..., { fromMessageId, action:'edit' })`;移除底部输入框“编辑消息”横幅与用户消息的“重新发送”入口。
|
||
- `main/workspace-revert.ts` 新增 `rollbackEditedArtifacts`;`main/ipc.ts` 的 `chat:send` 在 `action==='edit'` 时先回退产物(无写入记录则跳过;记录不完整则拒绝提交),原生分支失败时撤销文件回退。
|
||
|
||
验证:`node scripts/verify-user-message-edit.cjs` 在真实隔离 Electron(真实 preload/IPC/渲染进程,`chat:send` 桩记录载荷)中 50 项断言全部通过,覆盖工具条几何/配色/时间格式/按钮集合与图标(copy/revert/edit、14×14、viewBox 16/14)、悬停显隐、进入编辑、编辑器几何与排版(容器 50/宿主 64/可编辑 15px·1.75/提示与按钮尺寸)、取消恢复原内容、有改动时外部 mousedown 不关闭、空内容校验阻断提交、提交中禁用 + spinner + `aria-busy`、`action:'edit'` + `fromMessageId` 真实 IPC 载荷、Enter 提交、无渲染器报错;报告与截图:`.tmp/edit-O1kT3A/`(早期 `.tmp/edit-2MoqPF/`、`.tmp/edit-624Sw3/`)。`npm run test:conversation-ipc` 新增 `testEditArtifactRollback`,覆盖真实主进程语义:产物回退到消息前版本、历史前缀保留并追加替换消息、原生分支失败时撤销文件回退且会话不变、无产物改动可直接编辑、含不可回滚工具时拒绝编辑且文件与会话不变。`npm run typecheck`、`npm run test:conversation`、`npm run test:recovered`、`npm run test:chat`、`npm run build` 均通过。
|
||
|
||
遗留:
|
||
1. 工具条淡入(`opacity .2s`,hover 后 `transition-delay:0s`)与 tooltip 出现时序未逐帧采集,当前按回收 CSS 声明实现,未做像素级时序比对。
|
||
2. 原版提交失败的错误码集合(`resend-settle-timeout`、`cancel-barrier`、`send-in-progress` 等)仅有映射表证据;YCode 目前只区分“上一轮未结束”和“其它失败”两类文案。
|
||
3. 原版 `userMessage.edit.resume`(恢复到此版本)依赖未发送/被丢弃消息状态,YCode 暂无对应状态,未渲染该按钮(DOM 结构与 CSS 规则已保留)。
|
||
4. 原版 `formatMessageTime` 含“昨天 HH:mm”分支,与此前用户确认的三档格式不同;当前沿用用户确认的三档规则,差异待确认。
|
||
|
||
### 04. 用户消息悬停时间
|
||
|
||
- [x] 为已发送用户消息补齐悬停显示时间。
|
||
- [x] 对齐出现位置、间距、字体、颜色和与操作图标的关系。
|
||
- [x] 复用三档时间格式;验证当天、同年异日、跨年、历史重载。
|
||
|
||
状态:随第 03 项的工具条一并实现并通过真实窗口回归;原版“昨天”分支差异待用户确认。
|
||
|
||
原版证据:
|
||
- 源码(REA 回收 `.tmp/wb-src-DbE1_mgM.js`):`ToolbarRenderer` 的 `span.cr-toolbar__time` 取 `formatMessageTime(props.createTime, locale, t('message.time.yesterday'))`;工具条整体由 `.cr-frame--right:hover` 控制显隐。
|
||
- 样式:回收表 `src-CyXHA0IC.css` 中 `.cr-toolbar__time{width:max-content;color:var(--cr-text-tertiary,#00000080);white-space:nowrap;font-size:14px;font-weight:400;line-height:20px}` 与 `.cr-toolbar{gap:8px;padding-top:4px;transition:opacity .2s ease-in-out .15s}`、`.cr-frame--right:hover .cr-toolbar{opacity:1;pointer-events:auto;transition-delay:0s}`。
|
||
- 运行时:`ev_245ea1740cde21869500348e47cf9663a1ea72943ccfe79c163a6838a153bfd3`(时间 33.23×20,位于复制按钮左侧,工具条 gap 8)、`ev_ee345b160e5f520ed866c1059b7c72e77b34e49bf1ce407cd266ea886aa86405`(时间计算样式 14px/20px `rgba(0,0,0,0.5)`)。
|
||
- 观察范围:桌面浅色主题、当天消息;同年/跨年分支来自源码 `formatMessageTime`,原版“昨天”分支未采用(以用户此前确认的三档格式为准)。
|
||
|
||
实现:`workbuddy/message-actions/UserMessageToolbar.tsx` 渲染 `span.cr-toolbar__time` 并复用共享 `formatMessageTime`(`components/AssistantMessageDetails.tsx`):当天 `HH:mm`、同年异日 `M月D日 HH:mm`、跨年 `YYYY年M月D日 HH:mm`;位置、间距、字体、颜色与“时间在图标左侧”的关系直接由回收的 `.cr-toolbar` / `.cr-toolbar__time` 规则提供。
|
||
|
||
验证:`node scripts/verify-user-message-edit.cjs` 57 项断言全 PASS,其中包含时间字号/行高/颜色、当天 `06:32`(HH:mm)、同年异日 `1月2日 09:05`、跨年 `2024年12月31日 09:05`、切换会话后只显示当前会话的时间、历史重载后三档时间保持一致;`npm run test:assistant-footer` 覆盖 `formatMessageTime` 的三档与无效输入。报告:`.tmp/edit-iAc4zi/`。
|
||
|
||
遗留:
|
||
1. 原版 `formatMessageTime` 含“昨天 HH:mm”分支,与已确认的三档规则不同;当前按用户确认的三档渲染,差异待确认。
|
||
2. 工具条淡入的延迟切换(默认 `.15s` 延迟、hover 时 `0s`)未逐帧采集。
|
||
|
||
### 05. 用户消息回退操作与图标
|
||
|
||
- [x] 移除已发送用户消息上的“重新发送”按钮。
|
||
- [x] 替换为“将对话及产物回退到此消息前”操作,图标、提示及交互通过 REA 对照。
|
||
- [x] 明确回退边界:该消息之前,而非该消息之后;对话记录与对应产物修改一起处理。
|
||
- [x] 验收:多轮对话、文件创建/修改、无产物、运行中禁用、取消确认、回退失败和重启后状态;不得只删除可见消息而保留应回退的文件修改。
|
||
|
||
状态:主体已实现,并通过真实窗口与真实 IPC 回归;弹窗逐像素几何、动画与失败 toast 尚未与原版比对。
|
||
|
||
原版证据:
|
||
- 桌面端接线(REA Evidence `ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb`,脚本 SHA-256 `5f35b7489b107e21ef452c8ee88a95265756f6fc444841eef869228bbc44768b`,item 295):`ConversationTimelineImpl` 组装 `userMessage.actions = { showDefaultRevert: !isSessionActive && transport !== 'cloud', revertLabel: t('rewind.entry.tooltip'), revertEntersEdit: false, hasRevision: e => resolveRevertEntryVisible({ requestId: e.requestId, firstLoadedUserRequestId, hasMoreRequests }), onAction }`;`onAction('revert')` 先校验 `resolveRevertEntryVisible`,再 `M.confirm('rollback', requestId)`,仅当结果不是 `cancel` 才执行 `M.runRewind(requestId)`;`resolveRevertEntryVisible = requestId && (hasMoreRequests || !firstLoadedUserRequestId || requestId !== firstLoadedUserRequestId)`。
|
||
- 文案(REA Evidence `ev_e91787b1d4dea651582c6cf12827775b9068b0ee8b80c68ba08d4c1d87d94e90`,同一脚本):`rewind.entry.tooltip`=将对话及产物回退到此消息前、`rewind.dialog.plain.title/desc`、`rewind.dialog.artifacts.title`=将对话及以下产物版本回退到此消息前?、`rewind.dialog.artifacts.desc`=回退暂不支持部分类型文件、`rewind.dialog.noVersion.title/desc`、`rewind.item.restoreAt/restoreUnknown/willDelete/noEarlierVersion`、`rewind.action.cancel/rewind/skipRewind`、`rewind.error.rollbackFailed`=对话回退失败,请稍后重试。
|
||
- 工具条运行时几何:`ev_5820293fcc4fdf5acdfad597ae816ac49e0c1b7992e3a024cf75aa9968fe1c84`(3 个 24×24 按钮 x=1093/1125/1157;回退图标 14×14 `viewBox 0 0 14 14`、两条填充 path,与 `RevertToBeforeIcon` 一致)、`ev_245ea1740cde21869500348e47cf9663a1ea72943ccfe79c163a6838a153bfd3`(无产物写入会话只有 copy + edit,含 `write_file` 记录的会话出现第三个按钮)。
|
||
- 另一表面(云端/远程会话的 `useUserMessageActions`)使用 `userMessageActions.revertToThisVersion`(“回退到此版本”)并打开 checkpoint 弹窗;本地桌面任务会话(YCode 目标表面)使用上面的 rewind 文案与流程,本项按本地表面复刻。
|
||
- 观察范围:桌面端浅色主题、本地传输会话、含 `write_file` 记录的会话与只读会话。未覆盖:云端 checkpoint 弹窗、`rewind.dialog.artifacts` 逐产物 fallback 状态(`no-earlier-version`、`versionDeleted` 等)的真实数据形态、弹窗逐像素几何与动画。
|
||
|
||
实现:
|
||
- `workbuddy/conversation/rewind-labels.ts`:按 zh-CN 逐字回收 `rewind.*` 文案(含 `{time}` 占位替换),并记录两条 Evidence。
|
||
- `workbuddy/message-actions/UserMessageToolbar.tsx`:回退按钮的 aria-label/tooltip 由库默认 `userMessage.action.revert`(恢复此版本)改为桌面端 `revertLabel` 的 `rewind.entry.tooltip`。
|
||
- `components/ChatPane.tsx`:新增 `rewindEntryVisible`(`requestId` 存在、非首条用户消息、会话不在运行/发送中)与 `rewindPreview`(`artifacts`/`no-version`/`plain` 三变体);回退弹窗按变体渲染标题与说明,产物清单显示文件名、完整路径 tooltip 与状态(将被删除 / 回退至历史版本),按钮为“取消/回退”,提交中禁用并置 `aria-busy`;取消不触发任何 IPC。
|
||
- `workbuddy/message-actions/MessageActionToolbar.tsx`:删除用户消息的编辑/恢复/重新发送死分支,用户消息操作只由 `UserMessageToolbar` 承载,界面上不再可能出现“重新发送”。
|
||
- `main/ipc.ts`:`chat:revert` 允许无产物版本的“仅回退对话”(对应原版 no-version 变体,产物保持不变),并在消息不存在时明确报错;有产物时仍先回退文件、失败时撤销并保留会话。
|
||
- `styles/app.css`:`.rewind-artifacts` 产物清单样式(`max-height:200px`、13px/20px 行、名称省略与完整路径 tooltip、状态右对齐)。
|
||
|
||
验证:`node scripts/verify-user-message-edit.cjs` 在真实隔离 Electron(真实 preload/IPC/渲染进程)中 67 项断言全 PASS,覆盖按钮集合与标签(复制 / 将对话及产物回退到此消息前 / 编辑)、首条用户消息隐藏回退入口、其余消息显示、会话活动期间回退与编辑入口一并隐藏、弹窗 `artifacts` 变体标题/描述/按钮/产物行(`edited-artifact.txt` · 回退至历史版本)、取消不触发 IPC、确认以 `sessionId` + `messageId` 触发 `chat:revert` 并按回退结果替换消息前缀;报告与截图:`.tmp/edit-kYHO92/report.json`、`.tmp/edit-kYHO92/rewind-dialog.png`。`npm run test:conversation-ipc` 改写 read-only 用例为“无产物版本回退仅回退对话且历史持久化”,连同产物回退、外部修改冲突、原生分支失败撤销、运行中拒绝等主进程语义用例全部通过。`npm run typecheck`、`npm run test:conversation`、`npm run test:recovered`、`npm run test:chat`、`npm run test:assistant-footer`、`npm run build` 均通过。
|
||
|
||
遗留:
|
||
1. 回退弹窗沿用 YCode 现有 modal 样式,未采集原版弹窗(宿主 Modal:标题/正文/产物清单/底部按钮)的几何与动画;待后续按原版逐像素对齐。
|
||
2. 原版失败路径以 toast 呈现 `rewind.error.rollbackFailed`(对话回退失败,请稍后重试。);YCode 当前在弹窗内联展示原始错误文本。
|
||
3. 原版逐产物 fallback 状态(回退至 {time} 版本 / 历史版本已被删除,将保持当前版本 / 该产物无历史版本,回退后文件仍会保留在目录中)依赖服务端产物版本历史;YCode 只有工具写入前后快照,暂映射“将被删除 / 回退至历史版本”。
|
||
4. 原版编辑提交时会先弹 `rewind.edit.*` 询问是否回退产物历史版本(回退 / 不回退),YCode 编辑提交固定回退产物(`rollbackEditedArtifacts`);差异记入 TODO 03 追补。
|
||
5. 原版在 `hasMoreRequests`(分页历史未全部加载)时首条已加载消息也可显示回退入口;YCode 全量加载历史,按 `requestId !== 首条用户消息 requestId` 判定。
|
||
|
||
### 06. 对话区域滚动条
|
||
|
||
- [x] 对齐宽度、颜色、轨道、圆角、悬停显隐、与内容边缘的间距。
|
||
- [x] 检查滚动条占位、长内容、流式跟随底部及用户手动上滚的行为。
|
||
- [x] 验收:浅色、深色、短对话、长对话、流式输出与不同窗口尺寸。
|
||
|
||
状态:已对齐并通过真实窗口回归(像素级比对 + CSSOM + 计算值 + 行为);本轮无需改动实现代码,回收样式表已在位。
|
||
|
||
原版证据:
|
||
- 样式表 `src-CyXHA0IC.css`(SHA-256 `a5e26b537bb50323aefbf25abddad52e288dd3868598be8d255d70886fe77cfe`):`.cr-message-list{scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--cr-scrollbar-thumb) transparent;flex:1;min-height:0;overflow-y:auto}`;`::-webkit-scrollbar{width:6px;height:6px}`、`::-webkit-scrollbar-track{background:0 0}`、`::-webkit-scrollbar-thumb{background-color:var(--cr-scrollbar-thumb);border-radius:3px;transition:background-color .2s}`、`::-webkit-scrollbar-thumb:hover{background-color:var(--cr-scrollbar-thumb-hover)}`。
|
||
- token 链:`--cr-scrollbar-thumb: var(--cr-internal-scrollbar-thumb)`;浅色 `#0003` / `#00000052`,深色 `#fff3` / `#ffffff52`。
|
||
- 空闲隐藏由库选项 `hideScrollbarWhenIdle`(默认 `false`)驱动 `useScrollIdleVisibility`(3s 无滚动加 `cr-message-list--scrollbar-idle`,滚动时移除);WorkBuddy 应用包(脚本 SHA-256 `5f35b748…` 的 item 295)未传该选项,运行时截图也没有该 class,故原版不启用空闲隐藏,YCode 同样不实现。
|
||
- 运行时光栅证据:REA Evidence `ev_5820293fcc4fdf5acdfad597ae816ac49e0c1b7992e3a024cf75aa9968fe1c84` 的 `workbuddy.png`:滑块占据 x=1328..1333(6px),颜色 `rgb(204,204,204)`(即 `#0003` 叠白),轨道透明,槽位右侧留 2px 空白。
|
||
- 观察范围:浅色主题、1336×1240 视口、长会话。未覆盖:原版 hover 态与深色主题的运行时像素、原版槽位宽度的直接测量(只能由像素推断)、不同窗口尺寸下的原版像素。
|
||
|
||
实现:无代码改动——`ChatPane` 的滚动容器为 `.chat-scroll.cr-message-list`,回收样式表 `workbuddy/conversation/source-styles.css` 已包含上述全部规则;`flex:1/min-height:0/overflow-y:auto` 与自适应宿主一致。本项补齐可复跑的验收脚本。
|
||
|
||
验证:新增 `scripts/verify-conversation-scrollbar.cjs`(真实隔离 Electron + 真实 preload/IPC/渲染进程),24 项断言全 PASS:
|
||
- 样式契约:`scrollbar-width:thin`、`scrollbar-gutter:stable`、`overflow-y:auto`、实际槽位 10px;CSSOM 中 6px 宽度、透明轨道、`var(--cr-scrollbar-thumb)` + 3px 圆角 + `.2s` 过渡、hover 使用 `--cr-scrollbar-thumb-hover` 均按回收声明存在。
|
||
- 计算值:浅色 `rgba(0, 0, 0, 0.2) rgba(0, 0, 0, 0)`、深色 `rgba(255, 255, 255, 0.2) rgba(0, 0, 0, 0)`(与 `#0003`/`#fff3` 一致)。
|
||
- 像素:滑块 6 列 x=1328..1333、`204,204,204` 合成色、轨道列(1324..1327、1334..1335)保持纯白——与原版测量列完全一致。
|
||
- 行为:长会话加载即贴底;流式增量期间保持贴底;用户上滚后不再强制跟随(偏差 < -80px);回到底部后恢复跟随;1100×700 与 1600×1000 两种窗口尺寸下槽位与轨道契约不变;短对话不溢出且仍预留稳定槽位;空闲 3.4s 不出现空闲隐藏 class;无渲染器报错。
|
||
- 报告与截图:`.tmp/scrollbar-fpB0Vi/report.json`、`.tmp/scrollbar-fpB0Vi/scrollbar-early.png`。
|
||
|
||
遗留:
|
||
1. 原版 hover 态滑块颜色(`#00000052` / `#ffffff52`)与深色主题未采集运行时像素,当前以回收声明与计算值对齐。
|
||
2. 原版槽位宽度无法直接从 layout 证据测得(内容列受 `max-width` 约束),只能由像素推断;YCode 实测槽位 10px、可见滑块位置与原版一致。
|
||
3. 原版滚动条附近的其他 affordance(如“回到底部”控件)未纳入本项回归。
|
||
|
||
### 07. 侧栏最下一行
|
||
|
||
- [x] 移除最下方不应存在的“收起菜单”入口。
|
||
- [x] 对齐最下一行的布局、高度、内边距、字体、头像、当前图标与悬停状态。
|
||
- [x] 验收:普通/窄侧栏、浅色/深色下均无残留入口或空白占位。
|
||
|
||
状态:已对齐并通过真实窗口回归(运行时几何 + 计算值 + hover 目标色);收起入口只在顶栏保留一处。
|
||
|
||
原版证据:
|
||
- 运行时布局 REA Evidence `ev_721d830c97114930e046ec8931bd2f473a11ab23efcab9c727c4256889e29879`:`.conversation-list` 宽 284.28(含 border-right 1px);`.conversation-list-footer` `[0,845,283.28,57]`、padding 8/12/8/8、border-top 1px `rgb(230,230,230)`、display flex + align-items center + justify-content flex-start;`.user-menu-trigger-wrapper` `[8,854,263.28,40]`、gap 4;`.user-menu-trigger--workbuddy` `[8,854,191.28,40]`、min-height 40 / padding 6px 8px / radius 8;`.user-menu-trigger-left` gap 10;头像 28×28 圆形;姓名 12px/600/12px;`.msg-center-bell-btn` 与 `.user-menu-trigger-miniprogram` 各 32×32 / radius 6。
|
||
- 回收样式表 `contexts-BkjwYSZT.css`(`.conversation-list-footer{...}`、`.conversation-list-footer .user-menu{width:100%}`)与 `UserMenu-CpHz-0rF.css`(`.user-menu-trigger*`、`.user-menu-trigger--workbuddy:hover{background-color:var(--wb-bg-hover)}`、`.msg-center-bell-btn:hover{background-color:var(--wb-button-ghost-bg-hover);color:var(--wb-icon-primary)}`、`.msg-center-badge{...}`、`.user-menu-avatar-initial{background-color:#0056d4}`)。
|
||
- 同一证据中侧栏收起源位于顶栏 `.conversation-list-topbar-actions`(`[8,3,76,24]`,3 个 24×24,x=8/34/60);底部该行只有账号 / 通知 / 小程序三个入口。
|
||
- 观察范围:桌面端浅色主题、zoom=1、1336 宽窗口。未覆盖:深色主题运行时像素、原版真实头像图片。
|
||
|
||
实现:
|
||
- `components/Sidebar.tsx`:底部账号区(`.sidebar__footer.conversation-list-footer`)只保留账号触发器、通知铃铛、小程序三个入口;删除底部“收起侧栏”按钮及随之无引用的 `IconCollapseRail`。
|
||
- `styles/sidebar.css`:逐条落地回收规则(footer padding/边框/flex、wrapper gap/宽度、触发器 min-height/padding/radius/`background-color .2s`、左组 gap 10、头像 28×28 圆形 + `#0056d4` 字面头像、姓名 12/600/12 省略号、铃铛与小程序 32×32/radius 6/`background-color, color .15s`、角标 14×14/radius 7/top 2/right 2/translate(40%,-30%));hover 直接使用回收 token `--wb-bg-hover`、`--wb-button-ghost-bg-hover`、`--wb-icon-primary`。
|
||
- `workbuddy/workbench/source-panel-layout.ts`:展开态侧栏宽度 280 → 284,使整列与 footer 边界对齐原版 284.28(实测 footer `[0,845,283,57]`、wrapper `[8,854,263,40]`、触发器 191×40)。
|
||
- 顶栏既有“收起侧边栏”入口(24×24)保持不变;折叠后由 `.sidebar__rail` 的“展开侧栏”恢复。
|
||
|
||
验证:新增 `scripts/verify-sidebar-footer.cjs`(真实隔离 Electron + 真实 preload/IPC/渲染进程)29 项断言全 PASS:
|
||
- 布局:footer padding 8/12/8/8、flex 契约、border-top 1px `rgb(230,230,230)`、高 57;wrapper gap 4 且子元素恰为账号 / 通知 / 小程序;footer 内不存在任何“收起”入口。
|
||
- 组件:账号触发器 min-height 40 / padding 6px 8px / radius 8 / 过渡 `background-color .2s`、宽 191;左组 gap 10;头像 28×28 圆形 `rgb(0,86,212)`;姓名 12/600/12 + 省略号;铃铛 32×32 / radius 6 / `rgba(0,0,0,.7)` / `.15s`;小程序 32×32 / radius 6;角标几何与配色(`rgb(246,64,65)`、top/right/translate)。
|
||
- hover:先留档“过渡冻结在起始值”现象(`playState: running`、`currentTime: 0`,QA 窗口无合成帧),随后关闭过渡读取目标色——铃铛 hover 背景等于 `--wb-button-ghost-bg-hover` 计算值 `rgba(0,0,0,0.06)`、图标色等于 `--wb-icon-primary` `rgba(0,0,0,0.9)`;账号触发器 hover 背景等于 `--wb-bg-hover` 计算值 `color(srgb 0 0 0 / 0.05)`。
|
||
- 收起入口:顶栏 24×24 按钮仍可折叠侧栏,折叠轨道保留“展开侧栏”。
|
||
- 回归:侧栏宽度变更后 `node scripts/verify-conversation-scrollbar.cjs`、`node scripts/verify-user-message-edit.cjs` 仍全 PASS;`npm run typecheck`、`npm run build` 通过。
|
||
- 报告与截图:`.tmp/sidebar-footer-3edCU8/report.json`、`.tmp/sidebar-footer-3edCU8/sidebar-footer.png`。
|
||
|
||
遗留:
|
||
1. 侧栏宽度取整数 284,与原版运行时 284.28 存在 0.28px 分形残差(四舍五入后可见差异为 0)。
|
||
2. 原版头像为真实图片(`.user-menu-trigger-avatar-img:after` 还有依赖 `--wb-avatar-fallback-*` 的 1px 内描边);YCode 无账号体系,用 `#0056d4` 字面初始头像且未接内描边。
|
||
3. 深色主题底部行未采集运行时像素,仅按回收 token(white-6% / white-10%)对齐。
|
||
4. 通知角标数据与小程序跳转为 mock(属 TODO 09 与待拆解项),本项只对齐入口与样式。
|
||
|
||
### 08. 新建任务发送后的立即切页与占位
|
||
|
||
- [x] 盘点发送到创建会话、准备环境、接收首个事件之间的阻塞位置。
|
||
- [x] 对照原版发送后的页面切换时机与等待状态。
|
||
- [x] 发送后立即进入消息页面并显示用户消息;启动较慢时显示合适的助手占位/准备状态。
|
||
- [x] 验收:慢启动、正常启动、发送失败、停止、切换任务;不等到模型输出后才切页,不重复创建会话或丢失草稿。
|
||
|
||
状态:已实现并通过真实窗口回归(发送即切页 + 乐观用户消息 + 启动占位 + 真实回合替换 + 失败/停止/切换任务)。
|
||
|
||
阻塞位置盘点(YCode 现状):
|
||
- 渲染进程 `stores/chat.ts` 的 `send()` 在无会话时依次 `await session.create` → `await chat.send`,旧实现只有两步都完成后才把用户消息与助手占位写入 `messages`,因此「点了发送什么也不动」直到回合就绪。
|
||
- 主进程 `chat:send` 在返回前要完成:附件/上下文准备、`createCapabilitySnapshot`、`getOmpKernel`(运行环境启动,本轮实测约 1.9–2.3 s)、`sessions.startTurn`;模型回合本身在后台推进,不属于返回前的阻塞。
|
||
|
||
原版证据:
|
||
- 启动阶段文案:REA `ev_717fa5e846e64d67ac19c8925d121bff632c0d3aefb3603b10858559cabb2dcb`(WorkBuddy 5.8.2,`contexts-BMeWSUHk.js`,SHA-256 `5f35b7489b107e21ef452c8ee88a95265756f6fc444841eef869228bbc44768b`):`chat.prepare.<stage>` 表与显示逻辑见 `docs/对话启动提示文案-2026-10-16.md`——阶段文案渲染在流式状态栏主文案位置,进入终态/失败/非 idle/MCP 进度后隐藏,其余回落「思考中」。本次占位使用 `acquiring_runtime` =「正在启动对话运行环境…」(用户确认沿用 5.8.2 已有内容,不重新采集)。
|
||
- REA 采集限制(已核到实现层):visual probe 的 `text` 动作要求 enabled text/search `INPUT`,`key` 动作对可编辑内容上的 Enter/Space 直接拒绝(`.tmp/rea-css-runtime/dist/browser/CdpElectronVisualProbe.js`;schema `.tmp/rea-css-runtime/dist/domain/javascript/ElectronVisualProbeSchema.js`)。WorkBuddy 新建任务编辑器是 `DIV[role=textbox]._editable_1cxd2_1`,因此无法用 REA 驱动发送并抓取「发送后」逐帧等待态;本项不通过其它通道查看/驱动原版。三次采集(`ev_a492a83d…`、`ev_150e8217…`、`ev_5b13ec7d…`)与限制说明见 `.tmp/todo08-wb-new-task/notes.md`。
|
||
- 观察范围:桌面端浅色主题、本机 5.8.3 运行时(文案表来自 5.8.2 已回收证据)。未覆盖:原版发送后逐帧顺序与各段时长。
|
||
|
||
实现:
|
||
- `stores/chat.ts`:无会话发送时先落地乐观的「用户消息 + 助手占位」(`local-user-*` / `local-assistant-*`,占位带 `prepareStage: acquiring_runtime` 与 `turnStartedAt`),随后才 `await session.create` / `chat.send`;`newSession(context, seedMessages)` 允许把乐观消息带进新会话;真实结果到达时先过滤 `local-*` 再追加真实用户消息与占位,避免重复;失败时移除乐观消息并写入 `error`。
|
||
- `components/ChatPane.tsx`:`submit()` 在发送前清空输入框,失败时把草稿恢复到当前所有者键(会话创建后草稿已迁移到会话 id);消息非空即离开首页布局(`isHome = currentId === null || messages.length === 0`)。
|
||
- `workbuddy/conversation/prepare-phases.ts`(新增)与 `StreamingFooter` 的 `prepareText`:按回收的 `chat.prepare` 表渲染「正在启动对话运行环境…」替换「思考中」,真实回合一到位即自动回落。
|
||
- `shared/types.ts`:`ChatMessage.prepareStage`(仅渲染进程启动占位使用,不持久化)。
|
||
|
||
验证:新增 `scripts/verify-new-task-switch.cjs`(真实隔离 Electron + 真实 preload/IPC/渲染进程 + 真实 OMP + 受控本地上游)30 项断言全 PASS:
|
||
- 计时(点击发送为 t0):对话页、用户消息、启动占位、「已处理」计时均在 **+26 ms** 出现;启动占位在 **+1.9 s** 让位给真实回合,真实回复在 **+2.6 s** 渲染(另一次跑分 +23.8 ms / +2.27 s / +2.97 s)——占位确实覆盖了运行环境启动窗口。
|
||
- 不重复:一次发送只创建 1 个会话;回合结束后 `.cr-self-bubble` 恰好 1 条(无乐观+真实重复);占位在真实回合出现前清除。
|
||
- 发送失败(清空模型注册表后发送):草稿恢复为原文、无残留乐观消息、错误「请先添加并选择自定义模型」显示、回到新建页、无启动占位。
|
||
- 停止与切换任务:回合运行中切到新任务再切回,消息与运行状态都在;点击停止后助手消息状态为 `cancelled`、取消指示可见、无残留占位、仍只有 1 条用户消息。
|
||
- 回归:`npm run typecheck`、`npm run build`、`npm run test:chat`、`npm run test:conversation`、`npm run test:conversation-ipc`、`node scripts/verify-user-message-edit.cjs` 全部通过。
|
||
- 报告:`.tmp/new-task-switch-641vdN/report.json`(另有 `.tmp/new-task-switch-TX5VSX/`)。
|
||
|
||
遗留:
|
||
1. 原版「发送后」逐帧等待态(先后顺序、每段时长)无 REA 运行时证据(探针限制见上),本项只对齐可证部分。
|
||
2. 发送失败时仍会留下一个空会话(`session.create` 已成功、回合未开始);原版在准备失败时是否保留会话未采集。
|
||
3. 启动阶段目前只使用 `acquiring_runtime` 一条;原版多阶段(expert → … → checking_launch_config)的发出方、顺序与时长未采集,不按字符串表臆造。
|
||
4. 发送窗口内没有「停止」入口(`停止` 只在回合运行后出现,沿用既有实现);原版在准备阶段是否可直接取消未采集。
|
||
|
||
### 09. 账户菜单与页面
|
||
|
||
- [x] 通过 REA 对齐点击当前登录账户后的入口、弹层位置、布局、交互及页面风格。
|
||
- [x] 内容按用户指定范围:头像与名称、套餐、余额、升级套餐、购买积分、设置、外观、记忆与进化、检查更新、退出登录。
|
||
- [x] 逐项盘点现有数据和功能,区分可直接接入、待实现与依赖服务端;不以虚构余额、套餐或成功提示代替真实能力。
|
||
- [x] 验收:已接功能实际生效,弹层关闭与导航返回正常,浅色/深色一致;内容不要求逐项照搬原版账户菜单。
|
||
|
||
状态:已按回收几何/样式实现并通过真实窗口回归(63 项断言);设置与外观接入真实设置窗口,账号/计费/更新类入口明确标注未接入。
|
||
|
||
原版证据:
|
||
- 打开态运行时布局 REA Evidence `ev_95a39af88081a641961779c55d5855e0e3d57889e5d2ccad2c3dc15e26e1c8bb`(WorkBuddy 5.8.3,视口 1336×1240,736 个节点):popover 根 `[8,607,320,577]`、`placement-top`、宽度 320、`max-width:calc(100vw - 24px)`(计算值 1312px)、`max-height:calc(100vh - 32px)`、`overflow:hidden auto`、padding 8、gap 8、圆角 16、背景 `rgb(247,247,247)`、`border-top-width` 计算值 1px(声明 `.5px`)、边框色 `rgb(222,222,222)`、阴影 `rgba(0,0,0,.04) 0 6px 24px 0, rgba(0,0,0,.04) 0 4px 6px 0`、字体 14/400/22、`role="menu"`;条目窄条 `.user-menu-item` 显式 `role="button"`(attributes 仅记录 class/id/role)。
|
||
- 同证据结构:`.user-menu-section` `[17,616,302,72]`(白卡 radius 8 / padding 8);`.user-menu-header` `[25,624,286,56]`(min-height 56 / padding 8 / gap 8)、头像框 `.user-menu-header-avatar` `[33,632,40,40]`、名称 `.user-menu-header-name-text` 16/500/24、`.user-menu-header-copy-uid` 20×20 默认 `opacity:0`;`.user-menu-billing-summary` `[17,696,302,102]` + 两个 `.user-menu-billing-cell` `[25|170,704,141,86]`(padding `4px 8px 8px`、title 12/400/20 `rgba(0,0,0,.5)`、value 14/500/22 + `tabular-nums`、`.user-menu-billing-cell-action` margin-top 8 / 高 24);`.user-menu-items` `[25,814,286,307]` gap 2、条目 `[25,*,286,32]`(padding `5px 8px`、radius 8、gap 8、`justify-content:space-between`、图标 16×16);`.user-menu-footer` `[25,1127,286,40]`(margin-top 6 + padding-top 8 + `:before` 1px `var(--wb-border-subtle)`);`::-webkit-scrollbar` 6px / thumb `var(--wb-scrollbar-thumb)` / radius 999。
|
||
- 同证据反证:736 个节点中没有全屏遮罩节点,也没有 overlay/mask/scrim/dismiss 类;因此原版不是靠全屏遮罩关闭,本实现不做 overlay(否则会吞掉外部点击的第一次交互)。
|
||
- 辅助证据:`ev_4162472c…`(5.8.2 账户菜单截图,`.tmp/rea-assessment-user-menu/frame-000.png`)、`ev_c9144986…`(5.8.3 打开→Escape 帧,`.tmp/rea-account-menu-20261017/`)、`ev_1e775995…`(只打开不关闭,`.tmp/rea-account-menu-open-20261017/`)。
|
||
- 回收样式表:`UserMenu-CpHz-0rF.css`(`.user-menu-popover`、`.user-menu-section`、`.user-menu-header*`、`.user-menu-billing-cell*`、`.user-menu-items`、`.user-menu-item*`、`.user-menu-separator`、`.user-menu-footer`、`.user-menu-avatar-initial{background-color:#0056d4}`)与 `tokens-Df-i-zsO.css` / `src-CBaV_A3S.css`(`--wb-bg-popover`:浅 `var(--wb-palette-white-100)` / 深 `#242424`,`.user-menu-popover` 内改写为 `var(--wb-bg-secondary)`;`--wb-popover-radius:var(--wb-radius-2xl)`=16px;`--wb-bg-card` 浅 `white-100` / 深 `#242424`;`--wb-border-subtle` 浅 `gray-3` / 深 `color-mix(in srgb, white 6%, transparent)`;`--wb-dropdown-item-bg-hover` 浅 `#f2f2f2` / 深 `#2e2e2e`;`--wb-scrollbar-thumb` 浅 `black-20` / 深 `white-20`、`--wb-scrollbar-thumb-hover` 浅 `black-30` / 深 `white-30`;`--wb-popover-shadow` 浅 `0 6px 24px 0 #0000000a, 0 4px 6px 0 #0000000a` / 深 `0 6px 24px -4px #0006, 0 4px 6px -8px #0003`)。
|
||
- 探针(可复跑):`scripts/rea/probe-account-menu-20261017.json`(打开 + 稳定帧)、`probe-account-menu-open-20261017.json`、`probe-account-menu-close-20261017.json`(Escape 关闭)。REA 的 layout 快照在 inspect 开始时抓取,早于探针动作,因此打开态证据需要「菜单已处于打开态再采集」。
|
||
- 观察范围:桌面端 5.8.3 浅色主题、zoom=1、1336 宽窗口,账户菜单打开态。未覆盖:原版深色主题运行时像素、原版条目 SVG 路径、账号/计费服务返回的真实数值、外部点击与 Escape 之外的关闭路径。
|
||
|
||
实现:
|
||
- `workbuddy/user-menu/AccountMenu.tsx`(新增):`createPortal` 到 `document.body`,根节点 `.ycode-account-menu` 类名按证据拼成 `wb-popover wb-popover--placement-top user-menu-popover user-menu-popover--local user-menu-popover--portal`;`useLayoutEffect` 按 `placement-top` 落位(左边缘对齐触发器、底边距触发器上沿 8px,并按视口夹紧,随 resize 重算);关闭沿用仓库既有 popover 约定(`document` 上的 `pointerdown` 外部判定 + 捕获阶段 Escape,Escape 后焦点回到触发器)。
|
||
- `workbuddy/user-menu/account-menu-source.css`(新增):逐条落地回收规则并把 8 个侧栏作用域缺失的 token 按回收取值补齐;所有规则收敛在 `.ycode-account-menu-scope` 下,避免与其它 `.wb-popover` 作用域互相污染。
|
||
- `workbuddy/user-menu/account-menu-icons.tsx`(新增):设置/外观/记忆与进化/检查更新/退出登录 5 个 16×16 线性图标,暂用 Lucide(MIT)占位并在文件头注明原版路径待回收。
|
||
- `components/Sidebar.tsx`:底部账号触发器改为打开账户菜单(`aria-haspopup="menu"`、`aria-expanded`,记录触发器几何供 popover 落位);收起侧栏时关闭菜单;菜单渲染在 footer 内(portal)。
|
||
- 内容与真实性:头部=mock 账号头像与名称(副标题标注「账号为 mock 数据,尚未接入登录」);计费摘要=套餐/余额两格,值显示「未接入」,升级套餐/购买积分按钮 `disabled` 并带「尚未接入账号与计费服务」提示;设置 → `openSettings('model')`、外观 → `openSettings('appearance')`(值跟随当前主题,显示浅色/深色);记忆与进化、检查更新、退出登录标注「待拆解」/「未接入」,点击只提示,不假装成功。
|
||
|
||
验证:新增 `scripts/verify-account-menu.cjs`(真实隔离 Electron + 真实 preload/IPC/渲染进程 + 真实设置窗口)63 项断言全 PASS:
|
||
- 几何/样式:popover 320 宽、左边缘与触发器对齐(实测 x=8)、底边距触发器上沿 8px(popover bottom 846 / 触发器 top 854)、padding 8 / gap 8 / radius 16、浅色背景 `rgb(247,247,247)`、边框计算值 1px `rgb(222,222,222)`、双层阴影、`overflow:hidden auto`、`max-width` 1312px、`max-height` 870px(视口 902)、`role=menu`;三张卡片 302 宽 / padding 8 / radius 8 / 白底。
|
||
- 头部与计费:min-height 56 / padding 8 / gap 8、头像框 40×40、初始头像圆形 `rgb(0,86,212)`、名称 16/500/24 且等于 mock 账号名;两个计费单元各 141×86、padding `4px 8px 8px`、title 12/400/20、value 14/500/22、动作行 margin-top 8 / 按钮高 24 / `disabled`、文案「升级套餐」「购买积分」;值恰为「未接入」,整份菜单文本不含任何数字(不虚构套餐/余额/积分)。
|
||
- 条目与 footer:4 个条目各 32 高 / `5px 8px` / radius 8 / gap 8 / space-between / 14/400/22 / 图标 16×16 / `role="button"`;设置显示 `Ctrl+,`、外观显示当前主题、未接入行显示「待拆解」「未接入」且 `aria-disabled="true"`;footer margin-top 6 + padding-top 8 + 1px 分隔线(`rgb(242,242,242)`)、退出登录行 32 高。
|
||
- 交互:触发器点击开/再点关、Escape 关闭并把焦点交还触发器(`aria-expanded` 同步为 false)、外部真实鼠标点击关闭且同一次点击不被吞(点通知铃铛 → 菜单关 + 通知面板开)、设置行复用并导航原生设置窗口到模型面板、外观点亮外观面板。
|
||
- 浅色/深色:通过设置窗口切深色后 popover 背景 `rgb(41,41,41)`、卡片 `rgb(36,36,36)`、边框 `rgb(61,61,61)`、阴影切换为深色 token,「外观」行值同步为「深色」;切回浅色后 `documentElement.dataset.theme` 与 `localStorage['ycode:theme']` 均为 light。
|
||
- 失败路径:未接入行点击后只显示提示、不新开窗口。
|
||
- 回归(单独串行执行,避免多进程 Electron 抢焦点):`npm run typecheck`、`npm run build`、`npm run test:chat`、`npm run test:conversation`、`npm run test:conversation-ipc`、`node scripts/verify-sidebar-footer.cjs`、`node scripts/verify-user-message-edit.cjs` 全部通过。
|
||
- 报告与截图:`.tmp/account-menu-Y1FW3I/report.json`、`account-menu-light.png`、`account-menu-dark.png`。
|
||
|
||
遗留:
|
||
1. 原版条目 SVG 路径未回收,5 个图标先用 Lucide(MIT)占位,待 REA 回收后替换。
|
||
2. 账号、套餐、余额、升级套餐、购买积分、检查更新、退出登录均依赖 YCode 不存在的账号/计费/更新服务,按「不渲染无效能力」原则保持未接入并显式标注;原版这些位置显示服务返回的真实数值。
|
||
3. 用户指定范围之外的原版组件未实现:Buddy 加油站 / 去邀约 / 成长计划 / 意见反馈、`.user-menu-header-copy-uid`(复制 UID,默认 `opacity:0`)、子菜单(`.user-menu-popover--portal .user-menu-submenu-wrapper`)。
|
||
4. 头部比原版高 4.19px(原版 56 只有名称行,本实现多一行「账号为 mock 数据」副标题),条目区比原版矮(原版 369 / 本实现 196,因未实现上述范围外条目),popover 总高因此为 408 而非 577。
|
||
5. 原版深色主题未做运行时像素采集,深色按回收 token 对齐(popover `#292929`、卡片 `#242424`)。
|
||
6. 原版外部点击与 Escape 之外的其他关闭路径(滚动、窗口失焦等)未采集。
|
||
|
||
## 设置
|
||
|
||
### 10. 通用
|
||
|
||
- [x] 拆解原版通用设置页面的布局、控件和交互。
|
||
- [x] 将 YCode 当前已经支持的内容接入对应控件,记录暂未支持的差异。
|
||
- [x] 验收:控件反映真实配置;修改即时生效或明确提示生效时机,重启后持久化正确。
|
||
|
||
状态:已实现并通过真实窗口回归;未接入项按「保持原版外观 + 明确标注」处理,逐项记录在下方。
|
||
|
||
原版证据:
|
||
- REA `ev_a2dbeb23e30b9770743e857279a2e43da38fcc48d10612f75ace2938c5b29114`(WorkBuddy 5.8.3 设置→通用面板,900×680,自顶向底滚动采样 0/400/795/0,帧 frame-000~003):16 行文案、控件类型与取值、分组标题(常规/权限/存储/通知/隐私)、滚动边界。
|
||
- REA `ev_2309de12a003fca2c863a223cbda5bccad36b3af73e29bc309fdaed94745cf9e`(同页 DOM 边界、computed style、svg_nodes):面板 `[232,40,638,1415]`(5.8.2 复核)、组 `[40/218] [274/514] [804/265] [1085/234] [1335/120]`、行高 `[44,68,52 | 56,68,68,76,68,68,56 | 56,68,56 | 66]`、卡片 `#fff / radius 12 / padding 12px 16px`、标题 14px/500/22px、描述 14px/400/22px `rgba(0,0,0,.5)`、开关 34×20、下拉 120×32、滑块 224×40、存储卡 638×235、路径输入 530×32、更改按钮 68×32、打开目录 76×22、通知按钮 130×32。
|
||
- REA `ev_377c8790468b3c67dad8577aabb327f98d3140659f0fffdbabfcff5a40cc9e94`:面板与控件样式来源(`contexts-BkjwYSZT.css` sha256 `50cbd673f7f35fc7f966bdddc6c9f5677fdfe944cb904888d214619fca259380`、`tokens-Df-i-zsO.css` sha256 `13f2bf82c96e5167a7d7098e36950c6c22c475b3337dbb8bb29ad2fe79cde696`、UI kit `src-Bh3YZ35H.css` sha256 `88dbdc1cd3f59d0e8b75824512a465a2be714bc7af481d790fe3a9484f85e01a`)。
|
||
- 观察范围:桌面端浅色主题(另加暗色抽查)、900×680 设置窗口、通用页默认态;未覆盖:各下拉展开态的选项列表(`general-settings-panel__link-open-select-popover` / `power-save-blocker-settings__popover` 均为 368 宽,规则已回收但本轮不接入)。
|
||
|
||
实现:
|
||
- `scripts/rea/recover-general-settings.mjs`:从上述 REA 回收样式表投影出面板规则(149 条)+ UI kit 控件规则 + 面板作用域 token,写入 `src/renderer/src/components/source-general-settings-recovered.css`;UI kit 规则用 `:where(.general-settings-panel)` 前缀并排在面板规则之前,以保持原版同权重、面板后加载的层叠(例如 `.wb-storage__open-folder{height:auto}` 必须压过 `.wb-button--medium{height:32px}`)。
|
||
- `src/renderer/src/components/GeneralSettingsPanel.tsx`(+ `source-general-settings.css`、`general-settings-icons.tsx`):按原版 DOM/类名/几何复刻 16 行;品牌替换遵循既有约定(「WorkBuddy 默认工作空间存储路径」→「YCode 默认工作空间存储路径」)。
|
||
- `src/main/general-settings.ts`、`src/main/config.ts`、`src/main/ipc.ts`、`src/main/index.ts`、`src/shared/{types,ipc}.ts`、`src/preload/index.ts`:新增 `window.ycode.general` API(get/update/openFolder/changeWorkspaceRoot/openSystemNotificationSettings)。
|
||
- 已接入(真实行为、重启持久化):
|
||
1. 运行时防止系统休眠 → `config.preventSleep`;任务运行期间主进程持有 `powerSaveBlocker`(`updateActiveRuns` 挂在 `chat:send` 运行生命周期上)。
|
||
2. 开机自动启动 → `config.autoLaunch` + 系统登录项(`app.setLoginItemSettings`),读取时以系统登录项真实状态为准。
|
||
3. 系统缓存目录 → 真实数据目录(`app.getPath('userData')`)、真实占用与磁盘容量(递归扫描 + `fs.statfs`)、「打开目录」调用系统文件管理器。
|
||
4. 默认工作空间存储路径 → `config.workspaceRoot`,只读填充态输入框 + 「更改」走系统目录选择器。
|
||
5. 桌面通知 → 「前往系统设置」打开系统通知设置页(Windows `ms-settings:notifications`)。
|
||
- 未接入(原版行为未实现,因此保留原版外观、控件类型与初始态,但标记 `aria-disabled` + `title="暂未接入"`,且不产生任何效果):语言、链接打开方式、字体大小、允许锁屏运行、网络代理、自动安装可信技能、技能自动更新、套件自动更新、通知提示音、定时任务产物通知、体验优化计划(含「了解更多」内联链接)。共 11 项。
|
||
|
||
验证:`scripts/verify-general-settings.cjs` 在真实隔离 Electron(真实 preload/IPC/渲染进程/文件系统 + 原生设置窗口,视口 900×680)中 43 项断言全部通过:导航「通用」可用且为当前页;面板列宽 638、顶层级顺序 group/group/storage/group/group;4 个分组标题;14 行标题与行高逐一匹配;分组标签 638×22 且左内边距 8px;卡片 `12px 16px`/radius 12/白底;标题 14/500/22、描述 14/400/22 三级色;开关 34×20;5 个下拉 120×32;滑块 224×40 且标签 小/默认/大;通知按钮 130×32 且带尾部图标;存储卡标题/说明/76×22 打开目录/530×32 输入框/68×32 更改/填充态样式;用量文案与真实数据目录;进度条 606×4 三段合计 604;分隔线 606×1;11 个占位项全部 `aria-disabled` + 「暂未接入」且点击无效(下拉不弹层、开关不切换);防休眠与开机自启动开关可切换、写入 `config.json` 并可还原;工作空间路径改写真实持久化(含 `workspace.get()` 与 `config.json`);重开设置窗口后偏好与路径仍正确渲染;暗色主题下面板卡片与标题重新着色;无渲染器报错。报告 `.tmp/general-settings-2elYNv/report.json`。
|
||
回归:`npm run typecheck`、`npm run build`、`npm run test:chat`、`npm run test:conversation`、`npm run test:conversation-ipc`、`node scripts/verify-settings-nav.cjs`、`node scripts/verify-composer-context-info.cjs`、`node scripts/verify-account-menu.cjs` 全部通过。`test:conversation-ipc` 的 electron mock 因新增 `powerSaveBlocker` 依赖而先失败,已在 `scripts/conversation-ipc-regression.ts` 补齐 mock(`powerSaveBlocker`、`app.isPackaged/isReady/getAppPath/getLoginItemSettings/setLoginItemSettings`、`shell.openExternal`)。
|
||
|
||
遗留:
|
||
1. 系统级写入的自动化隔离:验收时以 `YCODE_DISABLE_SYSTEM_INTEGRATION=1` 跳过登录项与系统设置页的真实写入,因此「写入注册表登录项」「打开 ms-settings:notifications」两条系统副作用只在人工路径覆盖,未纳入自动化断言。
|
||
2. 「更改」按钮会弹出系统目录选择器,无法自动点击;脚本改为断言按钮可用 + 通过 IPC 验证路径持久化,并在重开窗口后断言面板渲染新路径。
|
||
3. 数据目录路径较长时,缓存目录说明行会像原版一样换行(原版 `.wb-storage__path-text` 无 nowrap);脚本按实际行高换算期望的面板/卡片高度(QA 沙箱路径下为 1415+22=1437)。
|
||
4. 若后续要把语言/链接打开方式/网络代理/通知提示音等下拉接入,需要先按 REA 采集各下拉的展开态选项列表(当前仅有弹层容器 368 宽的样式规则)。
|
||
|
||
### 11. 外观与浅色/深色主题
|
||
|
||
- [ ] 对齐原版外观页面及主题切换交互;当前只保留浅色、深色两种选择。
|
||
- [ ] 梳理原版主题切换会改变的 token、样式、资源和组件状态。
|
||
- [ ] 统一主窗口、设置窗口、独立任务窗口、弹层、输入区、对话、代码/表格/工具结果和右侧栏的主题响应。
|
||
- [ ] 验收:即时切换、跨窗口同步、重启持久化;所有已接页面无浅深混色和不可读内容。
|
||
|
||
状态:待 REA 对照与主题覆盖审计。
|
||
|
||
### 12. 快捷键页面与真实功能
|
||
|
||
- [ ] 拆解页面布局、分组、按键显示、编辑/恢复默认等实际存在的交互。
|
||
- [ ] 建立快捷键 → 命令 → 实际动作映射,按 REA 证据确认支持范围。
|
||
- [ ] 验收:展示按键与实际绑定一致;输入框、编辑器、弹层、独立窗口中的作用域和冲突处理正确;可修改项持久化。
|
||
|
||
状态:待 REA 拆解与命令绑定盘点。
|
||
|
||
### 13. 个性化页面与真实功能
|
||
|
||
- [ ] 拆解原版页面、字段、说明、保存/启停等交互。
|
||
- [ ] 将支持的个性化配置接入实际运行链路,确认在哪些新建/既有会话中生效。
|
||
- [ ] 验收:修改与持久化有效;界面显示与实际行为一致;未接能力明确记录,不用占位控件宣称完成。
|
||
|
||
状态:待 REA 拆解。
|
||
|
||
### 14. 记忆与进化
|
||
|
||
- [ ] 记录待拆解:入口、页面、数据、读写机制、会话生效方式及生命周期。
|
||
|
||
状态:待拆解,本轮不自动展开实施。
|
||
|
||
### 15. 智能体页面:团队管理
|
||
|
||
- [ ] 拆解原版智能体设置页面,本轮仅对接“智能体团队管理”。
|
||
- [ ] 根据 REA 证据梳理团队列表、详情及管理操作,将需要的真实存储/配置链路接入。
|
||
- [ ] 验收:仅展示已确定范围;管理操作实际生效、重启后可恢复;团队管理与真实子代理调度的完成度分别记录。
|
||
|
||
状态:待 REA 拆解;范围限定为团队管理。
|
||
|
||
### 16. 移除助理与设备连接
|
||
|
||
- [x] 移除设置中的“助理”“设备连接”导航和页面入口。
|
||
- [x] 清理默认选中、内部导航、深链接/旧配置回退中的无效目标。
|
||
- [x] 验收:设置中不再出现这两项,旧导航目标不会导致空白页。
|
||
|
||
状态:已实现并通过真实窗口回归。
|
||
|
||
原版证据:WorkBuddy 5.8.3,REA Evidence `ev_2309de12a003fca2c863a223cbda5bccad36b3af73e29bc309fdaed94745cf9e`(归档 `.tmp/rea-assessment-settings-window/layout.json`,视口 900×680):设置导航 16 项 / 4 组、条目 `190×32`、x=12、y 从 111 起;`.settings-modal__nav` 200 宽而条目 190 宽,10px 差值为内容溢出时 thin 滚动条占用的槽位。观察范围:设置窗口导航栏;未覆盖:被移除项的原版内部页面(本轮不迁移其内容)。
|
||
|
||
实现:`src/renderer/src/components/SettingsDialog.tsx` 新增 `REMOVED_SETTINGS_TABS = ['claw','deviceConnections','dataManagement','buddyApps']`、导出 `VISIBLE_SETTINGS_TABS = SOURCE_WINDOWS_SETTINGS_TABS.filter(...)`(12 项)、`normalizeSection()`;`?section=` 启动、已打开窗口的 `onSettingsNavigate` 深链接统一走 normalize(`models`→`model`,已移除项→`model` 回退),`SourceSettingsNavigation` 新增 `visibleTabs` 属性按可见列表渲染。未删除任何用户数据、已安装内容或配置。
|
||
|
||
验证:`scripts/verify-settings-nav.cjs` 在真实隔离 Electron(真实 preload/IPC/renderer + 原生设置窗口)中 19 项断言全部通过:12 项顺序与移除项不在 DOM/标签/搜索元数据、条目几何 x=12 / 32 高 / 宽度等于导航内容宽、3 个组标题 + 1 条 about 分隔线、未接入项 `aria-disabled` + 「暂未接入」提示且点击提示「该设置尚未接入」、`appearance`/`models` 仍可打开、`openSettings('claw')`(旧窗口导航)与 `openSettings('dataManagement')`(新窗口 `?section=` 启动)都回退到模型面板、无渲染器报错;报告 `.tmp/settings-nav-5YlLX6/report.json`。回归:`npm run typecheck`、`npm run build`、`npm run test:chat`、`npm run test:conversation`、`npm run test:conversation-ipc`、`node scripts/verify-composer-context-info.cjs` 全部通过。
|
||
|
||
遗留:移除 4 项后本窗口导航不再溢出,条目宽 200(原版 16 项溢出时为 190);未补 `scrollbar-gutter`,因为 REA 回收的原版 CSS 没有该规则,保持源码级一致,条目增多再次溢出时会自动回到 190。
|
||
|
||
### 17. 移除数据管理与应用管理
|
||
|
||
- [x] 移除设置中的“数据管理”“应用管理”导航和页面入口。
|
||
- [x] 清理相关无效导航与页面占位。
|
||
- [x] 验收:入口完全移除且其余设置导航正常;不删除已有用户数据或已安装内容。
|
||
|
||
状态:已实现并通过真实窗口回归(与 16 同一次改动,代码见上)。
|
||
|
||
原版证据、实现、验证:同 16。`dataManagement`、`buddyApps` 与 `claw`、`deviceConnections` 一并进入 `REMOVED_SETTINGS_TABS`,走同一份 normalize 回退与 `visibleTabs` 渲染;验收脚本同时断言这两项(以及 `claw`、`deviceConnections`)不在 DOM、标签和搜索元数据中,且 `openSettings('dataManagement')` 深链接落到模型面板而不是空白页。未触碰本地会话数据、安装中心与任何已有用户内容。
|
||
|
||
遗留:无新增;数据管理/应用管理的原版页面内容不再复刻(用户指定仅移除入口)。
|
||
|
||
### 18. 安装中心
|
||
|
||
- [ ] 记录待拆解:入口、页面结构、安装来源、安装/更新/卸载流程及与其他能力中心的关系。
|
||
|
||
状态:待拆解,本轮不自动展开实施。
|
||
|
||
### 19. 关于与获取帮助
|
||
|
||
- [ ] 通过 REA 复刻“关于”“获取帮助”的布局、字体、图标、间距和交互外观。
|
||
- [ ] 不复制原版跳转链接;逐项标注 YCode 自有入口或尚未配置的跳转。
|
||
- [ ] 验收:本应用版本等信息真实;没有误跳 WorkBuddy 官方账户、帮助或其他服务地址。
|
||
|
||
状态:待 REA 对照;布局纳入范围,原版链接排除。
|
||
|
||
## 后续模块
|
||
|
||
### 20. 定时任务、资料库、更多中的项目
|
||
|
||
- [ ] 定时任务:待拆解入口、页面、任务配置、触发/执行记录及生命周期。
|
||
- [ ] 资料库:待拆解入口、资料组织、添加/读取/检索及会话引用方式。
|
||
- [ ] 更多 → 项目:待拆解入口、项目组织、工作区/任务关系及管理操作。
|
||
|
||
状态:三个模块均待拆解,本轮不自动展开实施。
|
||
|
||
## 追加需求
|
||
|
||
### 21. 对话框 Git 分支检测与会话上下文信息
|
||
|
||
- [x] 通过 REA 拆解 WorkBuddy 对话框中 Git 分支和会话上下文信息的显示位置、图标、字体、间距、提示及交互。
|
||
- [x] 根据当前任务绑定的工作目录检测 Git 仓库和实际分支;核对原版对非 Git 目录、未产生首次提交、detached HEAD、检测失败等状态的处理。
|
||
- [x] 明确原版“上下文信息”的实际字段、数据来源、更新时机和展示方式,再接入 YCode 的真实会话数据;上下文用量等字段如存在,必须区分真实统计与估算。
|
||
- [x] 验收:新建任务、已有会话、切换空间/任务、外部切换 Git 分支后的信息归属与刷新正确;无仓库时不显示虚构分支;浅色/深色及窄窗口布局与原版对齐。
|
||
|
||
状态:已按回收几何/样式/图标/文案实现,并通过真实隔离 App 端到端回归(47 项断言 + 6 项 Git 检测断言);分支为真实 git 结果,用量为上游真实 token(不估算)。
|
||
|
||
原版证据:
|
||
- 运行时几何/结构 REA Evidence `ev_2569313a7a0aa901c5a3b8dc1d7fd190109b9006c915830109b62195f312376a`(WorkBuddy 5.8.3 会话输入区,视口 1336×902,`.tmp/rea-debug-ready-20261017/layout.json`):
|
||
左组 `[.cr-add-menu 431,32]` → `[._branchLabel_jdvlk_244 471,82×32]` → `[.cr-permission-setting 561,107×32]` → `[.cr-status-chips 676]`;
|
||
右组 `[.cr-context-usage 903,32×32]` → `[.cr-model-selector 939,174×32]` → `[.cr-voice-trigger 1117]` → `[.cr-send-button 1157]`;两组 gap 分别为 8 / 4。
|
||
分支标签:radius 999、padding `5px 8px`、gap 8、max-width 188、字体 14/400/22、transition `background .15s`;图标按钮 16×16(`._branchLabelIconButton_jdvlk_279`),默认 SVG `viewBox 0 0 16 16` 分支字形、悬停 copy 字形(`lucide lucide-copy wb-icon`、`viewBox 0 0 24 24`、`stroke-width 1.5`、rect 14×14 + path),两个字形用 opacity 互换(1/0 → 0/1),`:hover`/`:focus-within` 背景 `var(--wb-bg-secondary)`;
|
||
用量环:32×32 / radius 16 / transition `background-color .2s`、hover `var(--cr-context-usage-hover-bg,#0000000d)`、`role="button"`、`cursor:pointer`;内含 16×16 SVG,track `circle r=7 stroke-width 2 stroke rgba(128,128,128,.2)`、progress `circle r=7 stroke-width 2 stroke-linecap round transform rotate(-90 8 8)` + `transition:stroke-dashoffset .3s`;截图像素采样确认该实例用的就是这两个回退色(`rgb(230,230,230)` / `rgb(158,162,176)`)。
|
||
浮层:`.cr-context-usage-popover` 280 宽 / padding 16 / radius 16 / font 12、标题行(`__title` 12/400/20 + `__close` 16×16)、百分比 20/700/28、`已使用` 12/20、进度条 8 高 radius 3、五类图例(每行 22 高、8×8 圆点、名称 13/22、值 12/20)、`__window-control` 分隔区;另有 `.__requesting-tip`(`bottom:calc(100% + 8px); right:0`)。
|
||
- 回收样式表:`contexts-BkjwYSZT.css`(`._branchLabel_*`、`._branchLabelCompact_jdvlk_274`)、`src-CyXHA0IC.css`(`.cr-context-usage*`);文案取自已回收 i18n 表 `chatInput.contextUsage.*`(title 上下文用量 / used 已使用 / tooltip `{percentage}% · {used} / {total} 上下文已使用` / requesting / needMessage / unavailable / 五个分类名)。
|
||
- 探针(可复跑):`scripts/rea/probe-todo21-discovery.json`(当前窗口基线)、`probe-todo21-home.json`、`probe-todo21-conversation.json`;本轮采集 `ev_f11473e2…`(首页基线,首页输入区只有工作空间/权限两个入口,没有分支标签与用量环)、`ev_c0dbb550…`、`ev_9796071b…`。
|
||
- 数据来源(原版):REA 只能拿到 DOM/样式/文案,浮层的定位与用量数值生成在 JS 内,本轮未取到;因此数值字段按已回收文案与类名结构对齐,YCode 侧只用自己能证明的真实值。
|
||
- 观察范围:桌面端 5.8.3、浅色运行时 + 深色按 `.cr-theme` token;未覆盖:原版 detached HEAD/无首次提交/非仓库时的标签表现、悬浮提示的实际锚定与延迟、用量环颜色的分段规则、`__window-control` 的内容。
|
||
|
||
实现:
|
||
- `src/main/git.ts`(新增):`readGitBranch(cwd)` 真实执行 `git -C <cwd> rev-parse --is-inside-work-tree` → `symbolic-ref --short -q HEAD` → 失败再 `rev-parse --short HEAD`;`windowsHide`、5s 超时、git 缺失/超时降级为 `unavailable:true`,全部异常都返回合法形状而不是抛出。
|
||
- `src/main/ipc.ts`:新增 `git:branch(sessionId)`,用会话 `cwd`(无则回退工作空间根目录)调用上面的检测。
|
||
- `src/renderer/src/workbuddy/composer/context-info/`(新增):`ComposerContextInfo.tsx`(`ComposerBranchLabel` / `ContextUsageRing`)、`icons.tsx`(分支字形 + lucide copy,路径逐字取自证据)、`source.css`(回收规则收敛在 `.cr-input-toolbar` 内)。
|
||
- `src/renderer/src/workbuddy/composer/ComposerControls.tsx`:新增 `branchLabelSlot`(左组「+」之后)与 `contextUsageSlot`(右组最前)两个槽位,与证据里的顺序一致。
|
||
- `src/renderer/src/components/ChatPane.tsx`:传入两个槽位;用量环的分母用所选模型真实的 `maxInputTokens`。
|
||
- 用量真实性:`ChatEvent` 新增 `usage`;OMP 通路在 `message_end` 读取助手消息上的真实 usage(`contextTokens ?? input + cacheRead + cacheWrite`,字段语义取自内核自身),网关通路在 Responses `response.completed.response.usage` 透传;渲染进程只保存最近一次真实值(`stores/chat.ts` 的 `sessionUsage`),拿不到就显示原版「正在会话中 / 暂无用量明细 / 暂时无法获取」文案。
|
||
- 原版浮层按五类拆分提示词构成,YCode 的提示词由内核拼装、拿不到该拆分,因此分类值显示「—」,不编造比例。
|
||
|
||
验证:
|
||
- `node scripts/verify-git-branch.cjs`(真实临时仓库 + esbuild 打包被测模块)6 项全 PASS:master 分支、`feature/x`、detached HEAD 短 SHA、空目录、包含不存在路径的非仓库目录、`git init` 无提交。
|
||
- `node scripts/verify-composer-context-info.cjs`(真实隔离 App + 真实 OMP 内核 + 受控本地 OpenAI 兼容上游 + 真实 Git 仓库)47 项全 PASS:
|
||
分支标签:文案等于真实分支名、32 高 / `5px 8px` / radius 999 / gap 8 / max-width 188 / 14-400-22 省略号、图标按钮 16×16、默认字形与 copy 字形几何与 class 逐项一致、默认可见 copy 隐藏、与「+」和权限入口各相距 8px;悬停后字形 opacity 互换且背景等于同作用域 `--wb-bg-secondary` 解析值;点击图标把真实分支名写入系统剪贴板。
|
||
用量环:32×32 / radius 16 / `role=button`、为右组第一个元素、与模型选择器相距 4px、内部 16×16 SVG 与 track/progress 圆的 r/描边/线帽/rotate/过渡逐项一致;无用量时 dashoffset 等于整周长。
|
||
浮层与提示:未拿到用量时显示「暂时无法获取用量明细,请稍后重试」且不显示百分比;真实回合(上游 `prompt_tokens 12800`、窗口 64000)后 dashoffset = 周长 ×0.8、悬浮提示 `20% · 12800 / 64000 上下文已使用`(底色/文字/圆角/内边距/字号与 `--wb-tooltip-*` 解析值一致)、浮层显示 `20%` 与 `12800 / 64000`、进度条填充 20%、五类图例名与 8×8 圆点齐全且值为「—」;关闭按钮可关闭浮层。
|
||
主题:通过原生设置窗口切深色后 `.chat-pane[data-cr-theme]` 变为 dark,浮层底色 `rgb(36,38,41)`、进度条 `rgba(255,255,255,.1)`、分支标签文字 `rgb(255,255,255)`;再切回浅色恢复 `rgb(255,255,255)`/`rgb(0,0,0)`。
|
||
- 回归(顺序执行,避免多进程 Electron 抢焦点):`npm run typecheck`、`npm run build`、`npm run test:chat`、`npm run test:conversation`、`npm run test:conversation-ipc`、`node scripts/verify-sidebar-footer.cjs`、`node scripts/verify-account-menu.cjs`、`node scripts/verify-new-task-switch.cjs` 全部通过。
|
||
- 报告:`.tmp/composer-context-lEIWRF/report.json`。
|
||
|
||
遗留:
|
||
1. 原版浮层/提示由 JS 定位(floating-ui),本轮只回收了 `.__requesting-tip{bottom:calc(100% + 8px);right:0}` 这一处锚定规则;YCode 按同向(上方 8px、右对齐到用量环)内联渲染,未逐像素比对原版浮层相对位置。
|
||
2. 原版五类上下文占比无法复刻(提示词由内核拼装,YCode 拿不到分类统计),分类值固定显示「—」;原版 `__window-control`(上下文窗口相关控件)内容未采集,未实现。
|
||
3. `._branchLabelCompact_jdvlk_274`(32 宽的紧凑形态)只回收了规则,未观察到运行时触发条件,尚未启用。
|
||
4. 原版对 detached HEAD、无首次提交、非 Git 目录、检测失败的标签表现未采集;YCode 在这些情况下不渲染标签(无提交但有分支时按 git 真实结果仍显示分支名)。
|
||
5. 用量环进度颜色:原版通过未回收的变量注入,本轮截图像素证明该实例用的是回退色 `#9ca3af`;是否随用量分段变色未验证。
|
||
6. 刷新时机未采集:YCode 在切换会话、窗口重新聚焦以及每 20s 轮询时重新读取分支;用量只在收到真实 `usage` 时更新。
|
||
7. 备用(非 OMP)内核走自建网关时用量来自 Responses `response.completed.usage`;Chat Completions 上游路径仍未解析 `usage`。
|
||
8. 用量环只在会话输入区渲染(本轮首页基线的 REA 证据里首页输入区确实没有该入口),首页/新建任务时不显示。
|
||
|
||
## 推进顺序
|
||
|
||
1. 对话与侧栏:01 → 02 → 03 → 04 → 05 → 06 → 07 → 08 → 09 → 21(均已实现并留下验收记录,未完成验收点在各条目内标注)。
|
||
2. 设置:16、17 的入口范围与 10(通用)均已落实并验证;接着依次推进 11 → 12 → 13 → 15 → 19。
|
||
3. 14、18、20 保留为待拆解,后续单独展开。
|
||
|
||
## 每项交付记录模板
|
||
|
||
- 当前状态:待拆解 / 证据已齐 / 方案已明确 / 实现中 / 已实现未验收 / 已验收 / 有阻碍。
|
||
- 原版证据:版本、REA Evidence ID、观察范围、未覆盖状态。
|
||
- YCode 差异与方案:具体问题、涉及模块、数据与交互边界。
|
||
- 实现:修改文件、真实功能、占位或未接部分。
|
||
- 验证:功能回归、实际窗口交互、浅色/深色、相关截图/几何/像素结果及限制。
|
||
- 遗留:尚未解决的差异与下一步所需证据。
|