# Agent UI(agentui.pro)组件库调研 → Agent 会话页参考 调研对象:(shadcn 目录第三方社区 registry `@agentui`)。结论先说:这是一套**纯前端交互参考**,MIT 协议、可自由复制修改;组件本身自包含(不依赖 radix-ui/ai-sdk,`registryDependencies` 为空),但视觉上大量使用 Tailwind 调色板色(`emerald-*`/`rose-*`/`amber-*`/`blue-*`)与个别十六进制硬编码,与本项目 `docs/design/README.md` 的 token 纪律正面冲突,**不能直接 `shadcn add` 落地,只能复制源码后逐组件替换视觉层**。交互设计(状态机、折叠策略、滚动跟随阈值)成熟度高于我们目前的空白,值得原样借用。 ## 1. 项目身份 | 项 | 内容 | |---|---| | 定位 | "Copy-paste AI agent components built with Motion and Tailwind CSS." | | 公开仓库 | (经 shadcn-ui/ui PR #11607 确认,`ashish200729` 为提交者,LICENSE 落款人 Saurabh Chauhan,应为同一团队) | | 许可证 | MIT(仓库根目录 `LICENSE`,标准 MIT 全文,保留版权声明即可商用/修改/私有项目使用,无需开源回馈) | | 技术栈 | React `^19.0.0`、Next.js `16.3.5`(仅站点本身,组件不依赖 Next 特性)、Tailwind CSS `^4.0.0`、TypeScript `^5.7.3`、动效库 `motion`(Framer Motion 后继,`motion/react`)、代码高亮 `shiki`、`clsx` + `tailwind-merge` | | shadcn style | 未取得。仓库根目录无 `components.json`,`registry.json` 也无 `style` 字段;组件不引用 shadcn 的 `Button`/`Card` 等基础原语(`registryDependencies: []`),只假设消费方项目已有 `bg-muted`/`text-foreground`/`border-border` 等 shadcn 语义 CSS 变量,style 差异对能否使用没有实质影响 | | 依赖 radix/ai-sdk | 均不依赖。折叠/弹层是 `motion` 手写实现,不是 Radix Primitive,复制后需自查无障碍(aria)行为,不能假设 Radix 默认可访问性 | | 安装方式 | 逐组件走 shadcn registry:`bunx --bun shadcn add https://www.agentui.pro/r/`(npm/pnpm/yarn 等价);无独立可发布的 npm 包,也可直接 `git clone` 仓库复制 `components/agents/*.tsx` 源码,本项目应选后者(见第 5 节) | | 组件规模 | 18 个组件文档页(footer 分「Primitives」「Surfaces」两组),`registry.json` 汇总 22 个 registry item(component/lib/hook 混计,如 `lib/ease.ts`、`message-context.tsx` 各自单独登记) | | 未取得 | LICENSE 与 PR 里的作者姓名不一致但均指向同一仓库,未进一步核实组织关系;`/pricing`、`/license`、`/playground` 页面均 404 或未抓取 | ## 2. 组件清单与交互语义 统一安装命令 `bunx --bun shadcn add https://www.agentui.pro/r/`,依赖均含 `clsx lucide-react motion tailwind-merge`(部分另加 `shiki`)。 ### 消息列表/气泡 | 组件 | 用途 | 关键 props | 视觉要点 | |---|---|---|---| | `Message` | 消息行容器,按 `from` 控制左右布局与头像位 | `from: "user"\|"assistant"`、`animateIn`;子件 `MessageAvatar`/`MessageHeader`/`MessageFooter`/`MessageTyping` | 7×7 圆形头像,header 时间戳 `text-[11px]` | | `MessageBubble` | 气泡本体 | `variant: solid\|soft\|tint\|outline\|ghost\|danger`、`align`、`MessageBubbleCollapsible.collapsedLines`(2~6 行截断) | `rounded-2xl px-3.5 py-2.5`,`max-w-[82%]`,长内容 `line-clamp` 折叠 | | `MessageScroller` | 会话滚动容器(见"滚动跟随") | — | — | ### 流式文本(打字/光标) `StreamingResponse`:`status: streaming\|complete\|error`,`sources`(接 Citations)、`showActions`(复制/重试/反馈)。是"内容整段替换后平滑重排"模型,**不是逐字符打字机**,也没有光标闪烁效果;富文本用 `[&_a]`/`[&_code]`/`[&_pre]` 选择器样式化。 ### 推理/思考折叠块 没有专门的"可展开查看完整思考记录"组件。两个近似方案: - `Agent Activity`:`contentType: text\|search\|mixed\|step\|tool\|trace`,时间线渐进渲染,完成后**折叠为摘要**"Thought for Ns"(`formatDuration()`),原始推理文本不保留可展开入口。 - `Loading States` 里的 `ReasoningText`:`phrases`(循环短语)+ `variant: cascade\|swap\|scramble`,纯"正在思考"提示,无历史、无折叠。 ### 工具调用卡片 | 组件 | 状态 | 折叠/耗时 | 备注 | |---|---|---|---| | `ToolResult` | `running\|success\|error\|cancelled` | `open/defaultOpen/collapseOnComplete`(完成自动折叠),`meta` 显示耗时/状态码,`maxHeight` 默认 220 | `ToolResultOutput` 承载语法高亮的输入/输出,`kind: terminal\|request\|custom` | | `ToolApproval` | `pending\|approving\|approved\|denied\|running\|complete\|error` | `parameters: {id,label,value}[]` 展示参数,无风险分级 | 三按钮:Allow once / Always allow / Deny | ### 计划/步骤 `TodoList`:扁平数组 `items: TodoItem[]`(**不支持嵌套子任务**),状态 `pending\|in-progress\|completed\|cancelled`,`in-progress` 可带 `progress` 百分比,`collapseOnComplete` 默认折叠,`maxHeight` 默认 248。 ### 审批/确认 | 组件 | 场景 | 状态机 | 交互 | |---|---|---|---| | `ApprovalCard` | 通用审批 + 多步问答 | `pending\|submitting\|approved\|rejected\|changes-requested\|answered` | Approve / Reject / Request Changes,或问题流的单选/多选/自由填写 + 步骤导航 | | `ToolApproval` | 单个工具执行权限 | 见上 | Allow once / Always allow / Deny | 两者均**没有 `riskLevel`/`scope` 字段**,风险信息属于应用层自行拼进 `title`/`description`/`parameters`。 ### 代码/JSON 展示 `CodeBlock`:`shiki` 高亮,`language: text\|bash\|diff\|json\|tsx\|typescript`,`showLineNumbers`(默认 true)、`highlightLines`、`status: streaming\|complete`、`copyable`,**无折叠功能**。`FileDiff` 是它的 diff 变体:`lines: FileDiffLine[]`,新增行 `bg-emerald-500/[0.07]`、删除行 `bg-rose-500/[0.07]`,同样支持 `collapseOnComplete`。 ### 引用来源 `Citations`:行内角标(`Citation` + `idPrefix` 关联)+ 底部可折叠来源列表(`Citations`,`defaultOpen` 默认 **false**),支持网站 favicon、新增项弹簧动效。 ### 附件/图片 `ImageGeneration`:状态机 `queued\|generating\|refining\|complete\|error`(带 `onRetry`),`aspectRatio`/`resolution` 固定占位避免布局跳动,悬浮有跟随鼠标的抖动叠层特效;**不支持多图网格、不支持点击放大**。`PromptInput` 的 "Attach image"/"Add context" 只是 Actions 菜单里的两个按钮项,没有真正的附件预览/管理组件。 ### 用量与费用 `UsageDashboard`:`records: UsageRecord[]`(`inputTokens/outputTokens/cachedInputTokens/costUsd/requests/date/provider/model`),`range: 7|30` 天切换,输出「按天令牌趋势折线图 + 按 provider/model 汇总 + 计费金额表」,**没有预算/额度、没有进度条**,图表配色是 4 组写死的十六进制 CSS 变量(`--usage-first:#0e857e` 等,深浅各一套)。 ### 会话列表/分支 `AISidebar`:资源树(folders/projects/files/bookmarks),支持拖拽移动、内联重命名(F2/双击)、键盘导航,**不支持搜索,也没有"会话分支"概念**,是文件系统式的资源管理器而非对话历史列表。 ### 输入框 `PromptInput`:自增高文本域 `minRows=2/maxRows=8`,Enter 发送、Shift+Enter 换行,`loading` 时按钮变"停止"(`onStop`),`models`(模型选择器)、`actions`(附加动作菜单)。 ### 空态与错误态 **没有独立的 EmptyState 组件**。错误态分散在各组件里:`StreamingResponse.status="error"`、`ImageGeneration.status="error"`(带重试)、`ToolResult.status="error"`、`ToolApproval.status="error"`;会话为空时没有专门占位方案。 ### 滚动跟随策略 `MessageScroller` 精确对应需求:内部按 `scrollHeight - scrollTop - clientHeight` 算与底部距离,小于 `followThreshold`(默认 56px)才判定为"跟随中";滚轮/触摸/方向键会主动 `leaveLiveEdge()` 退出跟随,直到用户手动滚回底部;`followOutput` 开关、`onFollowChange` 回调、`ResizeObserver` 监听内容增长。这是本次调研里与我们需求匹配度最高的单个组件。 ## 3. 与本项目的映射 | 我们的事件/对象(`packages/contracts/src/*`) | agentui 对应组件 | 需要改造的点 | |---|---|---| | `run_started`(`agent-events.ts`) | 无对应"轮次开始"组件 | 需自建,可借 `Message` 起新一行 + `AgentProgress` 计时 | | `text_delta` | `StreamingResponse` | 该组件是整段替换模型,需要自己把增量 `delta` 拼接成累积字符串再喂入;无逐字符/光标效果 | | `thinking_delta` | `AgentActivity`(`contentType="text"`)/`ReasoningText` | 都不满足"可展开查看完整推理文本"——`AgentActivity` 完成后只留"Thought for Ns"摘要,`ReasoningText` 无历史;需要在其骨架上新增可展开的完整文本区 | | `tool_call_start`/`tool_call_end`(字段 `capabilityId`/`toolCallId`/`inputSummary`/`outputSummary`/`isError`) | `ToolResult` | 状态映射 running→running、!isError→success、isError→error 基本可套;`inputSummary`/`outputSummary` 对应 `ToolResultOutput` 折叠内容,耗时对应 `meta` | | `tool_call_rejected`(带 `toolName`/`reason`) | `ToolApproval.denied` | **需要改造**:库里 `denied` 只是三按钮里的弱化态,不够醒目;需要新增一个突出的"已拒绝"卡片(destructive 材质 + 拒绝原因文案),不能照抄 | | `compaction`(`phase`/`reason`/`status`) | 无对应组件 | 该库没有"上下文压缩"概念,需全新设计系统提示卡片 | | `run_completed`/`run_failed`(含 `AgentUsageTotals`、`budget` 超限原因) | `StreamingResponse.status="complete"/"error"` | 可复用完成态操作条,失败态需要自己接超限原因(`tool_calls/duration/tokens/cost`)文案 | | Capability `scope`/`riskLevel`/`idempotent`(`capability.ts`)、`idempotencyKey`(`agent.ts` `CapabilityInvocation`) | `ToolApproval.parameters` | 只有 key/value 参数列表,**没有风险等级视觉(红/黄/绿)、没有幂等键/审计信息位**,需要在参数区之外加风险徽章 + 审计元数据条 | | `Approval`(`agent.ts` ~222,`status: pending\|approved\|rejected\|revoked\|expired\|consumed`) | `ApprovalCard`/`ToolApproval` | 状态集不对齐:我们多出 `revoked/expired/consumed`,库多出 `submitting/changes-requested/answered`,需要做一层状态映射并砍掉/新增对应视觉 | | `AgentGoalReport`(`modelText` vs `buildGoalReport()` 系统汇总)、UI `FinalReport()`(`agent-conversation.tsx`,"模型说的"/"系统核实的结果"双栏) | 无对应组件 | **该库完全没有"声明 vs 核实"双轨展示**,`StreamingResponse` 只有单一正文 + `Citations`;需要新写一张"系统核实结果"卡片,可能借 `ToolResult` 或 `Citations` 的折叠壳改造 | | `AgentBudget`/`AgentUsage`、`BudgetBar()`+`Meter()`(`agent-conversation.tsx`,四项:工具调用/token/时长/费用) | `UsageDashboard` | 该组件是历史用量仪表盘(按天图表 + 汇总表),**没有"当前额度 vs 已用"的进度条**,量级和展示逻辑都不同,预算条要另写 | | ~~`AgentRunMode`(`execute`/`plan_only`)~~ | — | AG-2 已取消运行模式;"先看方案再执行"是输入区的开关(`scope.proposeFirst`) | ## 4. 设计纪律冲突 对照 `docs/design/README.md` 的硬规则(禁原色/十六进制/调色板色/字面量字号/字面量时长/裸 `rounded`): - **调色板色(命中"禁止"规则)**:`ApprovalCard`/`ToolApproval`/`TodoList`/`CodeBlock`/`FileDiff` 大量使用 `text-emerald-600`、`text-rose-600`、`text-amber-600`、`text-blue-600`(及各自 `dark:` 变体)表示成功/失败/等待/进行中,直接对应我们要求的 `success`/`destructive`/`warning`/(进行中无专用状态色,需另议)。 - **十六进制硬编码(命中"禁止"规则)**:`UsageDashboard` 图表配色写死 `--usage-first:#0e857e` 等 4 组变量,亮暗各一套。按我们规则,图表这类"只接受颜色值的地方"允许组件内常量表 + `token-allow` 注释,但当前写法未走语义 token,需要重新登记。 - **字号**:多用 Tailwind 语义档 `text-xs`/`text-sm`,不是字面量 `text-[13px]`,和我们规则里"Tailwind 档继续用"部分相容,但两套字号阶梯(我们的 `micro/label/meta/caption/body/control...`)档位不完全对齐(如我们 `caption=13px` 在 Tailwind 默认档里没有对应),混用会出现两套字号语言,需要逐组件按角色替换成我们的 `text-*` token。 - **圆角**:`rounded-md/lg/xl/2xl/full` 与我们"控件/输入/卡片/面板/药丸"的角色划分基本对齐,迁移成本低;但我们另有 `rounded-tight`/`rounded-slab` 等角色名,复制后要重新按角色套。 - **动效时长**:`docs/motion-patterns` 文档直接写 `duration: 0.22`/`0.18`/`0.12` 等字面量秒数,命中"禁止 `duration-N`"类规则,需要批量替换成 `duration-micro/base/layout` 具名档。 - **材质体系**:agentui 全部是纯 shadcn 语义色平面卡片(`bg-muted`/`bg-background`/`border-border`),没有玻璃拟态、没有 `backdrop-blur`,与我们三层材质(`pg-surface`/`pg-slab`/`pg-float`)是完全不同的视觉语言;复制后每个容器要重新判断属于贴边/内容卡/悬浮层的哪一层并换成对应 `pg-*` 类,不能直接沿用 `bg-muted`。 改造成本估算(以复制源码后逐项替换为准): - 调色板色 → 语义状态色:每组件 5~15 处 className,18 个组件全量约 2~3 人日。 - 十六进制清理(仅 `UsageDashboard` 4 组图表色):新增语义 token 并跑 `deno task gen:tokens`,约 0.5 人日。 - 字号/圆角对齐:圆角基本零成本,字号逐组件替换约 0.5 人日。 - 动效时长具名化:批量替换,约 0.5 人日。 - 材质分层改造(成本最大项):需要设计判断 + 重写外层 class,尤其 `ChatApp` 整合布局改动最大,预估 2~4 人日(取决于选用组件数量)。 - 全量 18 个组件改造合计约 5~8 人日;若只挑第 5 节列出的核心 6~8 个组件,约 3~4 人日。 ## 5. 建议采用方案 **优先"复制源码改 token",不建议"只借鉴交互自己写"。** 理由:组件本就是自包含源码(`registryDependencies: []`,不依赖 Radix 基座),复制后完全在自己仓库维护,和门户现有 shadcn 组件的接入方式一致;MIT 协议允许修改与内部使用,仅需保留源码头部版权声明;`MessageScroller` 的跟随阈值、`ToolResult`/`TodoList` 的折叠状态机等细节比从零设计更省时间。只有个别极简组件(如 `Citations` 的行内角标)从零写可能比适配 `motion` 依赖更快,可视情况自写。 落地优先顺序(按覆盖我们 9 类事件/对象的紧迫度 + 改造成本排序): 1. `Message` + `MessageBubble` + `MessageScroller` —— 会话主干,先落地,材质改造集中在气泡外壳。 2. `StreamingResponse` —— 承载 `text_delta`,需先验证"整段替换"模型能否套我们的增量拼接。 3. `ToolResult` + `ToolApproval` —— 覆盖 `tool_call_*` 与 approval 对象;`tool_call_rejected` 需新增比 `denied` 更醒目的拒绝态。 4. `TodoList` —— `agent.plan` 步骤清单(会话页顶部;抽屉压缩为一行进度)。 5. `ApprovalCard` —— 覆盖非工具类通用审批。 6. `CodeBlock`/`FileDiff` —— 系统核实结果与 diff 类工具输出的展示载体。 7. `Citations` —— 改造成本最低,可较早排期。 8. `AgentActivity`/`ReasoningText` —— 只借鉴时间线骨架与"Thought for Ns"摘要交互,不直接复制状态机(缺"保留完整推理文本可展开"这一我们需要的能力)。 9. `UsageDashboard` —— 仅作历史用量图表参考;预算条(`BudgetBar`/`Meter`)在此库能力范围之外,需独立新写。 10. `AISidebar`/`ChatApp` —— 不支持会话分支、无搜索,只作整体布局排版参考,不建议直接采用其资源树数据模型。 ## 6. 已采用清单(W4-F,2026-09-27) 源码取自 `ashish200729/agentui@b2b752b`(MIT),放在 `apps/web/app/components/agentui/`,每个文件头保留版权与来源, 许可证全文见同目录 `LICENSE-agentui.md`。统一改造:去掉 `motion`/`shiki`/Next.js 依赖(入场用 tw-animate 的 `animate-in`,折叠用 CSS grid-rows 过渡,全部带 `motion-reduce:` 关闭);调色板色 → `success`/`warning`/`destructive` 并配文字;字号/圆角/时长/缓动换项目 token;文案中文化。 | 上游组件 | 本项目文件 | 用在哪 | 主要改造 | |---|---|---|---| | `Message`(+`message-context`) | `message.tsx` | 会话流的用户/Agent/系统行 | 新增 `from="system"`;`MessageMarker` 作压缩提示;`MessageTyping` 用 animate-pulse | | `MessageBubble` | `message-bubble.tsx` | 用户消息、失败/超预算/中止提示 | 变体换语义底色,新增 `warning`;去 layout 动画 | | `MessageScroller` | `message-scroller.tsx` | 会话页与侧栏抽屉的滚动区 | 保留跟随阈值状态机;只有"往上翻"才离开跟随;新增"回到底部"按钮;首屏回放直接跳底;log 只播报新增行 | | `StreamingResponse` | `streaming-response.tsx` | 流式文本、"模型说的"栏 | 新增流式光标;去点赞/点踩;来源接 `CitationList` | | `ToolResult` | `tool-result.tsx` | 工具调用卡片 | 新增 `badges`(类别/scope/风险/幂等/恢复补记)与收起摘要;输入输出用 `CodeBlock` | | `ToolApproval` | `tool-approval.tsx` | **拒绝态**(白名单拦截 `tool_call_rejected` 与授权校验失败的 `unauthorized`) | 新增 `rejected` 醒目态:destructive 材质 + `role=alert` + 原因 + 授权说明;去掉 "Always allow" | | `TodoList` | `todo-list.tsx` | `agent.plan` 步骤清单(新增 `skipped` 态) | SVG 状态图标保留,弹簧动画换 CSS;新增 `error` 态 | | `ApprovalCard` | `approval-card.tsx` | Approval 对象(计划、payload hash、版本、到期、风险) | 状态集对齐 `pending/approved/rejected/revoked/expired/consumed`(+`submitting`);去掉问答模式;非管理员会话按钮禁用并说明 | | `CodeBlock`(+`agent-code`) | `code-block.tsx` | 工具输入/输出 | 不做语法高亮;JSON 自动缩进 | | `FileDiff` | `file-diff.tsx` | 系统核实栏的"入库目标" | +/− 符号保留,底色换 success/destructive 洗层 | | `Citations` | `citations.tsx` | 系统核实栏的下载任务/整理记录链接 | 去 favicon;站内链接走 `Link` | | `PromptInput` | `prompt-input.tsx` | 新目标与追加消息输入框 | 保留自增高、Enter 发送/Shift+Enter 换行/组字不发送、运行中"停止";去模型选择与动作菜单,换 `toolbar` 槽 | | `AgentDisclosure` | `disclosure.tsx` | 所有折叠区 | motion → CSS grid-rows;收起时 `inert` | 未采用:`AgentActivity`/`ReasoningText`(思考块自写,保留完整推理文本可展开)、`UsageDashboard`(预算条自写,带超限态)、 `AISidebar`/`ChatApp`(会话列表自写:状态筛选 + 目标/状态/模式/时间/用量)、`ImageGeneration`。 已知限制:幂等键来自审计事件(`/audit-events` 按 `payload.agentRunId` 过滤,最多翻 3 页、按同能力先后与工具调用配对), 较早的运行可能取不到;审批卡片的计划来自工具调用输入/输出里能解析到的 `planId`(摘要被截断时可能漏)与服务端汇报。