feat(sidebar): replicate WorkBuddy account menu (TODO 09)

This commit is contained in:
xiaohan.yuan committed 2026-10-17 07:57:17 +08:00
1 parent 6173a77918
commit cb8a7e6466
9 files changed
+1035 -6

No files matched your search

+39 -5
View File
@@ -258,12 +258,46 @@
### 09. 账户菜单与页面
- [ ] 通过 REA 对齐点击当前登录账户后的入口、弹层位置、布局、交互及页面风格。
- [ ] 内容按用户指定范围:头像与名称、套餐、余额、升级套餐、购买积分、设置、外观、记忆与进化、检查更新、退出登录。
- [ ] 逐项盘点现有数据和功能,区分可直接接入、待实现与依赖服务端;不以虚构余额、套餐或成功提示代替真实能力。
- [ ] 验收:已接功能实际生效,弹层关闭与导航返回正常,浅色/深色一致;内容不要求逐项照搬原版账户菜单。
- [x] 通过 REA 对齐点击当前登录账户后的入口、弹层位置、布局、交互及页面风格。
- [x] 内容按用户指定范围:头像与名称、套餐、余额、升级套餐、购买积分、设置、外观、记忆与进化、检查更新、退出登录。
- [x] 逐项盘点现有数据和功能,区分可直接接入、待实现与依赖服务端;不以虚构余额、套餐或成功提示代替真实能力。
- [x] 验收:已接功能实际生效,弹层关闭与导航返回正常,浅色/深色一致;内容不要求逐项照搬原版账户菜单。
状态:待 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 之外的其他关闭路径(滚动、窗口失焦等)未采集。
## 设置