File Diff
文件改动卡片:增删行 diff 视图(绿实线/红斜纹左缘条)。优先级 P0,已移植(1:1 aicss.dev)。
① 活体演示
上:结构化 rows 渲染(上游演示数据)。中:真实样本渲染——用 ③ 脱敏样本的 result_excerpt 经 parseExcerpt 解析。下:降级形态——excerpt 解析不出 +/- 行时退成普通步骤卡文本(契约 3.D.3)。四态:running / ok / error / empty。
② 对比台
左:wb-ui 活体(上游演示数据 src/auth.ts);右:aicss.dev 原站截图存档(2026-08-13 采集,可滚动)。

③ 真实字段说明
字段真源:packages/contracts/workbench-events.mjs:24-77(ACTIVITY_KIND 含 patch / file_write);reducer apps/web/index.html:6782。没有结构化 diff 行字段——diff 文本只存在于 result_excerpt,由 parseExcerpt 解析 +/- 前缀行;解析不出则降级普通步骤卡(契约 3.D.3)。
| 事件 type | 字段 | 驱动行为 |
|---|---|---|
workbench.activity.started | payload.kind = "patch"|"file_write" | 识别为文件改动卡片 |
workbench.activity.started | payload.target / label / args_summary | 头部文件名(target 优先) |
workbench.activity.completed | payload.result_excerpt | diff 正文来源:+/- 行 → 结构化 diff 视图;否则降级普通文本 |
workbench.activity.completed | payload.outcome("ok"|"error") | ok / error 态(error 头部染 danger) |
| (无 patch/file_write 事件) | — | empty 态:组件不渲染 |
脱敏样本(真实解析器输出;文件:fixtures/tool-activity.ndjson)——patch/file_write 节选:
{"type":"workbench.activity.started","activity_id":"act_e8bc3dc638a5","kind":"patch","label":"edit $PROJECT_ROOT/workbench-data/round7-fixture/scratch.md","tool":"Update","target":"$PROJECT_ROOT/workbench-data/round7-fixture/scratch.md","args_summary":"+2 / -1 lines"}
{"type":"workbench.activity.completed","activity_id":"act_e8bc3dc638a5","outcome":"ok","summary":"Changed 1 → 2 lines","result_summary":"Changed 1 → 2 lines","result_excerpt":"- second line\n+ second line, edited\n+ third line added"}
④ 用在 workbench 哪个位置
接入点:apps/web/index.html:7479 nativeStepInner(step)——kind=patch|file_write 的工具步骤卡换用本组件;diff 色板走 --wbui-diff-*(值 1:1 aicss,按 OQ-1=B 不映射既有 --diff-* token,契约 3.D.3)。文件名取 target,diff 文本取 result_excerpt。渲染链路:timelineParts → patchTimeline → nativeStepHtml(:7494) → nativeStepInner(:7479)。只换渲染层,不动 reducer。
⑤ 复制即用代码
引入 tokens + 组件 CSS/JS。全部相对路径、零依赖。
<link rel="stylesheet" href="design/wb-ui/lib/tokens.css"> <link rel="stylesheet" href="design/wb-ui/lib/components/file-diff.css"> <script src="design/wb-ui/lib/components/file-diff.js"></script>
// 真实接入:excerpt 有 +/- 行 → diff 视图;没有 → 自动降级普通步骤卡
var h = WbUI.fileDiff.render(el, {
file: started.target || started.label, // activity.started
excerpt: completed.result_excerpt // activity.completed
});
h.set(completed.outcome === "error" ? "error" : "ok");
// 结构化 rows(演示/已有解析结果时)
WbUI.fileDiff.render(el, { file: "src/auth.ts", rows: [
{ old: 12, cur: 12, type: "ctx", text: "export function getToken() {" },
{ old: 13, cur: null, type: "del", text: " return localStorage.token;" },
{ old: null, cur: 13, type: "add", text: ' const t = cookies.get("session");' }
]});