Generative UI
Diminuendo’s agent can produce rich, interactive user interfaces directly within the chat message stream. Instead of relying solely on text and markdown, the agent generates structured UI specs that the client renders as interactive React components — dashboards, forms, charts, data tables, quizzes, and more.Generative UI is powered by the json-render SpecStream format. The agent emits JSONL patches inside
```spec fenced blocks, which the gateway parses and streams to clients as ui.spec_* events.How It Works
User sends a prompt
The user asks for data, comparisons, forms, or any content that benefits from visual presentation.
Agent generates UI specs
The agent writes normal conversational text and embeds
spec fenced blocks containing JSONL patches. Each patch incrementally builds a UI spec.Gateway parses spec fences
The
SpecFenceParser detects ```spec fences in the agent’s output, strips them from the text stream, and emits ui.spec_start, ui.spec_delta, and ui.spec_complete events.Client renders progressively
The client applies patches incrementally, rendering a partial UI during streaming. When
ui.spec_complete arrives, the final compiled spec replaces the partial.Available Components
The agent has access to 27 components organized into six categories:Layout
Stack · Card · Grid · Separator · Accordion · Tabs · TabContent · TimelineText & Emphasis
Heading · Text · Link · Badge · Alert · CalloutData
Metric · Table · CodeBlock · Progress · ImageCharts
BarChart · LineChart · PieChartInput
TextInput · SelectInput · RadioGroup · ButtonActions
setState · pushState · removeState · sendMessage · openUrl · copyToClipboardData Binding & Interactivity
The agent places data in the spec’s/state and references it in component props using expressions:
| Expression | Purpose | Example |
|---|---|---|
{ "$state": "/path" } | Read a value from state | "value": { "$state": "/metrics/revenue" } |
{ "$bindState": "/path" } | Two-way binding (forms) | "value": { "$bindState": "/form/name" } |
{ "$cond": ..., "$then": ..., "$else": ... } | Conditional value | Show “Yes” or “No” based on a flag |
{ "$template": "Hello ${/user/name}" } | String interpolation | Build dynamic text from state |
{ "$computed": "fn", "args": {...} } | Call registered function | Format currency, compute totals |
Visibility Conditions
Components can be conditionally shown or hidden:Actions
Buttons and interactive elements trigger actions:| Action | Description |
|---|---|
setState | Set a value at a state path |
pushState | Append to an array |
removeState | Remove from an array by index |
sendMessage | Send text back to the agent (form submission) |
openUrl | Open a URL in the browser |
copyToClipboard | Copy text to clipboard |
Sample Prompts
Try these prompts to exercise different aspects of the generative UI system. Each prompt is designed to trigger a specific UI pattern.Data Dashboards
Data Dashboards
Prompt 1: Project Metrics Dashboard
- Metrics display with trend arrows (up/down/neutral)
- LineChart renders with labeled axes
- Table renders with sortable columns
Prompt 2: Sales Analytics
- All three chart types render (LineChart, PieChart, BarChart)
- PieChart has color-coded legend
- Data binds correctly from state
Interactive Forms
Interactive Forms
Prompt 3: Project Setup Form
- TextInput accepts user input
- SelectInput dropdown works
- Button triggers setState (hides itself) and sendMessage (sends data back)
- Visibility conditions toggle correctly
Prompt 4: Contact Form with Validation
- Multiple TextInputs with different types (text, email)
- Form data binds to state via $bindState
- Submit button sends interpolated message via $template
Quizzes & Questions
Quizzes & Questions
Prompt 5: Multiple-Choice Quiz
- RadioGroup allows selection with $bindState
- Button triggers setState to show feedback
- Correct Callout appears only when answer = “b” AND checked = true
- Wrong Callout appears for any other answer when checked
Prompt 6: Survey with Multiple Questions
- Multiple RadioGroups with independent $bindState paths
- TextInput for free-text entry
- Submit button sends all state values via sendMessage
Comparisons & Tabs
Comparisons & Tabs
Prompt 7: Framework Comparison
- Tab switching works (click each tab)
- Each tab shows different data from state
- Metrics display correctly with state bindings
Prompt 8: Pricing Tier Comparison
- Grid layout with 3 columns
- Cards with titles and structured content
- Metrics for pricing
Timelines & Processes
Timelines & Processes
Prompt 9: Migration Plan Timeline
- Timeline renders with vertical dots and connecting lines
- Completed phase has solid dot
- Current phase has highlighted dot
- Upcoming phases have muted dots
Code & Documentation
Code & Documentation
Prompt 10: API Documentation
- Accordion sections expand/collapse
- CodeBlock renders with syntax class
- All three endpoints have distinct content
Prompt 11: Code Review Summary
- Metrics with appropriate trend indicators
- Table with file status data
- CodeBlock with diff content
Complex & Edge Cases
Complex & Edge Cases
Prompt 12: Large Dashboard with Many Components
- All components render without errors
- Large spec compiles correctly
- No missing elements or broken references
- Progressive rendering shows partial UI during streaming
Protocol Events
Generative UI uses four protocol events:| Event | Persistence | Description |
|---|---|---|
ui.spec_start | Ephemeral | Signals the beginning of a new UI spec block |
ui.spec_delta | Ephemeral | One RFC 6902 JSON Patch operation |
ui.spec_complete | Persisted | Final compiled spec, stored in message metadata |
ui.spec_error | Ephemeral | Parse or validation error |
History Persistence
Completed UI specs are stored in assistant message metadata. When a session is reloaded, the client reconstructs the UI blocks frommetadata.uiBlocks and metadata.parts, preserving the inline position of UI relative to text.
Stream Snapshots
Late-joining clients receive in-flight UI specs via thestream_snapshot event’s uiSpecs array, enabling them to see the current state of any spec being built.
Theming
Generated UI components inherit the application theme automatically. All components use Tailwind CSS custom properties (--background, --foreground, --primary, --border, etc.) that cascade from the app shell. Dark mode, accent colors, and font sizes are applied consistently.
Spec Diffing
The agent can update a previously rendered UI block by emitting a new```spec block that targets an existing uiId. Instead of creating a new UI block, patches are applied to the existing spec, enabling iterative dashboard refinement across turns.
Architecture
Gateway: SpecFenceParser
Detects
```spec fences in agent text output, extracts JSONL patches, compiles final specs, and emits ui.spec_* events. Stateful per-session with automatic flush on turn completion.Client: SpecRendererProvider
React context providing the
renderSpec function. The self-contained renderer walks the spec tree, resolves expressions, manages local state, and renders 27 component types with theme-aware styling.Store: Parts Array
The zustand chat store maintains an ordered
parts[] array on ActiveTurn — interleaved text and UI blocks preserving inline position. Microtask batching collapses rapid patch events into single state updates.Agent: Prompt Addendum
A comprehensive prompt addendum teaches the agent when to use generative UI, the output format, available components, data binding, interactivity patterns, and includes worked examples.