x-card について
x-cardは、`@ant-design/x-card`の利用方法を扱うスキルです。構造化コマンドからAIエージェント向けのインタラクティブなUIを描画するReactレンダラーについて、コンポーネント設定、データバインディング、アクション、ストリーミングを説明し、v0.9を推奨しつつv0.8にも対応します。
主な機能
- 01ReactでのA2UIコマンド描画
- 02コンポーネントカタログの管理
- 03JSON Pointerによるデータバインディング
- 04ユーザー操作の処理
- 05ストリーミング更新の段階的描画
- 06A2UI v0.8とv0.9への対応
活用シーン
- 01エージェント主導のフォームUI構築
- 02エージェント応答のインタラクティブ表示
- 03カードUIの段階的なストリーミング表示
- 04A2UIコマンドのv0.8からの移行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ant-design/ x --skill x-cardインストール後、Claude Code で/x-cardを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
reference/ACTIONS.mdAPI.mdCATALOG.mdCOMMANDS.mdDATA_BINDING.mdINTRODUCTION.mdUSAGE.md詳細
🎯 スキルの位置づけ
このスキルでは @ant-design/x-card を扱います — A2UI プロトコルの React 実装です。エージェントが構造化された JSON コマンドストリームを通じて、リッチでインタラクティブな UI を動的にレンダリングできます。
対象となる内容は次のとおりです。
XCard.BoxとXCard.Cardコンポーネントの使用方法- A2UI v0.9 のコマンドタイプ:
createSurface、updateComponents、updateDataModel、deleteSurface - カスタムコンポーネントの登録とカタログ管理
- JSON Pointer パス(RFC 6901)によるデータバインディング
- アクション処理 — ユーザーイベントをエージェントに送信
- ストリーミングによる段階的なレンダリングパターン
- v0.8 と v0.9 のプロトコルの違い
対象範囲:推奨プロトコルは v0.9 です。v0.8 は後方互換性のためにのみサポートされています。新しい開発ではすべて v0.9 を使用してください。
目次
📦 パッケージの概要
| パッケージ | 役割 |
|---|---|
@ant-design/x-card | A2UI プロトコル用の React レンダラー — XCard.Box、XCard.Card、カタログ API |
@ant-design/x | チャット UI コンポーネント(Bubble、Sender など)— ここでは扱いません |
@ant-design/x-sdk | データプロバイダー、ストリーミング — ここでは扱いません |
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';
// Subcomponents
XCard.Box; // Container: receives commands, owns catalog maps
XCard.Card; // Renderer: renders a single surface by id
🗂️ コンポーネントのアーキテクチャ
XCard.Box
├── owns: catalogMap, surfaceCatalogMap
├── dispatches: commands → all XCard.Card children
├── aggregates: onAction events from all Cards
└── XCard.Card (id="surface-a")
│ ├── owns: component tree, data model, commandVersion
│ └── resolves: data bindings, triggers actions
└── XCard.Card (id="surface-b")
└── ...
XCard.Box の Props
interface BoxProps {
commands?: (XAgentCommand_v0_9 | XAgentCommand_v0_8)[];
/** Component names must start with an uppercase letter (React component convention) */
components?: Record<string, React.ComponentType<any>>;
onAction?: (payload: ActionPayload) => void;
children?: React.ReactNode; // Should contain XCard.Card elements
}
XCard.Card の Props
interface CardProps {
id: string; // surfaceId to render
}
ActionPayload
interface ActionPayload {
name: string; // from action.event.name
surfaceId: string; // which surface triggered it
/**
* Context passed by component, with path references automatically resolved.
*
* For action.event.context fields using { path: "xxx" } format:
* - X-Card automatically resolves them to { value: "actual_value" }
* - Other properties (like label) are preserved
*
* Example input config:
* { username: { path: "/form/username", label: "用户名" } }
*
* Example resolved context:
* { username: { value: "张三", label: "用户名" } }
*/
context: Record<string, any>;
}
🚀 クイックスタートの選択ガイド
| 次のことを行うには… | まず読むもの |
|---|---|
| XCard.Box と XCard.Card をセットアップする | USAGE.md → 基本セットアップ |
| エージェントからカードにコマンドを送信する | COMMANDS.md |
| カスタムコンポーネントカタログを登録する | CATALOG.md → ローカルカタログ |
| コンポーネントの Props をライブデータにバインドする | DATA_BINDING.md |
| ユーザー操作やフォーム送信を処理する | ACTIONS.md |
| ストリーミングによる段階的な UI を構築する | USAGE.md → ストリーミング |
| v0.8 から v0.9 に移行する | COMMANDS.md → v0.8 と v0.9 の違い |
| すべての Props の型を確認する | API.md |
🛠 推奨ワークフロー
- カタログを定義する — ローカルカタログを登録するか、A2UI Basic Catalog URL を使用します。
- カスタムコンポーネントを登録する —
XCard.Boxのcomponentsprop 経由で渡します。 - React ツリーを作成する — サーフェスを
XCard.Boxでラップし、サーフェスごとにXCard.Cardを追加します。 - コマンドを渡す —
XAgentCommand_v0_9[]をcommandsprop に渡します(通常はストリーミング中のエージェント応答から渡します)。 - アクションを処理する —
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';
// 1. Define and register local 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. Custom component implementations
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. Build commands (from agent stream)
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: 'Start',
action: { event: { name: 'start', context: {} } },
},
],
},
},
{
version: 'v0.9',
updateDataModel: {
surfaceId: 'welcome',
path: '/user/name',
value: 'Alice',
},
},
];
// 4. Render
export default function App() {
const [cmdQueue, setCmdQueue] = useState<XAgentCommand_v0_9[]>(commands);
const handleAction = (payload: ActionPayload) => {
console.log('Action:', payload.name, payload.context);
// Append new commands based on agent response
setCmdQueue((prev) => [...prev /* new commands */]);
};
return (
<XCard.Box commands={cmdQueue} components={{ Text, Button }} onAction={handleAction}>
<XCard.Card id="welcome" />
</XCard.Box>
);
}
🚨 開発ルール
- すべてのコマンドに
"version": "v0.9"を必ず含めてください — 省略するとプロトコルによって拒否されます。 - 各サーフェスのコンポーネントツリーに
id: "root"コンポーネントを1つだけ配置してください — これがツリーのルートです。 - フラットな隣接リストのみを使用してください — コンポーネントオブジェクトを別のコンポーネントオブジェクトの中にネストせず、必ず
id文字列で子を参照してください。 - 構造とデータを分けてください — レイアウトには
updateComponents、コンテンツや状態にはupdateDataModelを使用します。 - マウント前にカタログを登録してください — コンポーネントツリーがレンダリングされる前に
registerCatalog()を呼び出してください。 componentsマップはXCard.CardではなくXCard.Boxに渡してください — Box がすべての Card に配布します。componentsオブジェクトをインラインで再作成しないでください — 再レンダリングを避けるため、useMemoまたはモジュールレベルの定数で安定させてください。- 入力コンポーネントには双方向バインディング用の
value: { path: "..." }が必要です — リテラル値ではデータモデルが更新されません。 - ストリーミングの場合:コマンドを置き換えるのではなく、配列に新しいコマンドを追加してください — Card が差分を段階的に処理します。
action.event.contextのパスは書き込み先です — データモデル内でユーザー入力データが格納される場所を指します。読み取り元として解決しないでください。- アクションコンテキスト内のパス参照は自動的に解決されます — アクションがトリガーされると、X-Card はアクション設定内の
{ path: "xxx" }を onAction ペイロード内の{ value: "actual_value" }に変換します。これは v0.9(action.event.context = { key: { path } })形式と v0.8(action.context = [{ key, value: { path } }])形式の両方で機能します。
🤝 スキル連携
| シナリオ | スキルの組み合わせ |
|---|---|
| 構造化されたカード応答を使う AI チャット | use-x-chat + x-components + x-card |
| 単独エージェント向けフォーム UI | x-card のみ |
| ストリーミング Markdown + カードのサイドパネル | x-markdown + x-card |
| エージェントからカードへの HTTP ストリーミング | x-request → 応答をコマンドとして渡す |
🔗 参考資料
- USAGE.md — セットアップガイド、ストリーミングのパターン、複数サーフェスの例
- COMMANDS.md — 4種類すべての A2UI v0.9 コマンドタイプ、v0.8 と v0.9 の差分
- DATA_BINDING.md — JSON Pointer パス、動的型、双方向バインディング、テンプレート反復
- ACTIONS.md — アクション定義、ActionPayload、フォーム送信パターン
- CATALOG.md — ローカルカタログの登録、リモート URL の読み込み、カスタムコンポーネントのスキーマ
- API.md — Box、Card、コマンド、カタログ、アクションの TypeScript 型の詳細
公式ドキュメント
コメント
Markdown 対応