# Adaptive Card Designer URL: /docs/web/components/adaptive-card-designer Visual drag-and-drop designer for Microsoft Adaptive Cards 1.5 / 1.6 payloads. Three-pane editor (toolbox · canvas · structure / properties) with paired JSON editors, live preview, undo / redo, and theme / width controls — backed by the in-repo Adaptive Card renderer. **Demo:** ```tsx 'use client'; import { useState } from 'react'; import { AdaptiveCardDesigner } from '@docyrus/ui/components/adaptive-card-designer'; import { type AdaptiveCardPayload } from '@docyrus/ui/components/adaptive-card'; import { Button } from '@docyrus/ui/primitives/ui/button'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@docyrus/ui/primitives/ui/dialog'; import { LayoutTemplate, Plus } from 'lucide-react'; const STARTER: AdaptiveCardPayload = { type: 'AdaptiveCard', version: '1.5', body: [ { type: 'TextBlock', text: '**Welcome to the Adaptive Card Designer**', size: 'large', weight: 'bolder' }, { type: 'TextBlock', text: 'Pick elements from the toolbox on the left, or edit the JSON below. The canvas updates live.', wrap: true, isSubtle: true }, { type: 'FactSet', facts: [{ title: 'Status', value: 'Draft' }, { title: 'Owner', value: '${user.name}' }] } ], actions: [{ type: 'Action.OpenUrl', title: 'Adaptive Cards spec', url: 'https://adaptivecards.io/' }] }; const STARTER_DATA = { user: { name: 'Alice', role: 'Admin' } }; export function AdaptiveCardDesignerDemo() { const [open, setOpen] = useState(false); const [mode, setMode] = useState<'sample' | 'blank'>('sample'); const openDesigner = (next: 'sample' | 'blank') => { setMode(next); setOpen(true); }; return (
); } ``` ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/ui-adaptive-card-designer ``` **Dependencies:** - [@dnd-kit/core](https://www.npmjs.com/package/@dnd-kit/core) - [@monaco-editor/react](https://www.npmjs.com/package/@monaco-editor/react) - [lucide-react](https://www.npmjs.com/package/lucide-react) The designer renders its canvas with the [`AdaptiveCard`](/docs/web/components/adaptive-card) renderer, which is installed automatically as a registry dependency. ## Overview `AdaptiveCardDesigner` is a full visual authoring surface for [Adaptive Cards](https://adaptivecards.io/) payloads. Where [`AdaptiveCard`](/docs/web/components/adaptive-card) only *renders* a payload, the designer lets users *build* one — dragging elements from a toolbox, reordering and reparenting them in a structure tree, editing element properties in a side panel, and watching the result render live. The raw payload and the templating sample-data are both editable as JSON at the bottom, and the two views stay in sync. Typical use cases: - **Internal card authoring** — a no-code surface for ops / support teams to compose Teams-style cards and agent UI without hand-writing JSON. - **Template tooling** — author the payload that an LLM agent or automation later fills with `${...}` bindings, previewed against sample data. - **Embedded editors** — drop the designer into an automation-node config or a CMS, read the emitted payload back via `onChange`. The designer works on its own normalized node tree internally (stable per-node ids, uniform child *slots*) and serializes back to a standard `AdaptiveCardPayload` on every edit. ## Layout The designer is a three-pane layout with a toolbar above and paired JSON editors below: | Region | Purpose | |--------|---------| | **Toolbar** | New / undo / redo / copy, theme (light / dark / auto), canvas width (standard / wide / full), preview toggle. Buttons are individually hideable via `hideToolbarButtons`. | | **Toolbox** (left) | Draggable element / input / action types grouped into `elements`, `inputs`, `actions`, `advanced`. Append your own via `extraToolboxItems`. | | **Canvas** (center) | Live render of the card via ``. Click an element to select it; drop toolbox items directly onto it. | | **Structure** (right, top) | Collapsible tree of the card. Reorder / reparent by drag, select to edit. | | **Properties** (right, bottom) | Type-aware property editors for the selected node. | | **Payload editor** (bottom) | The live `AdaptiveCardPayload` JSON (Monaco). | | **Sample-data editor** (bottom) | The templating data JSON used to resolve `${...}` bindings in preview. | ## Usage ### Uncontrolled ```tsx import { AdaptiveCardDesigner } from '@docyrus/ui/components/adaptive-card-designer'; import { type AdaptiveCardPayload } from '@docyrus/ui/components/adaptive-card'; const starter: AdaptiveCardPayload = { type: 'AdaptiveCard', version: '1.5', body: [{ type: 'TextBlock', text: 'Hello, **Adaptive Cards**!', weight: 'bolder' }] }; console.log(payload, sampleData)} /> ``` ### Controlled Pass `payload` / `sampleData` to drive the designer from outside. `onChange` fires after every edit: ```tsx const [payload, setPayload] = useState(starter); const [data, setData] = useState({}); { setPayload(next.payload); setData(next.sampleData); }} /> ``` ### Read-only inspection `readOnly` disables every mutation — toolbox drag, drop zones, property edits, JSON edits, undo / redo / new, and keyboard shortcuts. Theme / width / preview / copy stay active, and selection remains enabled so users can click through the tree and inspect properties: ```tsx ``` ### Custom toolbox items Append your own draggable types alongside the built-ins. Each item supplies a `factory` that returns a fresh node on every drop: ```tsx import { Sparkles } from 'lucide-react'; import { type ToolboxItem } from '@docyrus/ui/components/adaptive-card-designer'; const extras: ToolboxItem[] = [ { id: 'Docyrus.UserChip', type: 'Docyrus.UserChip', label: 'User Chip', icon: Sparkles, group: 'advanced', keywords: ['user', 'chip', 'avatar'], factory: () => ({ __designerId: crypto.randomUUID(), type: 'Docyrus.UserChip', props: { userId: '' }, slots: {} }) } ]; }} /> ``` Pair `extraToolboxItems` (authoring) with `customElements` (rendering) so the canvas knows how to draw the type you just added. `customElements` and `hostConfig` are forwarded straight to the inner ``. ### Trimming the toolbar ```tsx ``` ## API Reference ### `AdaptiveCardDesignerProps` | Prop | Type | Default | Description | |------|------|---------|-------------| | `payload` | `AdaptiveCardPayload` | — | Current card payload (controlled). | | `defaultPayload` | `AdaptiveCardPayload` | — | Initial payload when uncontrolled. | | `sampleData` | `unknown` | — | Templating data resolved into the preview canvas (controlled). | | `defaultSampleData` | `unknown` | — | Initial sample data when uncontrolled. | | `onChange` | `(next: { payload: AdaptiveCardPayload; sampleData: unknown }) => void` | — | Fires after every payload or sample-data edit. | | `hostConfig` | `AdaptiveCardHostConfigOverride` | — | Forwarded to the inner ``. | | `customElements` | `Record` | `{}` | Forwarded to the inner ``. Pair with `extraToolboxItems` to author custom types. | | `defaultPreview` | `boolean` | `false` | Start in preview (render-only) mode rather than edit mode. | | `defaultTheme` | `DesignerTheme` | `'light'` | Initial canvas theme — `'light' \| 'dark' \| 'auto'`. | | `defaultWidth` | `DesignerWidth` | `'standard'` | Initial canvas width — `'standard' \| 'wide' \| 'full'`. | | `hideToolbarButtons` | `ToolbarButtonKey[]` | — | Hide specific toolbar buttons — `'new' \| 'theme' \| 'width' \| 'undo' \| 'redo' \| 'copy' \| 'preview'`. | | `extraToolboxItems` | `ToolboxItem[]` | — | Append additional toolbox items alongside the built-ins. | | `height` | `string` | `'70vh'` | Designer chrome height. | | `className` | `string` | — | Forwarded to the root element. | | `readOnly` | `boolean` | `false` | Disable all mutations. Theme / width / preview / copy and selection stay active. | | `aiAssistantOpen` | `boolean` | — | Controlled open state for the AI Assistant drawer. When provided the designer stops managing the open state internally — pair with `onAiAssistantOpenChange`. | | `onAiAssistantOpenChange` | `(open: boolean) => void` | — | Fired when the AI Assistant toolbar button toggles the drawer. | | `renderAiAssistant` | `(ctx: IAdaptiveCardAiAssistantRenderContext) => ReactNode` | — | Mounts a custom AI Assistant drawer body on the left of the designer. When set, the toolbar shows a Bot toggle that opens/closes the drawer; this render fn supplies the body (typically a `` or `` wrapper). Without it the AI button is hidden. | | `aiAssistantWidth` | `number` | `380` | Width in pixels the drawer animates to when open. | ## EditorAgent integration Pair `useApplyAdaptiveCard` with `renderAiAssistant` to let an LLM author Adaptive Card payloads. The hook owns the designer's controlled `payload` + `sampleData` and exposes an `applyAdaptiveCard` tool that the agent calls to commit a new payload. The tool rejects primitives / arrays / null and requires `type: "AdaptiveCard"` at the root so a malformed call cannot crash the designer. ```tsx import { EditorAgent } from '@docyrus/ui/components/editor-agent'; import { AdaptiveCardDesigner, useApplyAdaptiveCard } from '@docyrus/ui/components/adaptive-card-designer'; export function AdaptiveCardPlayground({ client, user, agentId }) { const adaptiveCard = useApplyAdaptiveCard(); const [aiOpen, setAiOpen] = useState(false); return ( { adaptiveCard.setPayload(next.payload); adaptiveCard.setSampleData(next.sampleData); }} aiAssistantOpen={aiOpen} onAiAssistantOpenChange={setAiOpen} renderAiAssistant={({ open, onClose }) => ( )} /> ); } ``` The matching backend agent must register a tool named `applyAdaptiveCard` whose input schema mirrors `{ payload: object (required), sampleData?: any, explanation?: string }`. ## Composition The default `AdaptiveCardDesigner` is a single component that composes a provider + the panes. For custom layouts you can assemble the pieces yourself: | Export | Purpose | |--------|---------| | `DesignerProvider` | Holds reducer state + history. Wrap your own panel composition in it. | | `useDesignerContext()` | Read `state` + `dispatch` inside a custom panel. | | `useApplyAdaptiveCard()` | Owns the controlled `payload` + `sampleData` state and ships the `applyAdaptiveCard` client-side tool for ``. See **EditorAgent integration** above. | ## Helpers The component also exports its tree model utilities for advanced integrations (custom serializers, programmatic edits, validation): | Export | Purpose | |--------|---------| | `cardToTree(payload)` / `treeToCard(node)` | Convert between an `AdaptiveCardPayload` and the designer's `DesignerNode` tree. | | `normalizeForRoundTrip(payload)` | Normalize a payload so tree → card → tree is lossless. | | `slotsFor(type)` / `SLOT_MAP` | The slot names (`items`, `columns`, `actions`, …) a given element type accepts. | | `isLeafType(type)` | Whether a type accepts no children. | | `createDefaultNode(type)` / `defaultChildType(type)` | Factory helpers for new nodes. | | `createDesignerId()` | Generate a stable internal node id. | | `findNode`, `findLocation`, `insertNode`, `removeNode`, `moveNode`, `updateNode`, `isAncestor`, `collectIds`, `countNodes` | Tree traversal + mutation utilities. | ## Type Exports | Type | Description | |------|-------------| | `AdaptiveCardDesignerProps` | Component props. | | `DesignerProviderProps` | Props for `DesignerProvider`. | | `DesignerNode` | A node in the designer's normalized tree — `{ __designerId, type, props, slots }`. | | `DesignerState` / `DesignerAction` | Reducer state + action union. | | `DesignerTheme` | `'light' \| 'dark' \| 'auto'`. | | `DesignerWidth` | `'standard' \| 'wide' \| 'full'`. | | `DesignerFocus` | Which editor was last focused — `'canvas' \| 'payload' \| 'data'`. | | `DesignerDiagnostic` | A validation diagnostic — `{ level, message, nodeId? }`. | | `HistorySnapshot` | An undo / redo history entry. | | `ToolboxItem` / `ToolboxGroup` | A toolbox entry and its group key. | | `ToolbarButtonKey` | Keys accepted by `hideToolbarButtons`. | | `DesignerRootType` | The `'__root'` discriminant for the card root node. | | `IAdaptiveCardAiAssistantRenderContext` | Argument passed to `renderAiAssistant` — `{ open, width, onClose, payload, sampleData }`. | | `IUseApplyAdaptiveCardResult` | Return shape of `useApplyAdaptiveCard` — `{ payload, setPayload, sampleData, setSampleData, tools }`. | ## Related - [`AdaptiveCard`](/docs/web/components/adaptive-card) — the renderer the designer canvas is built on. - [`useAdaptiveCard`](/docs/web/hooks/use-adaptive-card) — the hook behind the renderer.