ToolCard 工具卡片
根据声明的渲染意图渲染工具调用及其结果,而不是根据标记。
适用场景:当你要展示 Agent 或任务实际做了什么——一条 shell 命令、一次文件修改、一次查询——并且希望由工具说明「这是什么」、由界面决定「长什么样」时。
一个返回 HTML 的工具,等于替 UI 选好了渲染器、主题和布局,而且是在面向模型的结果里做这件事——这恰恰是最不该掺入 UI 关切的地方。声明意图把两者分开:同一次调用在这里可以是终端块,在紧凑的记录视图里是一行,在编辑器里是一个跳转目标,而工具完全不需要知道这些界面的存在。
快速开始
<r-tool-card open></r-tool-card>const card = document.querySelector('r-tool-card');
card.call = { card: 'terminal', title: 'pnpm test', cwd: '/repo' };
card.status = 'running';
// …调用返回后
card.result = { card: 'terminal', output: '2351 passed', exitCode: 0 };
card.status = 'success';卡片种类
generic
默认值,同时也是兜底。包含标题、可选的参数键值表,以及可选的结果内容。
card.call = { card: 'generic', title: 'Read file', input: { path: 'src/a.ts', limit: '200' } };
card.result = { card: 'generic', content: 'export const a = 1;' };terminal
这次调用本身就是一条 shell 命令。title 是命令;description 与 cwd 渲染在输出上方。非零 exitCode 会被显示,零则不显示。
card.call = { card: 'terminal', title: 'ls -la', description: '列出目录树', cwd: '/repo' };
card.result = { card: 'terminal', output: 'total 8\ndrwxr-xr-x …', exitCode: 0 };diff
这次调用创建或修改了文件。每一项都渲染为带双栏行号的 unified 风格 hunk,由 ranuts/utils 的 diffLines 计算。oldText 为 null 表示文件正在被创建——这正是调用期视图所拥有的信息,因为调用方无法读取尚不存在的旧内容。
card.call = {
card: 'diff',
title: 'Edit config',
diffs: [{ path: 'vite.config.ts', oldText: 'port: 3000\n', newText: 'port: 5173\n' }],
};两条会咬人的规则
这些视图会在实时调用时计算一次,在日志重放时再计算一次。其余规则都由此推导而来。
- 视图必须是调用参数的纯函数(结果视图还包括结果本身)。不做 I/O、不读时钟、不读会话状态——否则重放出来的画面会和用户当初看到的不一致。
- 无法识别的卡片降级,绝不抛异常。 来自更新版本生产方的卡片种类,或是在存储中被损坏的值,都会以
generic渲染并保留它的标题;畸形视图渲染为空。展示层绝不能让重放崩掉。
位置引用
调用上的 locations 会渲染为按钮并触发 locationclick,以便编辑器跟随:
card.call = { card: 'generic', title: 'Read', locations: [{ path: 'src/a.ts', line: 42 }] };
card.addEventListener('locationclick', (e) => openInEditor(e.detail.location));API 参考
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
call | ToolCallView | null | null | 待完成视图,由调用参数推导而来。 |
result | ToolResultView | null | null | 完成视图,会替换待完成视图。 |
status | 'running' | 'success' | 'error' | 'running' | 反射到属性,便于样式据此区分。 |
open | boolean | false | 主体是否展开。 |
sheet | string | '' | 注入元素 Shadow DOM 的 CSS。 |
未知的 status 值读回时为 running。
事件
| 事件 | detail | 触发时机 |
|---|---|---|
locationclick | { location: ToolLocation } | 某个文件引用被激活。 |
Part
card、header、status、title、toggle、body、description、exit、input、output、file、path、hunk、line、locations、location。
diff 行带有 data-kind,取值为 context、added 或 removed。
无障碍
页眉是真正的 <button type="button"> 并带 aria-expanded,无需额外接线即可用键盘抵达与操作。
相关
- Conversation 对话 —— 把它作为工具调用 view 的
mount目标 - ranuts/utils —— 渲染
diff卡片所用的diffLines