Files
ycode/docs/复刻待办清单-2026-10-17.md
T

67 KiB
Raw Blame History

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. 任务操作菜单

  • 将侧栏对话条目后的直接删除按钮替换为原版任务操作入口。
  • 通过 REA 对齐菜单的入口、位置、顺序、图标、文案、悬停、关闭、禁用和确认状态。
  • 接入:置顶、取消置顶、重命名、独立窗口中打开、打开文件夹、归档、删除任务。
  • 验收:置顶顺序和重命名持久化;独立窗口打开正确任务;文件夹指向正确工作目录;归档与删除行为独立;涉及运行中任务时有明确处理。

状态:主体已实现;运行中任务的处理与逐像素截图比对未完成。

原版证据: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. 表格“添加到对话”及引用结果

  • 对齐表格上方“添加到对话”按钮的字体、图标、背景、边距、出现条件及交互。
  • 对齐点击后输入区内引用表格的呈现,包括结构、预览、移除和与已有草稿的组合方式。
  • 对齐发送后用户消息中的表格引用显示。
  • 验收:引用正确表格内容,不丢失已有草稿;输入区、发送结果及历史重载三种状态一致。

状态:主体已实现并做了真实窗口回归;仍有两处原版证据缺口(见“遗留”)。

原版证据:

  • 渲染器实现: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. 编辑已发送消息

  • 拆解原版编辑入口、编辑容器、文本排版、按钮、焦点与键盘交互。
  • 对齐进入编辑、修改、取消、提交及失败状态。
  • 验收:编辑正确消息;取消恢复原内容;提交后的消息分支与产物处理符合已确认行为。

状态:主体已实现,并通过真实窗口回归与真实 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. 用户消息悬停时间

  • 为已发送用户消息补齐悬停显示时间。
  • 对齐出现位置、间距、字体、颜色和与操作图标的关系。
  • 复用三档时间格式;验证当天、同年异日、跨年、历史重载。

状态:随第 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. 用户消息回退操作与图标

  • 移除已发送用户消息上的“重新发送”按钮。
  • 替换为“将对话及产物回退到此消息前”操作,图标、提示及交互通过 REA 对照。
  • 明确回退边界:该消息之前,而非该消息之后;对话记录与对应产物修改一起处理。
  • 验收:多轮对话、文件创建/修改、无产物、运行中禁用、取消确认、回退失败和重启后状态;不得只删除可见消息而保留应回退的文件修改。

状态:主体已实现,并通过真实窗口与真实 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. 对话区域滚动条

  • 对齐宽度、颜色、轨道、圆角、悬停显隐、与内容边缘的间距。
  • 检查滚动条占位、长内容、流式跟随底部及用户手动上滚的行为。
  • 验收:浅色、深色、短对话、长对话、流式输出与不同窗口尺寸。

状态:已对齐并通过真实窗口回归(像素级比对 + 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. 侧栏最下一行

  • 移除最下方不应存在的“收起菜单”入口。
  • 对齐最下一行的布局、高度、内边距、字体、头像、当前图标与悬停状态。
  • 验收:普通/窄侧栏、浅色/深色下均无残留入口或空白占位。

状态:已对齐并通过真实窗口回归(运行时几何 + 计算值 + 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. 新建任务发送后的立即切页与占位

  • 盘点发送到创建会话、准备环境、接收首个事件之间的阻塞位置。
  • 对照原版发送后的页面切换时机与等待状态。
  • 发送后立即进入消息页面并显示用户消息;启动较慢时显示合适的助手占位/准备状态。
  • 验收:慢启动、正常启动、发送失败、停止、切换任务;不等到模型输出后才切页,不重复创建会话或丢失草稿。

状态:已实现并通过真实窗口回归(发送即切页 + 乐观用户消息 + 启动占位 + 真实回合替换 + 失败/停止/切换任务)。

阻塞位置盘点(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. 账户菜单与页面

  • 通过 REA 对齐点击当前登录账户后的入口、弹层位置、布局、交互及页面风格。
  • 内容按用户指定范围:头像与名称、套餐、余额、升级套餐、购买积分、设置、外观、记忆与进化、检查更新、退出登录。
  • 逐项盘点现有数据和功能,区分可直接接入、待实现与依赖服务端;不以虚构余额、套餐或成功提示代替真实能力。
  • 验收:已接功能实际生效,弹层关闭与导航返回正常,浅色/深色一致;内容不要求逐项照搬原版账户菜单。

状态:已按回收几何/样式实现并通过真实窗口回归(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. 通用

  • 拆解原版通用设置页面的布局、控件和交互。
  • 将 YCode 当前已经支持的内容接入对应控件,记录暂未支持的差异。
  • 验收:控件反映真实配置;修改即时生效或明确提示生效时机,重启后持久化正确。

状态:待 REA 拆解与能力盘点。

11. 外观与浅色/深色主题

  • 对齐原版外观页面及主题切换交互;当前只保留浅色、深色两种选择。
  • 梳理原版主题切换会改变的 token、样式、资源和组件状态。
  • 统一主窗口、设置窗口、独立任务窗口、弹层、输入区、对话、代码/表格/工具结果和右侧栏的主题响应。
  • 验收:即时切换、跨窗口同步、重启持久化;所有已接页面无浅深混色和不可读内容。

状态:待 REA 对照与主题覆盖审计。

12. 快捷键页面与真实功能

  • 拆解页面布局、分组、按键显示、编辑/恢复默认等实际存在的交互。
  • 建立快捷键 → 命令 → 实际动作映射,按 REA 证据确认支持范围。
  • 验收:展示按键与实际绑定一致;输入框、编辑器、弹层、独立窗口中的作用域和冲突处理正确;可修改项持久化。

状态:待 REA 拆解与命令绑定盘点。

13. 个性化页面与真实功能

  • 拆解原版页面、字段、说明、保存/启停等交互。
  • 将支持的个性化配置接入实际运行链路,确认在哪些新建/既有会话中生效。
  • 验收:修改与持久化有效;界面显示与实际行为一致;未接能力明确记录,不用占位控件宣称完成。

状态:待 REA 拆解。

14. 记忆与进化

  • 记录待拆解:入口、页面、数据、读写机制、会话生效方式及生命周期。

状态:待拆解,本轮不自动展开实施。

15. 智能体页面:团队管理

  • 拆解原版智能体设置页面,本轮仅对接“智能体团队管理”。
  • 根据 REA 证据梳理团队列表、详情及管理操作,将需要的真实存储/配置链路接入。
  • 验收:仅展示已确定范围;管理操作实际生效、重启后可恢复;团队管理与真实子代理调度的完成度分别记录。

状态:待 REA 拆解;范围限定为团队管理。

16. 移除助理与设备连接

  • 移除设置中的“助理”“设备连接”导航和页面入口。
  • 清理默认选中、内部导航、深链接/旧配置回退中的无效目标。
  • 验收:设置中不再出现这两项,旧导航目标不会导致空白页。

状态:已实现并通过真实窗口回归。

原版证据: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. 移除数据管理与应用管理

  • 移除设置中的“数据管理”“应用管理”导航和页面入口。
  • 清理相关无效导航与页面占位。
  • 验收:入口完全移除且其余设置导航正常;不删除已有用户数据或已安装内容。

状态:已实现并通过真实窗口回归(与 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 分支检测与会话上下文信息

  • 通过 REA 拆解 WorkBuddy 对话框中 Git 分支和会话上下文信息的显示位置、图标、字体、间距、提示及交互。
  • 根据当前任务绑定的工作目录检测 Git 仓库和实际分支;核对原版对非 Git 目录、未产生首次提交、detached HEAD、检测失败等状态的处理。
  • 明确原版“上下文信息”的实际字段、数据来源、更新时机和展示方式,再接入 YCode 的真实会话数据;上下文用量等字段如存在,必须区分真实统计与估算。
  • 验收:新建任务、已有会话、切换空间/任务、外部切换 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 差异与方案:具体问题、涉及模块、数据与交互边界。
  • 实现:修改文件、真实功能、占位或未接部分。
  • 验证:功能回归、实际窗口交互、浅色/深色、相关截图/几何/像素结果及限制。
  • 遗留:尚未解决的差异与下一步所需证据。