Components / Tool & Action States

File Diff

文件改动卡片:增删行 diff 视图(绿实线/红斜纹左缘条)。优先级 P0,已移植(1:1 aicss.dev)。

① 活体演示

上:结构化 rows 渲染(上游演示数据)。中:真实样本渲染——用 ③ 脱敏样本的 result_excerptparseExcerpt 解析。下:降级形态——excerpt 解析不出 +/- 行时退成普通步骤卡文本(契约 3.D.3)。四态:running / ok / error / empty。

真实样本渲染(③ 的 patch result_excerpt → parseExcerpt)
降级形态(excerpt 无 +/- 行 → 普通步骤卡)

② 对比台

左:wb-ui 活体(上游演示数据 src/auth.ts);右:aicss.dev 原站截图存档(2026-08-13 采集,可滚动)。

wb-ui 活体
aicss.dev 原站截图(design/reference/aicss/file-diff/screenshot.png)
aicss.dev file-diff 原站截图

③ 真实字段说明

字段真源:packages/contracts/workbench-events.mjs:24-77ACTIVITY_KINDpatch / file_write);reducer apps/web/index.html:6782没有结构化 diff 行字段——diff 文本只存在于 result_excerpt,由 parseExcerpt 解析 +/- 前缀行;解析不出则降级普通步骤卡(契约 3.D.3)。

事件 type字段驱动行为
workbench.activity.startedpayload.kind = "patch"|"file_write"识别为文件改动卡片
workbench.activity.startedpayload.target / label / args_summary头部文件名(target 优先)
workbench.activity.completedpayload.result_excerptdiff 正文来源:+/- 行 → 结构化 diff 视图;否则降级普通文本
workbench.activity.completedpayload.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");' }
]});
Ported from aicss.dev/components/file-diff, fetched 2026-08-13. Internal use only(上游无 license 声明,对外使用前须确认授权——docs/86 R1)。