chore: establish YCode replication baseline and TODO

This commit is contained in:
xiaohan.yuan committed 2026-10-17 02:43:49 +08:00
commit 6dc59f89ba
1118 files changed
+196002

No files matched your search

+305
View File
@@ -0,0 +1,305 @@
# 2026-10-14
## WorkBuddy AI「1:1 复刻」可行性评估(REA)
- 输入:`C:\Users\Administrator\code\ygent\docs\WorkBuddy-AI-实现分析报告.md`
- 产出:`C:\Users\Administrator\code\ygent\docs\WorkBuddy-AI-复刻可行性评估.md`
- 结论:**不能 1:1 复刻**,四重封死——法律(专有代码)+ 原生二进制(≈360MB,本机无 Ghidra/Hopper/IDA,无法反编译)+ 压缩 JS(0 个 sourceMappingURL,`.js.map` 无 sourcesContent,无 .ts 源)+ 云依赖(本地不推理,排除云后能力归零)。
- 关键实测数据(务必备查):
- 安装总体积 ≈935MB(Electron 运行时 279MB + app.asar 94MB + unpacked 562MB);asar 清单 14475 文件 / 680MB
- `node_modules/@tencent/tencent-docs-ai-engine/bin/win32-x64/editor_sdk.exe` = **199.5MB**,判定不可重建
- 自研沙盒:`tsbx.dll`(795KB)/`tsbx_sdk.dll`/`tsbxldr.dll`/`sandbox-center.exe`(9MB)/`betterleaks.exe`(22MB) —— 最关键且最不可还原
- 压缩 JS:`cli/dist/codebuddy-lite-wb.mjs` 9.48MB/2679 行、平均行长 3539;`main/server.js` 3.6MB;renderer/assets 116.7MB
- 可 1:1 的只有 ≈2.7MB:`cli/vendor/shim/*.cjs`(node-brokered-fs-shim 1162 行、safe-delete-bulk-guard 465 行)与 `cli/bin/*.cjs` 是未压缩完整源码
- 第三方 OSS ≈58MB 可直接 npm 复用(OpenAI Agents SDK、MCP SDK、OpenTelemetry、node-pty、koffi、lark SDK 等)
- REA 实测:`binary_session` 显示 ghidra=`not_configured`、hopper=`unsupported_host`、ida=`not_configured`、`recover_javascript_sources`(Wakaru)=provider_missing(仅 Linux)
- REA 分析 2 个源文件 → 11395 AST 节点 / 10 findings / 0 parse failures,但 coverage=partial,11,387 app unknowns + 128,327 semantic unknowns
- 技术注记:asar 解析时 header 长度字段含填充,需 `hdr[:hdr.rfind('}')+1]` 兜底;`analyze_javascript_application` 只接受目录或 `.asar`(不接受单个 `.mjs`),且官方 asar 需 `integrity_policy=record-and-continue`(unpacked 原生二进制被签名剥离导致校验不符)。
- 建议路径:不复刻,改走「参照公开架构 + 自研」——复用 OSS 底座(Electron / Anthropic sandbox-runtime / MCP SDK / OpenAI Agents SDK)+ 自建模型网关 + 自选沙盒方案。
## WorkBuddy 对话区 / 右侧栏 UI 复刻可行性(REA)
- 产出:`C:\Users\Administrator\code\ygent\docs\WorkBuddy-UI复刻可行性评估.md`
- 结论:**UI 层是整个项目最接近可复刻的部分**(视觉~95% / 结构~90% / 交互~70% / 数据 0%)。
- 决定性原因:**腾讯压了主进程(`main/server.js` avg 3037 字符/行、全单字母),却没压渲染进程** —— `contexts-BMeWSUHk.js`(7.55MB) 保留 **10,347 个可读函数名**、920 个具名 React 组件,函数体逐行可读(`ConversationInputArea`/`ConversationStreamingFooterImpl` 实测),仅局部变量重命名为单字母。
- 视觉层全明文:CSS 类名**无 hash**(`.cb-chat-message--user`、`.detail-sidebar--collapsed`、`.claw-secondary-sidebar--pinned`)、设计 token 完整(`--wb-palette-brand-8:#00c29a`,`wb-` 用 1425 个、`cb-` 用 437 个类)、i18n 三语全量本地(zh-cn/zh-tw/en,含全部 `chat.*` key)、图标/插图资源可复制、图标用 VS Code codicon(MIT)。
- UI 技术栈几乎全开源(可直接复用,无需复刻):React18 + Radix(17) + Floating UI(54) + Zustand(39) + mermaid(101) + KaTeX(49) + Excalidraw(63) + PDF.js + xterm + Monaco(15) + marked/markdown-it + DOMPurify + i18next(16) + dayjs(50) + framer-motion。
- 右侧栏形态 = 主壳组件(`DetailPanelSurfaceProvider/Host/Portal`、`ClawSecondarySidebar`)+ 独立扩展 `resources/extensions/{library,global-search,im-channels,community,cloud-assistant,...}/ui/assets`。
- 受限点:函数内部逻辑被混淆需重写;跨进程交互(窗口控件/原生菜单/文件对话框/剪贴板/截图)依赖主进程+原生模块;office 预览走不可复刻的 `editor_sdk.exe`(199.5MB);`cb-` 内部组件库需重写;排除云后无数据源=空壳。
- 两个不变天花板:**版权**(不能复制 CSS/资源/代码产出物)+ **云依赖**(无后端则界面无内容)。
- 复用要点:`analyze_javascript_application` 的 `source_paths` **不接受 .css**(实测 invalid_value),CSS 需用文本工具处理;renderer 目录可直接作为 input_path。
## YCode 工程落地(Electron 最小闭环)
- 位置:`C:\Users\Administrator\code\YCode`(clean-room,不复制 WorkBuddy 产物)
- 选型(用户确认):Electron 桌面应用 + 最小端到端闭环 + 自建 OpenAI 兼容网关(自有 API Key)
- 已落地:Electron 37.10.3 + React 19 + Vite 8 + TS 7 + Zustand 5 + electron-vite 5;主进程(config/ipc/sessions/gateway/agent+tools)、preload(CJS)、renderer(三栏 UI);typecheck/build 通过
- `npm run smoke` 全部通过(本地假上游驱动真实 gateway+loop:SSE 流式、分片工具调用组装、工具执行、上下文回填、越界拒绝、取消/缺 Key 报错)
- **关键坑(务必记住)**:
1. `electron` 在 devDependencies,`externalizeDepsPlugin` 不外置它 → 必须显式 `rollupOptions.external: ['electron']`,否则会把 electron 的 Node 入口内联进主进程
2. sandbox:true 要求 preload 为 **CJS** → preload 输出 `format:'cjs', entryFileNames:'index.cjs'`;主进程为 ESM(`index.mjs`),`__dirname` 由 electron-vite shim
3. TS 7 移除了 `baseUrl`,paths 必须写成 `./src/...`
4. **GPU 进程沙箱**:在受限进程树里 Chromium GPU 子进程以 0xC0000005 反复崩溃 → 用 `YCODE_DISABLE_GPU_SANDBOX=1`(仅关 GPU 沙箱,GPU 仍生效,实测 3 进程 0 崩溃) 或 `YCODE_DISABLE_GPU=1`(纯软渲染兜底)
5. 宿主环境带 `ELECTRON_RUN_AS_NODE=1`,从该环境启动 electron 会退化成普通 node → 需 `env -u ELECTRON_RUN_AS_NODE` 或换正常终端
6. 从 Agent shell 启动的 GUI 进程会在工具调用结束时被回收,无法跨调用保持
- 环境:node 22.22.2 / npm 10.9.7;用户机器 GPU = AMD Radeon RX 460,WorkBuddy 自身为 Electron 37.10.3
## YCode 视觉对齐(按 WorkBuddy 实测值重做)
- 起因:首版自拟的绿色主题+自创排版被用户指出「和 WorkBuddy 不相干」
- 依据(两项,勿凭记忆):① `ygent/docs/workbuddy-chat-layout.md` 真实 CDP 实测;② 安装包 `renderer/assets/tokens-Df-i-zsO.css` + `contexts-BkjwYSZT.css` 的 token 表
- 关键实测值(已落到 `src/renderer/src/styles/tokens.css`):
- **UI 主体是中性黑白灰**:文字 #000 / rgba(0,0,0,.7) / rgba(0,0,0,.5);背景 #fff / #f2f2f2;hover #ebebeb;active #e6e6e6;边框 #e6e6e6 / #ebebeb / #f2f2f2
- 品牌绿 #00c29a 只作状态点缀,**不用于主按钮/大面积色块**;成功 #0cbf5b、警告 #ff7800、危险 #f64041、信息 #3370ff
- 圆角 4/6/8,气泡 16(`16px 16px 0`),输入框 24
- 字号 caption 12 / body 14 / 气泡 13;Markdown 根 17.143px 行高 1.75,段落 .875em → 15/26.25;代码 12.857/22.5
- 布局:左栏 280px;内容列 max-width 832px 居中 + 左右 padding 40px(正文列 752px)
- 助手头部:24px 圆形标识 + 10px 间距,行高 24.5px,名称 14px/600,下间距 12px
- 用户气泡:#f2f2f2、padding 8px 12px、min-width 40px、max-height 310px
- 输入容器:圆角 24px、边框 #e6e6e6、白底+双层轻阴影;编辑区 min-height 64px / 横向 padding 16px;工具栏 56px、按钮 32px;发送按钮 32px 圆形(启用=黑/反色,禁用=rgba(0,0,0,.15))
- 落地原则:CSS 用自有类名(`.msg--assistant`/`.bubble`/`.composer__inner` 等),**只取实测数值,不复制对标产品的类名与样式表结构**
- 组件已同步:MessageBubble(助手头部+正文分离)、ChatPane(输入区拆「编辑区+56px 工具栏」)、ToolCard、Sidebar(280px + 列表容器)、RightSidebar
- **用户偏好:不要反复帮他启动应用**;他已自行 `npm run dev` 跑热更新,需要启动时由他自己来
## YCode 视觉对齐 第二轮(用户反馈「几乎完全不相干」后)
- 用户同时给了两张图:左=ygent(YCode),右=真实 WorkBuddy,差距很大
- 又捞到的**关键实测值**(来自安装包 CSS,勿再凭截图猜):
- **输入框填充是渐变**:`--wb-bg-input-slot-gradient: linear-gradient(180deg,#ebebeb 0%,#f5f5f5 100%)`(深色为 `#333→#292929`),外框 `border:1px solid #e6e6e6`、`border-radius:24px`,**无阴影**;focus 时 border 变 `rgba(0,0,0,.15)`
- 发送按钮:图标尺寸 32px,hover `opacity .8`,active `transform:scale(.93)`,禁用 `opacity:1`
- 侧栏导航(BEM `cb-sidebar-nav__*`):item `padding:8px 20px` flex 对齐;选中 `__item--selected` 用 list inactiveSelectionBackground(即灰底);icon `14×14` 且 `color-mix(text-primary 60%)` + `margin-right:8px`;title `13px/400/1.3`;section `margin-bottom:16px`;section-title `11px/600/uppercase/letter-spacing .5px/padding 8px 20px 4px`;header-title `13px/600/uppercase/opacity 30%`;content `flex:1;padding:8px 0 16px;overflow-y:auto`;footer `border-top:1px;padding:12px 20px`
- 真实 WorkBuddy 布局:左栏是「导航项+分组(任务/空间)+底部用户信息」,不是纯会话列表
- 本轮改动:tokens 新增 `--yc-input-slot-gradient`;app.css 重写侧栏(header/content/section/item/footer)+ 输入区渐变 + `.chat-head` 去掉下边框;Sidebar.tsx 改为「新建对话 + 任务分组 + 底部工作目录」;ChatPane 工具栏改为「+ / 模型名 / 发送圆钮」两段式
- **仍未对齐**:真实 WorkBuddy 侧栏顶部有多个导航项与图标集;输入区工具栏还有权限选择器、麦克风等。要做逐像素对齐必须开 CDP 实测真实 WorkBuddy(端口 9223,参考 ygent/docs/workbuddy-cdp.md)
## WorkBuddy CDP 采集准备(视觉对齐第三轮)
- **开关确认**:环境变量 `WORKBUDDY_REMOTE_DEBUGGING_PORT`(在 app.asar 的 `/main/index.js`:commandLine.appendSwitch('remote-debugging-port', r));`/main/server.js` 另有 `--remote-debugging-port=`。当前版本 37.10.3-42 仍有效
- **单实例锁**:起第二个实例(独立 `--user-data-dir`)**无效**,被锁挡掉、端口不开;WorkBuddy 会自行重设 userData
- **结论**:只能「完全退出 → 带环境变量重启」才开 CDP。Agent 跑在 WorkBuddy 内部,重启会中断会话,**必须由用户在外部终端执行**
- 已交付脚本(都在 YCode/scripts):
- `enable-wb-cdp.ps1` —— 一条命令走完:杀进程 → 带 env 启动 → 等端口(≤90s) → 调采集 → 打印产物目录
- `wb-capture.mjs <port> <outDir>` —— CDP 只读采集;44 个选择器(侧栏/对话区/输入区/右栏)+ 41 个精选计算属性 + 顶层布局树 + 截图;产出 `styles.json` + `page.png`
- npm script:`npm run wb:capture`(默认 9223 → `docs/workbuddy-capture`)
- 恢复:退出 WorkBuddy 后不带环境变量重启即关闭调试端口
- 产物按时间戳存 `docs/workbuddy-capture/<ts>/`
### 坑:.ps1 必须纯 ASCII
- 第一版 `enable-wb-cdp.ps1` 带中文,用户执行报 `字符串缺少终止符` —— Windows PowerShell 5.1 按 **ANSI(GBK)** 读 .ps1,中文被读成乱码(`宸茬瓑寰?`)并吃掉闭引号,解析直接失败(未执行,WorkBuddy 未被杀)
- 修法:脚本**全部改用英文输出**,实测 `non-ASCII bytes: 0` + `[Parser]::ParseFile` 通过
- 校验套路(以后写 ps1 都先跑):`[System.Management.Automation.Language.Parser]::ParseFile($p,[ref]$null,[ref]$errors)` 检查语法 + 统计 >127 字节数
- 采集产物改存 `$env:USERPROFILE\.ycode\captures\<ts>`(含真实对话,不进仓库);npm script 默认 `.tmp/wb-capture`
## CDP 实测成功 → 视觉对齐修正(2026-10-15 00:2x)
用户执行脚本后 WorkBuddy 以 CDP 启动,**采集成功**(视口 1334×901 / DPR 1 / zoom 1.0)。
链路:`scripts/wb-capture.mjs`(44 选择器 → styles.json + page.png)+ 新增 `scripts/wb-tree.mjs <port> <selector> [max] [outDir]` 导出子树结构与样式。
**实测数据已固化到 `docs/视觉对齐实测.md`**(以后先读它,别再靠截图猜)。
关键实测修正(我之前做错的地方):
1. **会话内输入框是白底**:`rgb(255,255,255)` + 1px `rgb(230,230,230)` + 圆角 24 + **双层阴影** `rgba(0,0,0,.03) 0 16px 32px -8px, rgba(0,0,0,.06) 0 2px 4px -4px`,`min-height:96`、`padding-top:12`、宽 **784**(不是 832)
- ⚠️ `--wb-bg-input-slot-gradient` 那个灰色渐变属于 **`.wb-home-composer__input-slot`(首页新建对话)**,**不是会话内**。我上一轮用它改错了,已改回
2. **主文字色 `rgba(0,0,0,.9)`**,不是 `#000`
3. **侧栏背景 `#f7f7f7`**(`rgb(247,247,247)`),不是我用的 `#f2f2f2`
4. **侧栏列表项是单行 31px**:`padding 5px 12px`、圆角 8、标题 14px/21 左 + 时间 11px/20 右(不是标题+副标题两行堆叠);导航按钮 255×32、图标 16×16、标签 14px
5. 分组标签 `.conversation-section-label` 30px 高、12px/600、色 `rgba(0,0,0,.5)`、文字形如「任务 (1)」
6. 品牌行高 64、标题 14px/700、版本徽标 10px
7. **右侧详情面板默认收起**(实测宽 1px)→ YCode 默认改为收起
8. 输入区工具栏 56 高 / padding 12 / `justify-content: space-between` / gap 8;发送按钮 32px 圆
9. 对话区 `.cr-message-list__content` 832 宽 + padding 0 40px → 正文列 752;`.cr-agent__header` 24.5 高 / margin-bottom 12 / gap 10;markdown 17.143px/30.0002;段落 15.0001/26.2502/margin-bottom 12;气泡 13/19、`#f2f2f2`、圆角 `16px 16px 0`、max-height 310
待补:右栏展开态(需用户在 WorkBuddy 里展开详情面板后再采一次)。
### 右栏展开态实测(2026-10-15 00:3x,视口 1638×901)
- 采到了:用户展开详情面板后重采,`scripts/wb-tree.mjs .detail-panel`
- 结构:`.detail-panel-container`(647×871, **border-left 1px #ebebeb**) > `.sidebar-next`(白底, **border-radius 12px 12px 0 0**) > `.detail-layout` > `.detail-layout__top`(64px) + `.detail-layout__content`
- 顶栏 `.sidebar-next-main-header`:646×64,`padding 0 16px`,**space-between**,`gap 4px`
- 页签:`.artifact-tab` 240×32(min 80/max 240),`padding 0 8px`,圆角 8,**当前项底色 #f2f2f2**,**14px/600**,行高 22;图标 16×16 色 `rgba(0,0,0,.7)`;关闭钮 14×14 色 #777 圆角 999
- 新增钮 32×32 圆角 8;右侧操作两个 32×32 圆角 6
- **宽度是可拖拽的**:CSS 里 `.detail-panel-container{width:100%}`,由外层布局决定 → 647px 只是本次实测值,不是 CSS 默认
- YCode 已对齐:右栏改为「顶栏 64 + 页签 + 操作按钮」,默认 647px(未实现拖拽调宽),并加了收起按钮
- **新增待补**:30px 高菜单栏 `.codebuddy-menubar`(文件/编辑/视图/帮助),YCode 还是 Electron 默认菜单栏
## 图标采集与接入(用户要求「图标先用 workbuddy 的,先 1:1 还原再改造」)
- 新增 `scripts/wb-icons.mjs`(CDP 导出指定节点 SVG outerHTML → icons.json)+ `scripts/gen-icons.mjs`(生成 icons.tsx)
- 生成 `src/renderer/src/components/icons.tsx`:15 个图标(新建任务/下拉箭头/筛选/收起/分组加号/加号/盾牌/小箭头/麦克风/停止/页签/关闭/右箭头/面板收起/右侧栏)
- **踩坑:SVG 属性必须转 JSX 驼峰**(`fill-rule`→`fillRule`、`stroke-width`→`strokeWidth`、`clip-path`→`clipPath` 等),已在 gen-icons.mjs 的 JSX_ATTRS 里统一处理,否则 React 告警
- `src/renderer/src/components/icons-extra.tsx`:自有矢量(IconSettings 滑杆、IconSendArrow、IconUser),可长期保留
- **合规标注**:icons.tsx 头部已写明「路径取自 WorkBuddy 运行时界面,仅作临时占位,正式发布前必须整体替换」;VS Code codicon 是 MIT 可长期用
- 验证:应用启动成功,`gpuRenderer` 实测 `ANGLE (AMD, AMD Radeon RX 460, D3D11)` —— **GPU 确实在跑**;截图存 `docs/screenshots/ycode-ui.png`
- 启动+截图的可行姿势:在同一个工具调用内完成——清 `ELECTRON_RUN_AS_NODE`、设 `YCODE_DISABLE_GPU_SANDBOX=1`、以独立进程启动 electron(带 `--user-data-dir` / `--remote-debugging-port=9335`),随后 `node scripts/shot.mjs 9335 <out.png>`
## 标题栏 + 自绘菜单栏(2026-10-15 00:4x)
- 实测(`.codebuddy-menubar`):**顶部条带高 30px**;左侧 x=0 宽 **92px** 为侧栏操作区;菜单区 `padding-left:4px`;菜单标题高 24、**`padding: 2px 8px`**、**圆角 4**、**12px/行高20**、色 **`#8b949e`**;菜单项 = **编辑(E)/窗口(W)/帮助(H)/关于(A)**;右侧窗口控制区宽 **138px**(≈46×3)
- 这是**自绘菜单**(VS Code 风格),故必须走无边框窗口
- 已实现:
- 主进程 `frame: false` + `Menu.setApplicationMenu(null)`;新增 IPC `window:minimize|toggleMaximize|close|state`、`edit:action`(走 webContents.undo/redo/cut/copy/paste/selectAll)、`app:info`、`app:openConfigDir`;窗口 maximize/focus 事件推给渲染进程
- 渲染 `components/TitleBar.tsx`:30px 条带、`-webkit-app-region: drag`(按钮/菜单 no-drag)、四个可展开的下拉菜单(条目都接真实行为)、右侧窗口控制(自绘 10×10 图标,关闭键 hover 变 `#e81123`)
- `components/AboutDialog.tsx`(版本/Electron/Chromium/Node/配置目录)+ Ctrl+, 打开设置
- CSS 新增 `.app-root`(列向 flex)+ `.app-shell{flex:1;min-height:0}` 以容纳顶部条带
- **重要坑:宿主环境带 `ELECTRON_RUN_AS_NODE=1`,且会间歇性被继承**,导致 electron 退化成 Node,报 `The requested module 'electron' does not provide an export named 'BrowserWindow'`
- 症状特征:报错里显示 `Node.js v22.21.1`,且 `import { ... } from "electron"` 报无命名导出
- 可靠解法:`Start-Process -UseNewEnvironment`(拿到干净环境块,不带该变量)+ 把 GPU 开关改为**命令行参数** `--disable-gpu-sandbox`(不再依赖 `YCODE_DISABLE_GPU_SANDBOX` 环境变量)
- `cmd /c set VAR= && ...` 被安全策略拦截,不要用
## 空态 + 间距阶对齐(2026-10-15 00:5x)
- 采集时机不巧:WorkBuddy 当时在会话中,页面**无空态元素**(`.claw-local-empty` 等命中 0)→ 本轮数据**取自安装包 CSS**,不是冒烟实测,已在文档里标注
- 实测值:
- 空态 `.colleague-chat-empty--profile`:容器 flex column 居中 / `gap:24` / `padding:0 16`;头像 **64px、边框 1px `#ebebeb`、圆角 18px**(radius lg+md+sm=8+6+4)、下距 16;标题 **24px/34/500**;说明 **max-width 324px、12px/18**
- 快捷提问胶囊:**`padding: 10px 16px`**、**`border-radius: 9999px`**、底色 gray-3 **`#f2f2f2`**、文字 `color-mix(text-primary 90%)`、14px、行高 21、`border: 0`
- 免责声明:`padding: 8px 0`、居中、色 `rgba(0,0,0,.3)`、12px/20、`pointer-events:none`;文案「内容由 AI 生成,请核实重要信息」
- **间距阶修正(重要)**:我原来的 `--yc-space-N` 整体**错位一档**。现按 `spacing-1=2 / 2=4 / 3=8 / 4=12 / 5=16 / 6=20 / 7=24 / 8=32 / 9=40 / 10=48 / 12=64` 对齐,**编号与对标一一对应**,以后翻译对方 CSS 无需换算
- 改动:`tokens.css`(间距阶 + 空态相关)、`app.css`(`.empty*` 重写 + `.composer__disclaimer`)、`ChatPane.tsx`(空态结构 + 免责声明);3 处 `--yc-space-6` 用法改 `--yc-space-7`
- 截图:`docs/screenshots/ycode-empty.png`
- **待复核**:用户切到 WorkBuddy 新建对话页后,可跑 `node scripts/wb-tree.mjs 9223 ".colleague-chat-empty" ` 做一次冒烟核对
## 参照文档已并入本项目 + 分级目标(2026-10-15 00:5x)
- **重要**:SOP 与对齐参照原本都在**另一个仓库** `C:\Users\Administrator\code\ygent\docs\`,YCode 里没有。已把 11 份拷到 **`YCode/docs/reference/`**,项目自此自包含:
`workbuddy-cdp.md`(SOP) / `workbuddy-chat-layout.md` / `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` / `ygent-workbuddy-ai-gap-2026-10-14.md` / `ygent-chat-parity.md`
- **采用其分级目标**(写进 `docs/视觉对齐实测.md` §0):L1 几何(≤1px) 可达 / L2 静态像素 90%+ / **L3 动态交互一致才是真目标、未完成** / L4 全局 100% 不可能不做
- **沿用其"不做"约定**:不伪造消耗/模型/产物计数凑视觉;不复制平台商业入口;不硬补高度;不把静态像素通过说成动态交互通过;不把绝对 y 差当 margin 修
- 从该文档获得的立即修正:
1. **字体顺序**:实测 `"PingFang SC"` 打头(我原来 `-apple-system` 打头)→ 已改
2. **滚动条**:实测宽 **6px** / thumb **rgba(0,0,0,.2)**(我原来 8px / .15)→ 已改
- **交叉验证成功**:ygent 在 1200×762 下达成的对话区像素级锚点(正文列 w752、输入外框 784×134 + 1px #e6e6e6 + 圆角 24 + 双层阴影、发送按钮 32×32、头像 24×24 gap 10)与我本次在 1334×901 的实测**完全一致** → 说明这些宽度是 token 而非尺寸相关
- **图标许可**:该文档同样指出「逆向取值的 WB 图标照搬涉及资产许可,建议自绘」——与我写在 `icons.tsx` 头部的告警一致,后续替换时优先考虑自绘或 OFL/MIT 图标集
### 参照文档补齐(用户说"复制过来")
- 用户先问「那个 SOP 在当前目录下吗」→ 答:不在,在 `ygent/docs/`。随即要求复制
- 最终 **`YCode/docs/reference/` 共 24 份、288K**,涵盖:SOP、布局/输入区实测、像素级可行性、字体/侧栏/设置/模型对齐、40 项差异审计、能力差距、对话对标路线图、**L3 动态交互对标(37KB)**、流式活动、消息编辑、审批提问、富内容、附件、会话标题、壳层设置、调试、独立会话、参照导入、岗位团队方案
- 索引已写进 `docs/视觉对齐实测.md` §0,项目自此**不再依赖外部仓库**
- **下一步重点**:`reference/ygent-chat-interaction-parity.md` 是 L3 的规范(含必测序列、事件与渲染状态要求),做动态交互前必须先读它
## Token 体系镜像 + 六档字号 + 面板调宽(2026-10-15 凌晨自主推进)
用户指令:持续推进 1:1,拆解/对比 WorkBuddy 必须用 REA,需决策处先推荐并记录、最后统一汇报。
- **完整 token 表已提取并固化**(`docs/视觉对齐实测.md` §11):间距阶 1=2/2=4/3=8/4=12/5=16/6=20/7=24/8=32/9=40/10=48/12=64;字号阶 1..11 = 8,10,12,13,14,16,18,20,24,28,32;行高 4=16/4-2=18/5=20/5-2=22/6=24/7=28/8=32/10=40;控件尺寸 3=12..17=68 + control-sm/ms/md/lg/xl/2xl;圆角 none/xs2/sm4/md6/lg8/xl12/2xl16/3xl20/4xl24/full;体例语义字号(body 14/22/400、caption 12/18/400、code 13/20、h1 24/32、h2 20/28、h3 16/24、display 32/40);**chatflow 独立一套**(h1 21/37、h2 19/33、h3 17/30、h4 15/26、p1 15/26、p2 13/23、p3 11/19)
- **`--yc-*` 编号与 `--wb-*` 一一对应** → 翻译对方 CSS 是机械映射
- **app.css 裸 px 从 321 → 0**(53 处一次性实测值提为具名常量区);`npm run build` 通过
- **六档字号**(`src/renderer/src/fontSize.ts`):`--yc-font-offset-global` + `--yc-md-root`,`clamp(-1,·,2)`,Ctrl+-/+/0,localStorage;实测档位2 = 14px/22px 与基线一致,1→6 档 13/14/15/16/16/16(+2 封顶)
- **关键简化**:因样式已全 token 化,**不需要**像对标那样扫描样式表注入覆盖规则
- **面板调宽**:`.app-shell__resizer` 绝对定位 6px 命中区贴合网格边界(不占网格列),320–900px,默认 647,双击还原,localStorage 持久化
- **补测真实对话区顶栏 `.workbuddy-topbar`:高 64px / padding 0 16px / 两端对齐** → `.chat-head` 据此改写(原自拟 12px 作废)
- **REA 使用记录**:
- 成功:`open_binary app.asar`(asar/archive) + `inspect_artifact` → 12253 节点 / 14476 文件 / **45 处完整性矛盾**(首条 `cli/bin/native/win32-x64/job-object.node`,即 Job Object 原生模块)
- **边界(重要)**:REA 的 CDP 浏览器 provider **只枚举 HTTP(S) 目标**,WorkBuddy 是 `file://` → `list_browser_targets` 返回 `targets:[]`,故 `inspect_web_page`/`capture_web_screenshot`/`compare_web_captures` **无法观察 WorkBuddy**;仍须用自建 CDP 脚本。REA 二进制 provider 本机未配置且不接归档 → 无字符串检索
- 新增脚本:`scripts/verify-tokens.mjs`(运行时 token 校验)、`scripts/pngdiff.mjs`(无依赖 PNG 像素对比,zlib 手写解码)
- 回归对比:token 化前后变化像素 2.673%(已定位为 chat-head 位移 + 抗锯齿重绘)
- **汇报与决策记录:`docs/本轮汇报与决策记录.md`**(含 D1–D10 决策、被否决选项、阻塞项、下一步)
## 外围壳层配齐(2026-10-15 凌晨,用户新指令)
用户指令:**「先把外围配齐,登录账号先用 mock 写死数据,通知、发现应用、收起、搜索、分类、设置等等等,还差非常多」**
→ **推翻了我此前的 D7/D8「不摆空壳」决策**,改为全部实现 + mock 数据。
已实现(全部按实测尺寸):
- **品牌区**:标题 14/700 + 版本徽标 10px + **「发现应用」按钮 98×28 / 圆角 9999 / bg #f2f2f2 / 12px500**
- **导航项 6 个**:新建任务 / 助理(Beta 徽标) / 专家·技能·连接器 / 定时任务 / 资料库 / 更多(灵感副文案);**215×32、padding 5px 12px、圆角 8、图标 16、文字 14**
- **搜索**:放导航下方,**真实过滤任务列表**(不是装饰)
- **分类分组**:任务 (N) / 空间 (N),标签 **30 高 sticky、padding 5px 12px、圆角 8、12px/600、色 rgba(0,0,0,.5)**;折叠箭头与操作按钮默认 opacity 0
- **账号区(mock)**:footer **57 高、padding 8 12 8 8、border-top 1px #e6e6e6**;头像 **28×28 圆、bg #ebebeb**;用户名 12/600;铃铛 **32×32 圆角 6**;**未读角标 14×14、#f64041、9px/500**
- **通知面板**(mock 3 条)+ **设置分区**(账号/模型/外观/工作目录/关于,左侧分区导航)
- **侧栏收起**为 44px 窄轨(localStorage 持久化)
- 新增图标 8 个(导航 5 + 铃铛 + 小程序 + 发现应用),图标模块共 **23 个**
**重要实测发现**:**侧栏宽度不是常量** —— 两次采集分别 280px(@1334 窗口) 与 240px(@1276 窗口)。
**Mock 约定(重要)**:`src/renderer/src/mock.ts`,全部 `MOCK_` 前缀;**UI 上必须显式标注为示例数据**(通知面板底部、设置→账号页、账号按钮 title 三处已落实);未实现入口点击给 2 秒「尚未实现」提示,**不伪造可用状态**。
新增决策 D11(mock 标注方式)/ D12(搜索落点)/ D13(未实现入口反馈);D7 标注为「已被用户推翻」。
新增脚本:`scripts/wb-shell.mjs`(外围结构采集)、`scripts/click-shot.mjs`(点击后截图验收)。
样式拆分为 `styles/sidebar.css`(独立文件,避免 app.css 过长)。
## 标题栏左上操作区(2026-10-15 凌晨补充)
用户指出左上角不一致(我那里是空的 92px)。补采实测:
- `.conversation-list-topbar-actions` 内含**三个 24×24 按钮**,位置 **x=8 / 34 / 60**,`gap: 2px`,圆角 6
- 依次为 **收起侧边栏 / 搜索 / 筛选**(title 属性即这三个词)
- 已实现:新增图标 `IconCollapseSidebar` + `IconSearchTopbar`(`IconFilter` 此前已有),图标模块共 **25 个**
- 交互接线:收起侧边栏 → 侧栏折叠;搜索 → 展开侧栏并聚焦搜索框;筛选 → 走统一的「尚未实现」提示通道
- 新增全局事件约定:`ycode:focus-search`(聚焦侧栏搜索)、`ycode:not-implemented`(外围未实现入口的统一反馈)
- **验收**:三个按钮实测落在 x=8/34/60、24×24、gap 2,与对标完全一致
**CDP 可写能力**:CDP 不仅能读,也能**点击与导航**(`Runtime.evaluate` 里 `.click()`),
即可直接切换 WorkBuddy 的页面状态。但点击会**真实改变用户的应用状态**
(例如「新建任务」会真的建一个任务),与参考 SOP「只读采集」的约定不同,需用户明确同意后再做。
## 用户批准方案 B(CDP 驱动 WorkBuddy)+ OMP 接入(2026-10-15 凌晨)
### 决策
- **方案 B 已获批准**:我可用 CDP 直接点击/切页采集 WorkBuddy。已用此采到**首页/新建任务页**
- **底层循环接入 OMP**(本地 Agent 内核),**支持 responses 格式**,使用 `https://api.iiiii.run` + 用户给的 key(有限额,用户起床过期)
### OMP 事实(重要,勿再重查)
- 可执行文件:**`C:\Users\Administrator\AppData\Local\Ygent\omp.exe`**(随 Ygent 安装);`omp/18.6.3`
- 配置:默认 `~/.omp/agent/models.yml`;**命名 profile 在 `~/.omp/profiles/<name>/agent/`**,用 `OMP_PROFILE=<name>` 选择
- RPC:`omp --mode rpc`,**stdio 上的 newline-delimited JSONL**(非 JSON-RPC 2.0);启动先发 `ready` 帧;单帧上限 1MiB
- 其它 mode:`text`(默认)/ `json` / `rpc` / `rpc-ui`
- 常用 flag:`-p/--print`(非交互)、`--model`、`--cwd`、`--config`(叠加覆盖)、`--profile`、`--session-dir`、`--no-session`
- 协议文档:`ygent/docs/reference/omp/`(rpc.md 89KB / cli.md / models.md / env.md / approval.md)
- **`api:` 字段取值**含 `openai-completions` / **`openai-responses`** / `openai-codex-responses` / `azure-openai-responses`
### 已完成的配置(未动用户的 ygent 配置)
- 新建 **`~/.omp/profiles/ycode/agent/models.yml`**:provider `ycode`,baseUrl `https://api.iiiii.run/v1`,
**`api: openai-responses`**,模型 `gpt-6-luna` / `gpt-6-astra` / `gpt-6.1-sol`
- **验证通过**:`OMP_PROFILE=ycode omp.exe -p --model ycode/gpt-6-luna "回答两个字:收到"` → 返回「收到」
- 上游 `api.iiiii.run` 同时支持 `/v1/chat/completions` 与 `/v1/responses`(含流式 SSE `event: response.created`)
- **API Key 处理**:只写入 `~/.omp/profiles/ycode/...`(仓库外);**未落任何仓库文件**
### 首页空态实测(方案 B 产出,见 `docs/视觉对齐实测.md` §15)
- 真实首页 = 大标题「<产品名>, 我帮你」+ 分段控件 + **分类胶囊行** + 输入框 + 工作目录/权限行 + 建议列表 + 免责声明
- 分类胶囊:**高 32 / padding 0 12 / gap 4 / bg #f7f7f7 / 边框 1px #dedede / 圆角 100px / 14px**;列表宽 **680**,gap 8
- **首页编辑区 74px,会话内 64px** —— 两者不同
- 旧空态(头像+竖排胶囊)是**从 CSS 推断的,与真实不符**,已按实测重写
### 新增脚本
`wb-probe.mjs`(按文案定位,应对 CSS Module 哈希类名)、`wb-home.mjs`、`click-shot.mjs`
### 教训(重复踩了两次)
**不要把多行 JS 内联在 `node -e "..."` 里**(shell 引号会吃掉换行,报 SyntaxError)。一律写成 `.mjs` 脚本文件。
## 首页空态推翻重做(2026-10-15 上午,用户指出「几乎不一样」)
用户醒来后指出新建对话页还是不对。逐项精测后发现**第一轮实现整体结构错误**:
- 错:输入框底部停靠、10 个分类挤一行、无分段控件、无工作目录/权限行
- 对:**居中内容列(x=353 宽 800)、输入框在页面中部、建议列表在其下方**、
分类分两处(分段控件 3 个 + 胶囊行 7 个)、输入框下方有「工作目录+权限」行
精测值(`docs/视觉对齐实测.md` §15.5):
- 标题 h1 **30px/600/行高42**,rect [614,159,278,42]
- 分段控件 [643,219,220,32];分类行 [353,317,680,32];输入框外层 [353,365,800,184]
- 白框 144(圆角24/padding-top 12/白底/双层阴影);编辑区 74;工具栏 56;**纵向无 gap**
- 建议项 **高 38/圆角 9999/白底/边框 1px #e6e6e6/14px**,行距 **50**(38+12),左对齐
- 垂直间距:标题→分段 18 / 分段→分类 66 / 分类→输入 16 / 输入→建议 52
- 会话内输入框 **134**(12+64+56+2);首页白框 **144**(12+74+56+2)—— 都无 gap
**已重做**:ChatPane 拆 `composer`(`data-variant` 区分 64/74 编辑区高)+ `composerFooter`;
首页 `.home` 布局。构建通过,截图对比已接近。
**方法论教训(重要)**:**第一次只按「分类胶囊」这一个元素去对齐,没测整页垂直结构** →
局部对了、整体还是错的。以后对齐一个页面,**必须先量整页的垂直堆叠(各区块 y/h)**,
再量单元素。`scripts/wb-home-layout.mjs` + `wb-home-parts.mjs` 就是为此写的。
## OMP 内核接入完成(2026-10-15 上午,用户拍板方案 A)
**YCode 已从「自建网关+自写循环」转为 OMP 前端壳**,端到端验证通过。
- `src/main/omp.ts`:OmpKernel(spawn `omp.exe --mode rpc --profile ycode --cwd <ws> --no-session`、JSONL 解析、id 关联、ready 等待、退出清理)+ `detectOmpPath()`(YCODE_OMP_PATH → `%LOCALAPPDATA%\Ygent\omp.exe`)
- `src/main/agent/omp-turn.ts`:OMP 事件 → ChatEvent(text_delta→text、tool_execution_*→tool_call/result、agent_end yielded→done、prompt_result error→error、abort→cancelled)
- `src/main/ipc.ts`:**会话→内核进程池**(每会话一个 omp.exe,上下文由 OMP 进程内维护,磁盘持久化仍归 YCode);会话删除释放进程;OMP 不可用回退自建 loop
- **验证**:「只回复收到」→「收到」;「列出工作目录文件」→ 3 张工具卡 **glob/read/bash(OMP 原生工具)**,全 ok,回复「12 个文件」
- **关键坑**:`spawn ENOENT` 但 exe 存在 → 是 **cwd 不存在**(工作目录空字符串)。spawn 的 cwd 与 exe 无关,必须兜底(回退 homedir)
- 工具事件载荷文档未给全 → `readToolInfo()` 多形状兼容解析(toolCall/call/tool/data 逐层找)
- 会话内 134 = 12+64+56+2、首页白框 144 = 12+74+56+2,**纵向都无 gap**(之前多加了 4px 导致差几像素)
- 新增脚本:`e2e-send.mjs`(UI 层端到端发消息)、`probe-tools.mjs`(工具卡探测)
- **遗留**:abort 未端到端验证;key 过期需更换 `~/.omp/profiles/ycode/agent/models.yml`