Files
ycode/docs/视觉对齐实测.md
T

663 lines
34 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WorkBuddy 界面实测数据(YCode 视觉对齐依据)
> 历史实测参照,尚需通过 REA 复核。现行目标是视觉、交互与功能 1:1;本文旧版分级中的“不做/不可能”不能用于缩减范围。后续所有原版采集使用 REA,禁止直接运行 `wb-*` CDP 脚本绕过。接手记录见 `接手状态-2026-10-15.md`。
**采集方式**:CDP(`WORKBUDDY_REMOTE_DEBUGGING_PORT=9223`)+ `scripts/wb-capture.mjs` / `scripts/wb-tree.mjs`
**采集环境**:WorkBuddy 37.10.3-42,Windows,视口 **1334 × 901**,DPR 1,zoom 1.000,浅色主题
**采集时间**:2026-10-15
**原始产物**:`%USERPROFILE%\.ycode\captures\`(含真实对话,不入库)
> 说明:以下均为**运行时 computed style 实测值**,不是静态 CSS 推断。
> 换窗口尺寸或字号档位需重新采集。
---
## 0. 参照文档与对齐分级
同项目源头文档已复制到 `docs/reference/`,不必再跳到别的仓库:
| 文档 | 用途 |
| --- | --- |
| `workbuddy-cdp.md` | **采集 SOP**(开启调试端口、采集流程、边界与常见问题) |
| `workbuddy-chat-layout.md` | 对话区布局实测(字号 token、宽度约束、滚动策略) |
| `workbuddy-composer-reference.md` | 输入区专项参考 |
| `ygent-ui-pixel-parity-feasibility-2026-10-14.md` | **像素级对齐可行性结论与分级目标** |
| `ygent-font-parity.md` | 字体与六档字号对齐 |
| `ygent-sidebar-parity.md` / `ygent-settings-parity.md` / `ygent-model-composer-parity.md` | 分区对齐记录 |
| `ygent-workbuddy-render-audit-2026-10-08.md` | 40 项逐项差异审计(含坐标/字库/间距) |
| `ygent-workbuddy-ai-gap-2026-10-14.md` | 能力差距盘点 |
| `ygent-chat-parity.md` | 对话区对标路线图 |
| **`ygent-chat-interaction-parity.md`** | **L3 动态交互对标与验收(必测序列、事件与渲染状态)** |
| `ygent-chat-stage-summary-2026-10-08.md` | 分阶段汇总 |
| `ygent-streaming-activity.md` | 流式活动播报 |
| `ygent-chat-edit.md` / `ygent-chat-approval-questions.md` | 消息编辑 / 审批提问 |
| `ygent-rich-content.md` / `ygent-attachments.md` | 富内容与附件 |
| `ygent-session-titles.md` / `ygent-shell-settings.md` | 会话标题 / 壳层设置 |
| `ygent-debug.md` | ygent 侧 CDP 调试 |
| `workbuddy-independent-conversations-2026-10-08.md` / `workbuddy-reference-import.md` | 独立会话与参照导入 |
| `ygent-岗位与项目团队方案.md` | 岗位/团队方案(后续扩展参考) |
### 对齐分级(直接采用其结论,避免重复踩坑)
| 级别 | 目标 | 可行性 |
| --- | --- | --- |
| **L1 几何对齐** | 同视口/同字号下关键锚点与尺寸 **≤1px** 取整误差 | ✅ 可达 |
| **L2 静态像素对齐** | L1 + 图标路径/颜色/阴影/滚动条 | ⚠️ 90%+,宿主原生控件除外 |
| **L3 动态交互一致** | 事件序列、折叠时机、滚动、取消/恢复 | ⏳ **真正的目标**,未完成 |
| **L4 全局 100% 像素一致** | 含宿主 chrome、平台入口、跨机器字形 | ❌ 不可能,不做 |
### 明确不做(沿用其约定)
- 不伪造消耗 / 模型元数据 / 产物计数以凑齐视觉
- 不复制平台商业入口(邀请卡、账号区)与无效操作按钮
- 不为对齐硬补高度
- 不把静态像素通过宣称为动态交互通过
- 不把任一端的绝对 y 差直接当作要修的 margin
### 已知的跨宿主差异(不可消除)
- 渲染引擎版本不同 → 默认 UA 样式、原生滚动条伪元素、字体回退顺序可能不同
- 系统字库为各机本地安装,不随项目分发 → 跨机器像素一致不成立
- 顶栏 chrome 与侧栏数据层级属宿主结构差,**不应为复制而破坏已对齐的内部坐标**
---
## 1. 顶层布局
| 区域 | 实测 |
| --- | --- |
| 菜单栏 | 高 **30px**(`.codebuddy-menubar`) |
| 左栏 | `.conversation-sidebar` x=0 y=30 **280 × 871** |
| 主内容区 | 宽 **1054px**(1334 − 280) |
| 右栏(详情面板) | `.detail-panel-container` **宽 1px** —— **默认收起** |
---
## 2. 左栏(侧栏)
| 元素 | 实测值 |
| --- | --- |
| 容器 `.conversation-list` | **280 × 871**,背景 **`rgb(247,247,247)` = `#f7f7f7`** |
| 头部 `.conversation-list-header` | 279 × 288,`padding: 0 12px 20px`,`gap: 2px`,flex column |
| 品牌行 `.conversation-list-logo-row` | 255 × **64**(min-height 40),`gap: 4px`,align center |
| 品牌标题 `.logo-*-title` | **14px / 700**,行高 22px,色 `rgba(0,0,0,.9)`,左边距 12px |
| 版本行 | 高 18,`padding-left: 12px`,`margin-top: -4px`,`gap: 6px` |
| 版本徽标 | **10px**,行高 18,`letter-spacing: .2px`,色 `rgba(0,0,0,.5)` |
| 次级按钮 `.wb-button--secondary` | 98 × **28**,`padding: 4px 6px 4px 8px`,`gap: 2px`,背景 `#f2f2f2`,色 `rgba(0,0,0,.7)`,边框 1px `#ebebeb`,**圆角 100px**,字号 **12px** / 500 |
| 导航按钮 `.conversation-list-tab-button` | 255 × **32**,`padding: 5px 12px`,`gap: 8px`,**圆角 8px**,图标 **16 × 16**,标签 **14px** / 行高 22 |
| Beta 徽标 | **10px**,大写,`padding: 0 5px`,边框 1px `#dedede`,圆角 100px,色 `rgba(0,0,0,.45)`,行高 16 |
| 分组标签 `.conversation-section-label` | 255 × **30**,`padding: 5px 12px`,圆角 8,背景 `#f7f7f7`,`gap: 4px` |
| 分组文字 | **12px / 600**,行高 20,色 `rgba(0,0,0,.5)`;形如「任务 (1)」 |
| 分组折叠箭头 | 14 × 14,色 `rgba(0,0,0,.3)` |
| 分组小按钮 | 20 × 20,圆角 4,色 `rgba(0,0,0,.5)` |
| 会话项 `.conversation-item` | 255 × **31**;内层 `.cb-agent-card` `padding: 5px 12px`、**圆角 8px** |
| 会话项标题 | **14px** / 行高 21,字重 400,色 `rgba(0,0,0,.9)`,左 24 |
| 会话项时间 | **11px** / 行高 20,色 `rgba(0,0,0,.5)`,**右对齐**(同一行,非第二行) |
---
## 3. 对话区
| 元素 | 实测值 |
| --- | --- |
| `.cr-message-list` | x=280 y=94 **1054 × 639**,基础字号 13px |
| `.cr-message-list__content` | **宽 832**(max-width 832),**`padding: 0 40px`** → 正文列 **752** |
| `.cr-agent` | **752 宽**,flex column,色 `rgba(0,0,0,.9)`,13px / 行高 19 |
| `.cr-agent__header` | 752 × **24.5**,**`margin-bottom: 12px`**,**`gap: 10px`**,align center |
| `.cr-markdown` | **17.143px / 行高 30.0002px** |
| `.cr-markdown p` | **15.0001px / 26.2502px**,`margin-bottom: 12px` |
| `.cr-self-message` | flex row,**`justify-content: flex-end`**,align-items flex-start,**`gap: 8px`** |
| `.cr-self-bubble` | `min-width: 40px`,`max-height: 310px`,**`padding: 8px 12px`**,**`gap: 4px`**,背景 **`rgb(242,242,242)`**,**`border-radius: 16px 16px 0`**,**13px / 19px** |
| `.cr-collapse__header` | 高 **26.25**,`gap: 8px`,`max-width: 100%`,色 **`rgba(0,0,0,.5)`**,**15px / 26.25px** |
**主文字色是 `rgba(0,0,0,.9)`,不是 `#000`。**
---
## 4. 输入区(最容易做错的地方)
| 元素 | 实测值 |
| --- | --- |
| `.cr-input-box` | x=415 y=733 **784 × 134**,`z-index: 16` |
| `.cr-input-box__main` | **`border-radius: 24px`**,`gap: 4px`,flex column |
| `.cr-input-container` | **背景 `rgb(255,255,255)`(白底,不是渐变)**;边框 **1px `rgb(230,230,230)`**;**圆角 24px**;`min-height: 96px`;`padding-top: 12px` |
| 阴影 | **双层**:`rgba(0,0,0,0.03) 0 16px 32px -8px, rgba(0,0,0,0.06) 0 2px 4px -4px` |
| 聚焦态 | 边框色变 `rgba(0,0,0,.15)` |
| `.cr-input-editor-host` | 782 × **64**(min-height 64),**`padding: 0 16px`** |
| 输入文字 | **14px / 行高 21** |
| `.cr-input-toolbar` | 782 × **56**,**`padding: 12px`**,`gap: 8px`,**`justify-content: space-between`**,min-height 32 |
| `.cr-input-toolbar__right` | 286 × **32**,`gap: 4px`,背景 `#fff` |
| `.cr-send-button` | **32 × 32**,**`border-radius: 50%`** |
几何关系:`12 (padding-top) + 64 (编辑区) + 56 (工具栏) ≈ 134` ✓;输入框 784 = 832 − 48。
> ⚠️ 注意区分两个 composer:
> `--wb-bg-input-slot-gradient`(`linear-gradient(180deg,#ebebeb,#f5f5f5)`)属于 **`.wb-home-composer__input-slot`(首页新建对话)**;
> **会话内输入框 `.conversation-input__box` 明确定义 `background: 0 0`(透明)+ 白底容器 + 描边**。两者不可混用。
---
## 5. 右栏:详情面板(展开态实测)
> 采集时视口变为 **1638 × 901**(用户展开面板后窗口更宽)。CSS 中 `.detail-panel-container` 为 `width: 100%`,
> 实际宽度由外层布局决定,**面板可拖拽调宽**;本次实测宽 **647px**。
| 元素 | 实测值 |
| --- | --- |
| `.detail-panel-container` | 647 × 871,**`border-left: 1px solid #ebebeb`** |
| `.sidebar-next` | 646 × 871,背景 **`#fff`**,**`border-radius: 12px 12px 0 0`** |
| `.detail-layout` | flex column,白底 |
| `.detail-layout__top` | 646 × **64**(min-height 64) |
| `.sidebar-next-main-header` | 646 × 64,**`padding: 0 16px`**,**`justify-content: space-between`**,`gap: 4px`,白底 |
| `.artifact-tabs` | 高 32,`gap: 4px` |
| `.artifact-tab`(当前项) | 240 × **32**(min-width 80 / max-width 240),**`padding: 0 8px`**,`gap: 4px`,**背景 `#f2f2f2`**,**圆角 8px**,**14px / 600**,行高 22 |
| `.artifact-tab__icon` | **16 × 16**,色 `rgba(0,0,0,.7)` |
| `.artifact-tab__close` | **14 × 14**,色 `rgb(119,119,119)`,`border-radius: 999px` |
| `.artifact-tabs__add` | **32 × 32**,圆角 8,图标 16 × 16 |
| 顶栏右侧操作 | 两个 **32 × 32** 按钮(`gap: 4px`),圆角 **6px**,图标 16 × 16,色 `rgba(0,0,0,.7)` |
| `.detail-layout__content` | 646 × 807 |
| `.detail-main__body` | 背景 `#fff` |
---
## 6. 图标采集与替换
真实界面的图标都是内联 SVG(`<svg><path fill="currentColor">`),不是字体图标(codicon 类名只在少量 VS Code 继承处出现)。
**采集链路**:`scripts/wb-icons.mjs`(CDP 取指定节点的 SVG outerHTML → `icons.json`)→ `scripts/gen-icons.mjs`(生成 `src/renderer/src/components/icons.tsx`)。
| YCode 组件 | 来源 | 尺寸 |
| --- | --- | --- |
| `IconNewTask` | 侧栏「新建任务」 | 16 |
| `IconChevronDown` | 「发现应用」下拉箭头 | 14 |
| `IconFilter` | 侧栏顶栏筛选 | 16 |
| `IconCompact` / `IconSectionAdd` | 分组操作 | 16 |
| `IconPlus` | 输入区「+」 | 16 |
| `IconShield` | 「允许完全访问」盾牌 | 16 |
| `IconChevronSmall` | 模型选择器箭头 | 12 |
| `IconMic` | 语音输入 | 16 |
| `IconStop` | 停止(圆底 + 圆角方块) | 32 |
| `IconTab` | 详情面板页签 | 13 |
| `IconClose` | 页签关闭 | 14 |
| `IconChevronRight` | 「查看所有产物」 | 8 |
| `IconPanelCollapse` / `IconPanelRight` | 面板收起 / 右侧栏 | 16 |
> ⚠️ **来源与替换要求**:`icons.tsx` 中的矢量路径取自 WorkBuddy 运行时界面,
> 属于第三方产品界面资产,**仅作为当前阶段 1:1 还原的临时占位**(用户明确要求"先用 workbuddy 的,先 1:1 还原,然后再改造")。
> **正式发布前必须整体替换**为自有图标或开源图标集。
> 对比:VS Code codicon 为 MIT 许可,可长期使用;本项目的 `icons-extra.tsx` 是自有矢量,可保留。
---
## 7. 标题栏与菜单栏(实测)
| 元素 | 实测值 |
| --- | --- |
| 顶部条带 | **高 30px**,通栏 |
| 左侧操作区 | x=0 宽 **92px**(侧栏顶部操作) |
| `.codebuddy-menubar` | x=92 宽 227,`padding-left: 4px`,flex row,align center |
| 菜单按钮 `.menubar-menu-button` | 高 **30px** |
| 菜单标题 `.menubar-menu-title` | 高 24,**`padding: 2px 8px`**,**圆角 4px**,**12px** / 行高 20,色 **`rgb(139,148,158)` = `#8b949e`** |
| 菜单项 | **编辑(E) / 窗口(W) / 帮助(H) / 关于(A)** |
| 右侧窗口控制区 | x=2422 宽 **138px**(约 46px × 3:最小化 / 最大化 / 关闭) |
> 这是**自绘菜单**(VS Code 风格),不是系统原生菜单。YCode 已改为
> `frame: false` 无边框窗口 + 渲染进程自绘标题栏(`components/TitleBar.tsx`),
> 菜单项均接真实行为(编辑用原生 role、窗口用 IPC、帮助/关于打开对应对话框)。
**无边框窗口注意**:条带设 `-webkit-app-region: drag` 负责拖动窗口,
按钮与菜单设 `no-drag`;主进程需 `Menu.setApplicationMenu(null)` 去掉原生菜单。
---
## 8. 空态与免责声明(实测自 CSS)
> 说明:采集时 WorkBuddy 处于会话中,页面无空态元素,以下取自**安装包 CSS 规则**(非冒烟实测),
> 待用户切到新建对话页后可做一次 `wb-tree` 复核。
### 空态 `.claw-agent-chat-pane .colleague-chat-empty--profile`
| 元素 | 实测值 |
| --- | --- |
| 容器 | flex column,居中,`gap: 24px`(spacing-7),`padding: 0 16px`(spacing-5),text-align center |
| 头像 | **64px**(spacing-12),`border: 1px solid` 边框色 `--wb-color-border-secondary` = **`#ebebeb`**,**`border-radius: 18px`**(radius-lg 8 + md 6 + sm 4),`margin-bottom: 16px` |
| 标题 | **24px / 34 / 500**,色 text-primary |
| 说明 | **`max-width: 324px`**,**12px / 18**,色 text-tertiary |
| 快捷提问容器 | flex column,`align-items: flex-start`,`gap: 16px`(spacing-5) |
### 快捷提问胶囊 `.conversation-embed-empty__quick-prompt`
`min-height: var(--wb-size-control-md)`、**`padding: 10px 16px`**、**`border-radius: 9999px`**、
背景 `--wb-color-bg-primary-default` = gray-3 **`#f2f2f2`**、文字 `color-mix(text-primary 90%, transparent)`、
**14px**、行高 21、`white-space: nowrap`、`border: 0`。
### 免责声明 `.claw-local-empty__disclaimer` / `.conversation-embed-empty__disclaimer`
`padding: 8px 0`、text-align center、色 `--wb-color-text-disabled` = **`rgba(0,0,0,.3)`**、
**12px**、`line-height: 20px`、`user-select: none`、`pointer-events: none`。
文案:**「内容由 AI 生成,请核实重要信息」**。
### 输入区(空态变体)
`.claw-local-empty__input-area`:`width: 100%`、`max-width: 832px`、`padding: 0 16px`、`margin: 0 auto`。
`.conversation-embed-empty__input-area`:`--cb-input-content-radius: 22px`、`--cb-input-main-area-radius: 22px`、
`--cb-content-background: transparent`。
---
## 9. 间距阶(编号已与对标对齐)
| 编号 | 值 | 编号 | 值 |
| ---: | ---: | ---: | ---: |
| spacing-1 | 2px | spacing-7 | 24px |
| spacing-2 | 4px | spacing-8 | 32px |
| spacing-3 | 8px | spacing-9 | 40px |
| spacing-4 | 12px | spacing-10 | 48px |
| spacing-5 | 16px | spacing-12 | 64px |
| spacing-6 | 20px | | |
> YCode 的 `--yc-space-N` **编号与之一一对应**,翻译对方 CSS 时无需换算。
---
## 10. 与 YCode 的差距(本轮已修 / 待修)
### 已修正
- **空态**:改为「64px 圆角头像(18px 圆角 + 1px `#ebebeb` 边框) + 标题 24px/500 + 说明 12px/18 max-width 324px + 胶囊快捷提问(`padding: 10px 16px`、圆角 9999、底色 `#f2f2f2`)」
- **输入区下方免责声明**:新增「内容由 AI 生成,请核实重要信息」(12px、居中、色 `rgba(0,0,0,.3)`、`padding: 8px 0`)
- **间距阶**:`--yc-space-N` 编号对齐对标 spacing 阶(原实现整体错位一档)
- **标题栏 + 菜单栏**:Electron 原生菜单 → **无边框窗口(`frame: false`)+ 自绘 30px 条带**;菜单 `编辑(E)/窗口(W)/帮助(H)/关于(A)`,12px / `#8b949e` / 标题 `padding: 2px 8px` / 圆角 4;右侧 46px×3 窗口控制
- 主文字 `#000` → **`rgba(0,0,0,.9)`**
- 侧栏背景 `#f2f2f2` → **`#f7f7f7`**
- 输入框:错误地改成渐变 → **改回白底 + 1px `#e6e6e6` + 圆角 24 + 双层阴影**,宽度 832 → **784**,`min-height` 96
- 侧栏列表项:`padding 8px 20px` + 13px + 两行堆叠 → **31px 单行,`padding 5px 12px`,圆角 8,标题 14px 左 + 时间 11px 右**
- 侧栏结构:改为「品牌行(64px) + 版本行 + 导航项 + 分组标签(30px) + 列表项」
- 右侧详情面板:默认显示 → **默认收起**(对齐真实默认态);展开态已按实测改为「顶栏 64px + 页签 32px/圆角8/底色 #f2f2f2/14px600 + 右侧 32×32 操作按钮(圆角6)」,面板白底 + 上圆角 12 + `border-left 1px #ebebeb`
- **详情面板拖拽调宽**:新增 `.app-shell__resizer` 手柄(0 宽网格列 + 6px 命中区伪元素,hover/拖拽显示品牌色指示线);宽度状态在 `App.tsx`,以 `--yc-layout-right` 注入网格;范围 **320–900px**,默认 **647px**(对齐实测值)。面板收起时手柄一并卸载。已实测:左拖 150px → 797px、右拖收窄、越界夹在 320 / 900
### 待补(需要真实界面数据)
- 侧栏顶部完整导航项(助理 Beta / 专家·技能·连接器 / 定时任务 / 资料库 / 更多)—— YCode 目前只实现「新建对话」与「任务」分组,不伪造未实现入口
- 权限选择器、麦克风等输入区工具项
- 图标集:真实界面用 SVG 图标(16×16),YCode 已接入采集图标(见第 6 节,正式发布前需替换)
---
## 10.1 对话区顶栏(补充实测)
| 元素 | 实测值 |
| --- | --- |
| `.workbuddy-topbar` | 高 **64px**,**`padding: 0 16px`**,align-items center,**justify-content: space-between** |
| `.workbuddy-topbar-options` | 高 32px,位于顶栏内部左右两端 |
| 顶栏下方首行 | `.cr-collapse--summary`(「已完成 3m2s」行),字号 13px |
> 据此 YCode 的 `.chat-head` 由自拟的 `padding: 12px 20px 10px` 改为
> **高 64px / `padding: 0 16px` / 两端对齐**,与实测一致。
---
## 11. 完整 design token 表(对标体系镜像)
来源:安装包 `tokens-Df-i-zsO.css` / `contexts-BkjwYSZT.css`。
**YCode 的 `--yc-*` 编号与对标 `--wb-*` 一一对应**,翻译对方 CSS 时是机械映射。
### 11.1 间距阶 `--wb-spacing-N`
| N | 值 | N | 值 |
| ---: | ---: | ---: | ---: |
| 1 | 2px | 7 | 24px |
| 2 | 4px | 8 | 32px |
| 3 | 8px | 9 | 40px |
| 4 | 12px | 10 | 48px |
| 5 | 16px | 12 | 64px |
| 6 | 20px | | |
### 11.2 字号阶 `--wb-font-size-N`
| N | 值 | N | 值 |
| ---: | ---: | ---: | ---: |
| 1 | 8px | 7 | 18px |
| 2 | 10px | 8 | 20px |
| 3 | 12px | 9 | 24px |
| 4 | 13px | 10 | 28px |
| 5 | 14px | 11 | 32px |
| 6 | 16px | | |
行高:`4:16` `4-2:18` `5:20` `5-2:22` `6:24` `7:28` `8:32` `10:40`
字重:`400` `500` `600` `700`
### 11.3 控件尺寸阶 `--wb-size-N`
| N | 值 | N | 值 |
| ---: | ---: | ---: | ---: |
| 3 | 12px | 9 | 36px |
| 3-2 | 14px | 10 | 40px |
| 4 | 16px | 11 | 44px |
| 5 | 20px | 12 | 48px |
| 6 | 24px | 13 | 52px |
| 7 | 28px | 14 | 56px |
| 8 | 32px | 17 | 68px |
语义别名:`control-sm 24` `control-ms 28` `control-md 32` `control-lg 40` `control-xl 48` `control-2xl 56`
### 11.4 圆角阶 `--wb-radius-*`
`none 0` `xs 2` `sm 4` `md 6` `lg 8` `xl 12` `2xl 16` `3xl 20` `4xl 24` `full 9999`
### 11.5 语义字号(体例)
| 角色 | 字号 | 行高 | 字重 |
| --- | ---: | ---: | ---: |
| body | 14 | 22 | 400 |
| body-strong | 14 | 22 | 600 |
| caption | 12 | 18 | 400 |
| code | 13 | 20 | 400 |
| h1 | 24 | 32 | 600 |
| h2 | 20 | 28 | 600 |
| h3 | 16 | 24 | 600 |
| display | 32 | 40 | 700 |
### 11.6 语义字号(对话流 chatflow,对标独立一套)
| 角色 | 字号 | 行高 |
| --- | ---: | ---: |
| h1 | 21 | 37 |
| h2 | 19 | 33 |
| h3 | 17 | 30 |
| h4 | 15 | 26 |
| p1 | 15 | 26 |
| p2 | 13 | 23 |
| p3 | 11 | 19 |
### 11.7 全局字号档位(六档)
机制:级别存 localStorage,同时更新「Markdown 根字号」与「全局字号偏移」;
偏移经 `clamp(-1, global, 2)` 夹取后作用于各语义 token。
| 档位 | `--cb-md-root` | 全局偏移 |
| ---: | ---: | ---: |
| 1 | 16px | −1 |
| 2(默认) | 17.143px | 0 |
| 3 | 18.286px | +1 |
| 4 | 20.571px | +2 |
| 5 | 22.857px | +2 |
| 6 | 26.286px | +2 |
快捷键:`Ctrl+-` 降档、`Ctrl++` 升档、`Ctrl+0` 恢复默认。
> **YCode 的实现差异**:因组件样式已全部 token 化,**不需要**像对标那样扫描样式表注入覆盖规则;
> 只改 `--yc-font-offset-global` 与 `--yc-md-root` 两个根变量即可全局生效。
> 未实现对标的 `.cb-font-size-fixed` 例外类——目前没有固定字号的控件需要豁免。
### 11.8 字体族
- 正文:`'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif`
- 等宽:`ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace`
### 11.9 实测常量(不在标准阶内,对标自身也硬写)
`scrollbar 6px`、`菜单最小宽 200px`、`窗口控制图标 10px`、`侧栏项高 31px`、`侧栏项纵向 padding 5px`、
`行高 19 / 21 / 24.5 / 26.25`、`列表缩进 21.45px`、`气泡 max-height 310px`、`工具卡正文 max-height 320px`、
`空态头像圆角 18px`、`空态说明 max-width 324px`、`输入 min-height 96px`、`选项卡 80–240px`、
`对话框 560 / 420px`
---
## 12. REA 拆解证据与工具边界
### 12.1 归档级拆解(已执行)
```
open_binary app.asar → format=asar, kind=archive
sha256 035254bc…729de918
inspect_artifact (record-and-continue) → 12253 节点 / 17356 occurrence / 14476 文件 / 2880 目录
45 处完整性矛盾(观察值标记为 untrusted)
```
**完整性矛盾**:首条为 `cli/bin/native/win32-x64/job-object.node`——声明的 sha256 与伴随文件不符。
REA 指出这是常见现象(打包工具会在写入归档后对未打包的原生二进制签名或裁剪),
但该模块正是**绑定子进程树的 Windows Job Object 原生实现**,与 GPU 沙箱受限环境的观察一致,
值得单独留意。
### 12.2 工具边界(重要)
REA 的 CDP 浏览器 provider **只枚举 HTTP(S) 页面目标**。
WorkBuddy 的渲染器是 `file://` URL,因此:
```
list_browser_targets http://127.0.0.1:9223
→ targets: [] excluded: { unsupported_url: 1 }
→ limitations: "All page targets with supported HTTP(S) URLs are listed."
```
**结论:REA 的浏览器类工具(`inspect_web_page` / `capture_web_screenshot` /
`compare_web_captures`)目前无法观察 WorkBuddy 主窗口。**
对 `file://` 渲染器的 Electron 应用,实测采集需要直接用 CDP(本项目的
`scripts/wb-capture.mjs` / `wb-tree.mjs` / `wb-icons.mjs`)。
YCode 开发态走 `http://localhost`,REA 浏览器工具对其可用。
同理,REA 的二进制 provider(Ghidra / IDA / Hopper)在本机均未配置,
且不接归档目标("Ghidra v1 imports executable targets, not archive targets"),
因此**无字符串检索能力**;token 表据以从归档内 CSS 文件直接提取。
---
## 13. 外围壳层完整结构(第二轮实测)
采集脚本:`scripts/wb-shell.mjs`(输出侧栏完整子树 + 外围元素清单)
> **重要发现**:侧栏宽度**不是常量**。两次采集分别为
> **280px(@1334 窗口)** 与 **240px(@1276 窗口)**,说明该宽度本身可变(可拖拽或随窗口变化)。
> YCode 取 280px 作为展开默认值,并支持收起为 44px 窄轨。
### 13.1 品牌区
| 元素 | 实测值 |
| --- | --- |
| `.conversation-list-logo-row` | 215×**64**(min-height 40),`gap: 4px`,align center |
| `.conversation-list-brand-block` | flex column,align flex-start,`padding-top: 4px` |
| `.conversation-list-brand-line` | 90×22,`padding-left: 12px`,`gap: 8px` |
| 品牌标题 | **14px / 700** / 行高 22,色 `rgba(0,0,0,.9)` |
| `.conversation-list-version-row` | 40×18,`padding-left: 12px`,`margin-top: -4px`,`gap: 6px` |
| 版本徽标 | **10px** / 行高 18,`letter-spacing: .2px`,色 `rgba(0,0,0,.5)` |
| 「发现应用」按钮 | 98×**28**,`padding: 4px 6px 4px 8px`,`gap: 2px`,背景 `#f2f2f2`,色 `rgba(0,0,0,.7)`,边框 1px `#ebebeb`,**圆角 100px**,**12px / 500** / 行高 20 |
| └ 左侧图标 / 右侧箭头 | 各 14×14;箭头色 `rgba(0,0,0,.3)`;文字 12px/400 |
### 13.2 导航项(实测 `.conversation-list-tab-button`)
| 项 | 备注 |
| --- | --- |
| 新建任务 | 主入口 |
| 助理 | 带 **Beta** 徽标 |
| 专家·技能·连接器 | |
| 定时任务 | |
| 资料库 | |
| 更多 | 右侧附 **「灵感」** 副文案 |
尺寸:**215×32**,`padding: 5px 12px`,`gap: 8px`,**圆角 8px**,图标 **16×16**,标签 **14px** / 行高 22。
- Beta 徽标:**10px / 500** / 行高 16,`padding: 0 5px`,边框 1px `#dedede`,圆角 100px,色 `rgba(0,0,0,.45)`
- 「灵感」副文案:**11px** / 行高 20,色 `rgba(0,0,0,.5)`
### 13.3 分组
| 元素 | 实测值 |
| --- | --- |
| 分组标签 `.conversation-section-label` | **215×30**,`padding: 5px 12px`,`gap: 4px`,背景 `#f7f7f7`,**圆角 8px**,**`position: sticky`** |
| 分组文字 | **12px / 600** / 行高 20,色 `rgba(0,0,0,.5)`,形如「任务 (1)」「空间 (2)」 |
| 折叠箭头 | 14×14,色 `rgba(0,0,0,.3)`,**默认 opacity 0**(hover 显现) |
| 分组操作按钮 | **20×20**,圆角 4px,色 `rgba(0,0,0,.5)`,默认 opacity 0 |
| 空间分组头 `.collapsible-section header` | **215×34**,`padding: 5px 12px`,`gap: 4px`,背景 `#f7f7f7`,圆角 8px |
| └ 图标 / 名称 | 16×16 / **14px** 行高 22 |
### 13.4 底部账号区(实测)
| 元素 | 实测值 |
| --- | --- |
| `.conversation-list-footer` | 239×**57**,`padding: 8px 12px 8px 8px`,**`border-top: 1px solid #e6e6e6`** |
| `.user-menu` | 219×**40**,`gap: 4px` |
| `.user-menu-trigger` | 147×40(min-height 40),`padding: 6px 8px`,**圆角 8px**;内部 `gap: 10px` |
| 头像 | **28×28**,`border-radius: 50%`,背景 `#ebebeb`,**12px / 500** |
| 用户名 | **12px / 600**(`.user-menu-trigger-name` 高 12) |
| 通知按钮 | **32×32**,圆角 6px,色 `rgba(0,0,0,.7)`,图标 16×16 |
| 未读角标 `.msg-center-badge` | **14×14**,背景 **`#f64041`**,白字,`border-radius: 7px`,**9px / 500** / 行高 14,`padding: 0 3px` |
| 小程序按钮 | 32×32,圆角 6px,图标 16×16 |
### 13.5 输入区补充
`.cr-permission-setting`(「允许完全访问」):**132×32** —— 权限选择器实为 32px 高控件。
---
## 14. Mock 数据约定
用户明确要求「登录账号先用 mock 写死数据」。约定:
| 文件 | 内容 | 替换时机 |
| --- | --- | --- |
| `src/renderer/src/mock.ts` | 账号、通知、空间、导航项清单 | 接入真实数据源时整体替换 |
**约束(已落实)**:
1. 所有导出一律 **`MOCK_` 前缀**,便于全局检索替换
2. 使用处**必须在 UI 上显式标注**为示例数据:
- 通知面板底部:「示例数据 · 尚未接入消息中心」
- 设置 → 账号:「账号信息为 mock 写死数据,尚未接入登录」
- 账号按钮 `title`:「账号为 mock 数据」
3. **未实现的入口不做假交互**:点击给出 2 秒临时提示(如「『专家·技能·连接器』尚未实现」),
不伪造可用状态。这是对外围壳层「配齐」与「不虚构能力」两条要求的折中。
---
## 15. 首页 / 新建任务页(空态)—— 冒烟实测
> 采集方式:用户批准「方案 B」后,由 CDP 直接点击侧栏「新建任务」切换页面后采集。
> 脚本:`scripts/wb-probe.mjs`(按文案定位,应对 CSS Module 哈希类名)、`scripts/wb-home.mjs`、`scripts/click-shot.mjs`
> 参考截图:`docs/screenshots/wb-home-reference.png`
### 15.1 页面构成(自上而下)
1. 大标题:**「WorkBuddy, 我帮你」**
2. 分段控件 `.wb-segmented`:[办公][开发][设计]…(场景切换)
3. **分类胶囊行** `.quick-actions__list`
4. **输入框**(比会话内更高)
5. 工作目录 + 权限行(📁 YCode ⌄ / 🔒 允许完全访问 ⌄)
6. **建议条目列表**(整行胶囊 + 右上箭头)
7. 底部免责声明:内容由 AI 生成,请核实重要信息
### 15.2 实测尺寸
| 元素 | 实测值 |
| --- | --- |
| 分类行 `.quick-actions__list` | **宽 680**,flex row,**`gap: 8px`**,align center |
| 分类胶囊 `.quick-actions__item` | **高 32**,`padding: 0 12px`,`gap: 4px`,背景 **`#f7f7f7`**,色 `rgba(0,0,0,.9)`,边框 **1px `#dedede`**,**`border-radius: 100px`**,**14px / 400** |
| 分段控件 `.wb-segmented` | 高 32,**`border-radius: 9999px`**,`gap: 2px` |
| 分段项 `.wb-segmented__item` | **72×32**,`padding: 5px 12px`,`gap: 4px`,圆角 9999px,**14px / 600**,阴影 `rgba(0,0,0,.05) 0 1px 4px 0` |
| **首页输入编辑区** `.cr-input-editor-host` | **798 × 74**,`min-height: 74px`,`padding: 0 16px` |
| 输入占位符 | 15px / 26.25px,色 `rgba(0,0,0,.9)`;文案「今天帮你做些什么?@ 添加上下文,/调用技能与指令」 |
> **注意**:首页编辑区 **74px**,会话内是 **64px** —— 两者不同,不能共用。
### 15.3 分类清单(实测)
`办公` `开发` `设计` `创作文档` `金融服务` `分析数据` `制作PPT` `深度研究` `策划方案` `定时任务`
> YCode 已按此实现分类行(`--yc-*` token 化),点击分类把对应提示词填入输入框(真实可用,
> 提示词为自拟,非对标文案)。
### 15.4 与旧实现的差异(已修正)
旧空态是「56px 头像 + 大标题 + 竖排胶囊」——那是**从 CSS 规则推断**的 `.colleague-chat-empty--profile`,
**与真实首页不符**。真实首页没有头像,改为「大标题 + 横排分类 + 建议列表」。已按实测重写。
### 15.5 垂直结构精测(第三轮,`scripts/wb-home-layout.mjs` + `wb-home-parts.mjs`)
> 本轮发现第一轮实现**整体结构错误**:我把输入框放页面底部、10 个分类挤一行。
> 真实首页是**居中内容列**,输入框在页面中部、建议列表在其下方。
| 区块 | 实测 rect(视口 1276×901) |
| --- | --- |
| 标题 `h1.wb-home-header__title` | `[614, 159, 278, 42]` —— **30px / 600 / 行高 42**,色 `rgb(0,0,0)`,`gap: 10px` |
| 分段控件 `.wb-segmented` | `[643, 219, 220, 32]` —— 居中于内容列(中心 753) |
| 分类行 `.quick-actions` | `[353, 317, 680, 32]` —— **左对齐于内容列** |
| 输入框外层 `.wb-home-route__input-box` | `[353, 365, 800, 184]`,flex column **`gap: 12px`** |
| └ 白框 `.cr-input-container` | `[353, 365, 800, 144]`,圆角 24,`padding-top: 12`,白底 + 双层阴影 |
| └ 编辑区 `.cr-input-editor-host` | `[354, 378, 798, 74]`,`padding: 0 16px` |
| └ 工具栏 `.cr-input-toolbar` | `[354, 452, 798, 56]`,`padding: 12px`,space-between |
| 权限项 `.cr-permission-setting` | `[461, 513, 132, 32]`,圆角 999px,**13.33px**,色 `rgb(180,44,63)` |
| 建议项 `.wb-home-prompt-list__row` | `[353, 601, 238, 38]` 起 —— **高 38 / 圆角 9999 / 白底 / 边框 1px `#e6e6e6` / 14px**,**行距 50**(38 + 12),左对齐 |
**垂直间距**:标题底 201 → 分段 219 = **18**;分段底 251 → 分类 317 = **66**;
分类底 349 → 输入 365 = **16**;输入底 549 → 建议 601 = **52**。
**结构结论**:
1. 内容列 **x=353、宽 800**,整体居中(中心 753 ≈ 内容区中心 758)
2. **输入框不是底部停靠**,在页面中部;建议列表在其下方
3. 分类分两处:**分段控件 3 个(办公/开发/设计)** + **胶囊行 7 个**,不是 10 个一行
4. 输入框下方有「工作目录 + 权限」行,**在输入框外层容器内部**(184 = 144 + 40)
5. 白框 **144 = 12 + 74 + 56 + 2,纵向无 gap**(YCode 早期实现误加 4px gap → 148,已修)
6. 会话内输入框 **134 = 12 + 64 + 56 + 2**,同为无 gap
**已实现**:`ChatPane` 拆为 `composer`(两种变体共用,`data-variant` 区分编辑区高度)+
`composerFooter`(工作目录/权限行);首页走 `.home` 布局,会话走原底部停靠布局。
分段控件用前 3 个分类,胶囊行用后 7 个,与对标结构一致。
---
## 16. OMP 内核接入(底层循环替换)
**架构决策(用户拍板)**:YCode 从「自建网关 + 自写 agent 循环」转为 **OMP 前端壳**。
### 16.1 事实
| 项 | 值 |
| --- | --- |
| OMP 可执行文件 | `C:\Users\Administrator\AppData\Local\Ygent\omp.exe`(随 Ygent 安装) |
| 版本 | `omp/18.6.3` |
| 协议 | `--mode rpc`:stdio 上的 **newline-delimited JSONL**,启动先发 `ready` 帧,单帧上限 1 MiB |
| Profile | `~/.omp/profiles/ycode/agent/models.yml`(独立 profile,不动用户 ygent 配置) |
| 上游 | `https://api.iiiii.run/v1`,**`api: openai-responses`**,模型 `gpt-6-luna/astra/6.1-sol` |
### 16.2 实现
| 文件 | 职责 |
| --- | --- |
| `src/main/omp.ts` | sidecar 管理:spawn + JSONL 解析 + 命令关联(id)+ ready 等待 + 退出清理;`detectOmpPath()` 探测可执行文件 |
| `src/main/agent/omp-turn.ts` | OMP 事件 → YCode `ChatEvent` 映射(渲染进程零改动) |
| `src/main/ipc.ts` | **会话 → 内核进程** 池;会话删除时释放进程;OMP 不可用时**回退自建循环** |
**事件映射**:
| OMP 事件 | YCode ChatEvent |
| --- | --- |
| `message_update.assistantMessageEvent.type = "text_delta"` | `text` |
| `tool_execution_start` / `_update` / `_end` | `tool_call` / `tool_result` |
| `agent_end`(`yielded !== false`) | `done` |
| `prompt_result.status = "error"` / `notice(level=error)` | `error` |
| abort | `cancelled` |
**踩过的坑**:
1. `spawn` 报 `ENOENT` 但可执行文件明明存在 —— 是 **cwd 不存在**(工作目录为空字符串)。
spawn 的 `cwd` 与可执行路径无关,必须单独兜底(现回退到用户主目录)。
2. OMP 工具事件载荷文档未给全 → `readToolInfo()` 做**多形状兼容解析**
(`toolCall/call/tool/data` 逐层找 `id/name/arguments`)。
### 16.3 端到端验证
```
发送「只回复两个字:收到」 → 回复「收到」(omp.exe 被拉起)
发送「用 list_dir 列出工作目录文件」 → 3 张工具卡:glob / read / bash(OMP 原生工具)
全部 ok;回复「工作目录根目录下有 12 个文件。」
```
> 注意工具名是 **OMP 自己的工具集**(glob/read/bash),不再是 YCode 的 `list_dir/read_file`
> —— 内核真正接管。YCode 自带的 `agent/tools.ts` 在 OMP 可用时不再使用(保留代码作回退)。
### 16.4 遗留
- 取消链路已接(`abort`),但未做端到端验证
- 工具事件载荷的解析是**容错**的,若 OMP 未来改字段名可能拿不到 name/args(UI 会显示 `tool`)
- OMP 进程按会话存活:长会话会常驻一个 omp.exe;会话删除时释放
- `~/.omp/profiles/ycode` 里的 key 有额度限制,过期后回合会失败(届时换 key 即可)