x-card

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

🎯 技能定位

🎯 技能定位

本技能覆盖
@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 实现,使 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、流式处理 —— 本技能不涉及
bash
npm install @ant-design/x-card
导出内容:
typescript
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; // 容器:接收命令,持有 Catalog 映射
XCard.Card; // 渲染器:按 id 渲染单个 Surface
包名职责
@ant-design/x-card
A2UI 协议的 React 渲染器 ——
XCard.Box
、
XCard.Card
、Catalog API
@ant-design/x
聊天 UI 组件(Bubble、Sender 等)—— 本技能不涉及
@ant-design/x-sdk
数据 Provider、流式处理 —— 本技能不涉及
bash
npm install @ant-design/x-card
导出内容:
typescript
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; // 容器:接收命令,持有 Catalog 映射
XCard.Card; // 渲染器:按 id 渲染单个 Surface

🗂️ 组件架构

🗂️ 组件架构

XCard.Box
├── 持有:catalogMap、surfaceCatalogMap
├── 分发:commands → 所有 XCard.Card 子组件
├── 汇聚:所有 Card 的 onAction 事件
└── XCard.Card (id="surface-a")
│   ├── 持有:组件树、数据模型、commandVersion
│   └── 处理:数据绑定解析、Action 触发
└── XCard.Card (id="surface-b")
    └── ...
XCard.Box
├── 持有:catalogMap、surfaceCatalogMap
├── 分发:commands → 所有 XCard.Card 子组件
├── 汇聚:所有 Card 的 onAction 事件
└── XCard.Card (id="surface-a")
│   ├── 持有:组件树、数据模型、commandVersion
│   └── 处理:数据绑定解析、Action 触发
└── XCard.Card (id="surface-b")
    └── ...

XCard.Box 属性

XCard.Box 属性

typescript
interface BoxProps {
  commands?: (XAgentCommand_v0_9 | XAgentCommand_v0_8)[];
  /** 组件名称必须以大写字母开头 */
  components?: Record<string, React.ComponentType<any>>;
  onAction?: (payload: ActionPayload) => void;
  children?: React.ReactNode; // 应包含 XCard.Card 元素
}
typescript
interface BoxProps {
  commands?: (XAgentCommand_v0_9 | XAgentCommand_v0_8)[];
  /** 组件名称必须以大写字母开头 */
  components?: Record<string, React.ComponentType<any>>;
  onAction?: (payload: ActionPayload) => void;
  children?: React.ReactNode; // 应包含 XCard.Card 元素
}

XCard.Card 属性

XCard.Card 属性

typescript
interface CardProps {
  id: string; // 要渲染的 surfaceId
}
typescript
interface CardProps {
  id: string; // 要渲染的 surfaceId
}

ActionPayload

ActionPayload

typescript
interface ActionPayload {
  name: string; // 来自 action.event.name
  surfaceId: string; // 触发动作的 Surface
  /**
   * 组件传递的上下文,已自动解析 path 引用
   *
   * 对于 action.event.context 中使用 { path: "xxx" } 格式的字段:
   * - X-Card 会自动将其解析为 { value: "实际值" } 格式
   * - 其他属性(如 label)会被保留
   *
   * 示例输入配置:
   *   { username: { path: "/form/username", label: "用户名" } }
   *
   * 示例解析后 context:
   *   { username: { value: "张三", label: "用户名" } }
   */
  context: Record<string, any>;
}
typescript
interface ActionPayload {
  name: string; // 来自 action.event.name
  surfaceId: string; // 触发动作的 Surface
  /**
   * 组件传递的上下文,已自动解析 path 引用
   *
   * 对于 action.event.context 中使用 { path: "xxx" } 格式的字段:
   * - X-Card 会自动将其解析为 { value: "实际值" } 格式
   * - 其他属性(如 label)会被保留
   *
   * 示例输入配置:
   *   { username: { path: "/form/username", label: "用户名" } }
   *
   * 示例解析后 context:
   *   { username: { value: "张三", label: "用户名" } }
   */
  context: Record<string, any>;
}

🚀 快速决策指南

🚀 快速决策指南

当你需要...优先阅读
配置 XCard.Box + XCard.CardUSAGE.md → 基础配置
从 Agent 发送命令到卡片COMMANDS.md
注册自定义组件 CatalogCATALOG.md → 本地 Catalog
把组件属性绑定到实时数据DATA_BINDING.md
处理用户交互/表单提交ACTIONS.md
构建流式渐进 UIUSAGE.md → 流式渲染
从 v0.8 迁移到 v0.9COMMANDS.md → v0.8 vs v0.9
查询完整类型定义API.md
当你需要...优先阅读
配置 XCard.Box + XCard.CardUSAGE.md → 基础配置
从 Agent 发送命令到卡片COMMANDS.md
注册自定义组件 CatalogCATALOG.md → 本地 Catalog
把组件属性绑定到实时数据DATA_BINDING.md
处理用户交互/表单提交ACTIONS.md
构建流式渐进 UIUSAGE.md → 流式渲染
从 v0.8 迁移到 v0.9COMMANDS.md → v0.8 vs v0.9
查询完整类型定义API.md

🛠 推荐工作流

🛠 推荐工作流

  1. 定义 Catalog —— 注册本地 Catalog 或使用 A2UI Basic Catalog URL。
  2. 注册自定义组件 —— 通过
    XCard.Box
    的
    components
    prop 传入。
  3. 构建 React 树 —— 用
    XCard.Box
    包裹,每个 Surface 对应一个
    XCard.Card
    。
  4. 注入命令 —— 将
    XAgentCommand_v0_9[]
    传入
    commands
    prop(通常来自 Agent 流式响应)。
  5. 处理 Action —— 在
    onAction
    中接收
    ActionPayload
    ,追加新命令作为响应。
  1. 定义 Catalog —— 注册本地 Catalog 或使用 A2UI Basic Catalog URL。
  2. 注册自定义组件 —— 通过
    XCard.Box
    的
    components
    prop 传入。
  3. 构建 React 树 —— 用
    XCard.Box
    包裹,每个 Surface 对应一个
    XCard.Card
    。
  4. 注入命令 —— 将
    XAgentCommand_v0_9[]
    传入
    commands
    prop(通常来自 Agent 流式响应)。
  5. 处理 Action —— 在
    onAction
    中接收
    ActionPayload
    ,追加新命令作为响应。

最小可运行示例

最小可运行示例

tsx
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';

// 1. 定义并注册本地 Catalog
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);

// 2. 自定义组件实现
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>;

// 3. 构建命令(来自 Agent 流)
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',
    },
  },
];

// 4. 渲染
export default function App() {
  const [cmdQueue, setCmdQueue] = useState<XAgentCommand_v0_9[]>(commands);

  const handleAction = (payload: ActionPayload) => {
    console.log('Action:', payload.name, payload.context);
    // 根据 Agent 响应追加新命令
    setCmdQueue((prev) => [...prev /* 新命令 */]);
  };

  return (
    <XCard.Box commands={cmdQueue} components={{ Text, Button }} onAction={handleAction}>
      <XCard.Card id="welcome" />
    </XCard.Box>
  );
}
tsx
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';

// 1. 定义并注册本地 Catalog
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);

// 2. 自定义组件实现
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>;

// 3. 构建命令(来自 Agent 流)
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',
    },
  },
];

// 4. 渲染
export default function App() {
  const [cmdQueue, setCmdQueue] = useState<XAgentCommand_v0_9[]>(commands);

  const handleAction = (payload: ActionPayload) => {
    console.log('Action:', payload.name, payload.context);
    // 根据 Agent 响应追加新命令
    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 数据丢失。
  • 每条命令必须包含
    "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
→ 将响应作为命令传入
场景技能组合
AI 对话 + 结构化卡片响应
use-x-chat
+
x-components
+
x-card
独立的 Agent 表单 UI仅
x-card
流式 Markdown + 卡片侧边栏
x-markdown
+
x-card
HTTP 流式 Agent 数据注入卡片
x-request
→ 将响应作为命令传入

🔗 参考资源

🔗 参考资源

  • USAGE.md —— 配置指南、流式模式、多 Surface 示例
  • COMMANDS.md —— 四种 A2UI v0.9 命令类型,v0.8 vs v0.9 差异
  • DATA_BINDING.md —— JSON Pointer 路径、动态类型、双向绑定、模板迭代
  • ACTIONS.md —— Action 定义、ActionPayload、表单提交模式
  • CATALOG.md —— 本地 Catalog 注册、远程 URL 加载、自定义组件 Schema
  • API.md —— Box、Card、命令、Catalog、Action 的完整 TypeScript 类型
  • USAGE.md —— 配置指南、流式模式、多 Surface 示例
  • COMMANDS.md —— 四种 A2UI v0.9 命令类型,v0.8 vs v0.9 差异
  • DATA_BINDING.md —— JSON Pointer 路径、动态类型、双向绑定、模板迭代
  • ACTIONS.md —— Action 定义、ActionPayload、表单提交模式
  • CATALOG.md —— 本地 Catalog 注册、远程 URL 加载、自定义组件 Schema
  • API.md —— Box、Card、命令、Catalog、Action 的完整 TypeScript 类型

官方文档

官方文档