| name | x-card |
| version | 2.7.0 |
| description | 当需要用 @ant-design/x-card 让 AI Agent 动态渲染富交互 UI 时使用——涵盖 XCard.Box、XCard.Card、A2UI v0.9 命令、数据绑定、Catalog、Actions 和流式渲染模式。 |
🎯 技能定位
本技能覆盖 @ant-design/x-card —— A2UI 协议的 React 实现,使 AI Agent 能够通过结构化 JSON 命令流动态渲染富交互 UI。
覆盖范围:
XCard.Box + XCard.Card 组件用法
- A2UI v0.9 命令类型:
createSurface、updateComponents、updateDataModel、deleteSurface
- 自定义组件注册与 Catalog 管理
- 通过 JSON Pointer 路径(RFC 6901)进行数据绑定
- Action 处理 —— 将用户事件回传给 Agent
- 流式渐进渲染模式
- v0.8 ↔ v0.9 协议差异
范围说明:v0.9 是推荐协议,v0.8 仅为向后兼容保留——所有新功能请使用 v0.9。
目录导航
📦 技术栈概览
| 包名 | 职责 |
|---|
@ant-design/x-card | A2UI 协议的 React 渲染器 —— XCard.Box、XCard.Card、Catalog API |
@ant-design/x | 聊天 UI 组件(Bubble、Sender 等)—— 本技能不涉及 |
@ant-design/x-sdk | 数据 Provider、流式处理 —— 本技能不涉及 |
npm install @ant-design/x-card
导出内容:
import { XCard, registerCatalog, loadCatalog, validateComponent, clearCatalogCache } from '@ant-design/x-card';
import type {
XAgentCommand_v0_9,
XAgentCommand_v0_8,
ActionPayload,
Catalog,
CatalogComponent
} from '@ant-design/x-card';
XCard.Box;
XCard.Card;
🗂️ 组件架构
XCard.Box
├── 持有:catalogMap、surfaceCatalogMap
├── 分发:commands → 所有 XCard.Card 子组件
├── 汇聚:所有 Card 的 onAction 事件
└── XCard.Card (id="surface-a")
│ ├── 持有:组件树、数据模型、commandVersion
│ └── 处理:数据绑定解析、Action 触发
└── XCard.Card (id="surface-b")
└── ...
XCard.Box 属性
interface BoxProps {
commands?: (XAgentCommand_v0_9 | XAgentCommand_v0_8)[];
components?: Record<string, React.ComponentType<any>>;
onAction?: (payload: ActionPayload) => void;
children?: React.ReactNode;
}
XCard.Card 属性
interface CardProps {
id: string;
}
ActionPayload
interface ActionPayload {
name: string;
surfaceId: string;
context: Record<string, any>;
}
🚀 快速决策指南
🛠 推荐工作流
- 定义 Catalog —— 注册本地 Catalog 或使用 A2UI Basic Catalog URL。
- 注册自定义组件 —— 通过
XCard.Box 的 components prop 传入。
- 构建 React 树 —— 用
XCard.Box 包裹,每个 Surface 对应一个 XCard.Card。
- 注入命令 —— 将
XAgentCommand_v0_9[] 传入 commands prop(通常来自 Agent 流式响应)。
- 处理 Action —— 在
onAction 中接收 ActionPayload,追加新命令作为响应。
最小可运行示例
import React, { useState } from 'react';
import { XCard, registerCatalog } from '@ant-design/x-card';
import type { XAgentCommand_v0_9, ActionPayload, Catalog } from '@ant-design/x-card';
const myCatalog: Catalog = {
catalogId: 'local://my_catalog.json',
components: {
Text: {
type: 'object',
properties: { text: { type: 'string' }, variant: { type: 'string' } },
required: ['text']
},
Button: {
type: 'object',
properties: { text: { type: 'string' }, action: {} },
required: ['text']
}
}
};
registerCatalog(myCatalog);
const Text: React.FC<{ text: string; variant?: string }> = ({ text, variant }) => (
<p className={`text-${variant ?? 'body'}`}>{text}</p>
);
const Button: React.FC<{ text: string; onAction?: (ctx: any) => void; action?: any }> = ({
text,
onAction,
action
}) => <button onClick={() => onAction?.(action?.event?.context ?? {})}>{text}</button>;
const commands: XAgentCommand_v0_9[] = [
{
version: 'v0.9',
createSurface: {
surfaceId: 'welcome',
catalogId: 'local://my_catalog.json'
}
},
{
version: 'v0.9',
updateComponents: {
surfaceId: 'welcome',
components: [
{ id: 'root', component: 'Column', children: ['title', 'btn'] },
{ id: 'title', component: 'Text', text: { path: '/user/name' }, variant: 'h1' },
{
id: 'btn',
component: 'Button',
text: '开始',
action: { event: { name: 'start', context: {} } }
}
]
}
},
{
version: 'v0.9',
updateDataModel: {
surfaceId: 'welcome',
path: '/user/name',
value: 'Alice'
}
}
];
export default function App() {
const [cmdQueue, setCmdQueue] = useState<XAgentCommand_v0_9[]>(commands);
const handleAction = (payload: ActionPayload) => {
console.log('Action:', payload.name, payload.context);
setCmdQueue(prev => [...prev ]);
};
return (
<XCard.Box commands={cmdQueue} components={{ Text, Button }} onAction={handleAction}>
<XCard.Card id="welcome" />
</XCard.Box>
);
}
🚨 开发规则
- 每条命令必须包含
"version": "v0.9" —— 缺少会导致协议拒绝。
- 每个 Surface 有且仅有一个
id: "root" 组件 —— 这是组件树的根节点。
- 只用扁平邻接表 —— 不要把组件对象嵌套在其他组件对象内;子组件只能通过
id 字符串引用。
- 结构与数据分离 ——
updateComponents 负责布局,updateDataModel 负责内容/状态。
- 挂载前先注册 Catalog —— 在组件树渲染之前调用
registerCatalog()。
components map 传给 XCard.Box,而不是 XCard.Card —— Box 负责向所有 Card 分发。
- 不要内联重建
components 对象 —— 用 useMemo 或模块级常量保持稳定,避免不必要的重渲染。
- 表单输入组件必须用
value: { path: "..." } 双向绑定 —— 字面量值不会更新数据模型。
- 流式场景:追加新命令到数组而不是替换整个数组 —— Card 会增量处理差异。
action.event.context 中的路径是写入目标 —— 它们指向用户输入数据在数据模型中的存储位置,不要作为读取来源解析。
- Action context 中的 path 引用会被自动解析 —— 触发 action 时,X-Card 会自动将 action 配置中的
{ path: "xxx" } 转换为 { value: "实际值" } 格式。这适用于 v0.9(action.event.context = { key: { path } })和 v0.8(action.context = [{ key, value: { path } }])两种格式。
- v0.8 的
action.context 是数组格式 —— [{ key, value: { path } }],与 v0.9 的对象格式 { key: { path } } 不同,混用会导致 action 数据丢失。
🤝 技能协作
| 场景 | 技能组合 |
|---|
| AI 对话 + 结构化卡片响应 | use-x-chat + x-components + x-card |
| 独立的 Agent 表单 UI | 仅 x-card |
| 流式 Markdown + 卡片侧边栏 | x-markdown + x-card |
| HTTP 流式 Agent 数据注入卡片 | x-request → 将响应作为命令传入 |
🔗 参考资源
官方文档