Skip to content

feat(composer): A2UI Dojo — interactive JSONL playback and streaming viewer#857

Draft
zeroasterisk wants to merge 33 commits intogoogle:mainfrom
zeroasterisk:feat/a2ui-dojo
Draft

feat(composer): A2UI Dojo — interactive JSONL playback and streaming viewer#857
zeroasterisk wants to merge 33 commits intogoogle:mainfrom
zeroasterisk:feat/a2ui-dojo

Conversation

@zeroasterisk
Copy link
Collaborator

@zeroasterisk zeroasterisk commented Mar 14, 2026

A2UI Dojo

A new /dojo route in the Composer that acts as a VCR for A2UI protocol traces. Load a scenario, press play, and watch JSONL chunks stream in one-by-one while the renderer updates in real-time.

Features

  • JSONL Streaming Simulation — Each chunk arrives as a complete JSON object (real JSONL), with byte sizes and wire format displayed
  • 3-Tab Left Pane:
    • Events — Lifecycle summaries (surface created, components registered, data bound) with color-coded badges
    • Data — Raw JSONL chunks as they arrive over the wire, grouped by ↓ SERVER / ↑ CLIENT direction
    • Config — Scenario selector, renderer picker, transport config
  • Interactive Scrubber — Drag to any point in the stream; click any chunk to seek
  • Bidirectional Flow — Restaurant booking scenario demonstrates the full lifecycle:
    1. Server creates surface
    2. Server registers 9 components (Column, Text, Image, TextField, DateTimeInput, Button)
    3. Server populates data model
    4. User submits booking form (↑ clientEvent with form data)
    5. Server responds with confirmation surface
    6. Server updates data with confirmation details
  • URL Deep Links?scenario=restaurant-booking&step=4 links directly to any state
  • Sidebar Nav — "Dojo" link added to Composer navigation
  • 5 Curated Scenarios from samples/agent/adk/ (real v0.8 wire format)

Files Changed

File Description
src/app/dojo/page.tsx Main Dojo page with 3-tab layout, streaming player, renderer
src/components/dojo/useStreamingPlayer.ts Hook: chunk-level playback, lifecycle events, byte tracking
src/components/dojo/useA2UISurface.ts Hook: transforms A2UI messages for the renderer
src/components/layout/sidebar-nav.tsx Added Dojo nav link
src/data/dojo/index.ts Curated scenario registry
src/data/dojo/restaurant-booking.json 6-chunk bidirectional scenario with user interaction

Live Demo

👉 https://composer-ten.vercel.app/dojo

Zaf and others added 7 commits March 12, 2026 03:10
…be component

- New guide: design-system-integration.md — step-by-step for adding A2UI
  to an existing Material Angular application
- Rewritten guide: custom-components.md — complete walkthrough for YouTube,
  Maps, and Charts custom components (replaces TODO skeleton)
- New sample component: YouTube embed for rizzcharts catalog
- Updated rizzcharts catalog.ts to include YouTube component
- Friction log documenting 8 friction points (P2/P3) encountered during
  development, with recommendations
- Added Design System Integration to mkdocs nav
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Remove friction log file (content already in issue google#825)
- YouTube component: add video ID regex validation (security)
- custom-components.md: rename to 'Custom Component Catalogs', reorder
  examples (media first), clarify basic catalog is optional, remove
  redundant heading, fix Maps input.required consistency, add
  encodeURIComponent to docs example
- design-system-integration.md: rewrite to focus on wrapping Material
  components as A2UI components (not using DEFAULT_CATALOG), show
  custom catalog without basic components, add mixed catalog example
- s/standard/basic/ throughout
@google-cla
Copy link

google-cla bot commented Mar 14, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the A2UI Dojo, a playback tool for agent scenarios, along with new documentation and sample scripts. The changes are a great addition, providing valuable tooling for development and testing.

My review focuses on improving the portability of the new sample scripts by removing hardcoded paths, and enhancing the robustness and maintainability of the new Dojo UI component in React. Specifically, I've pointed out a bug with React keys and suggested improvements to TypeScript type safety. I've also provided feedback on the code examples in the new documentation to prevent potential bugs for developers following the guide.

messages.append(p.root.data)

if messages:
out_path = "/home/node/.openclaw/projects/A2UI/tools/composer/src/data/dojo/contact-lookup.json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The output path is hardcoded to an absolute path, which makes the script non-portable for other developers. It's better to construct a path relative to the script's location.

You can achieve this using pathlib. You'll need to add from pathlib import Path at the top of the file. Also, consider ensuring the directory exists before writing, for example with out_path.parent.mkdir(parents=True, exist_ok=True) before the with open(...) block.

Suggested change
out_path = "/home/node/.openclaw/projects/A2UI/tools/composer/src/data/dojo/contact-lookup.json"
out_path = Path(__file__).resolve().parents[4] / "tools/composer/src/data/dojo/contact-lookup.json"

messages.append(p.root.data)

if messages:
out_path = "/home/node/.openclaw/projects/A2UI/tools/composer/src/data/dojo/restaurant-finder.json"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The output path is hardcoded to an absolute path, which makes the script non-portable. It's better to construct a path relative to the script's location.

You can achieve this using pathlib. You'll need to add from pathlib import Path at the top of the file. Also, consider ensuring the directory exists before writing, for example with out_path.parent.mkdir(parents=True, exist_ok=True) before the with open(...) block.

Suggested change
out_path = "/home/node/.openclaw/projects/A2UI/tools/composer/src/data/dojo/restaurant-finder.json"
out_path = Path(__file__).resolve().parents[4] / "tools/composer/src/data/dojo/restaurant-finder.json"

<div className="flex flex-col gap-2">
{(scenarios[selectedScenario] as any).map((msg: any, index: number) => (
<div
key={msg.id}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The key prop is set to msg.id, but the message objects in your scenario data don't have a top-level id property. This will cause React to issue a warning about missing or duplicate keys and can lead to unpredictable rendering behavior. Since this is a static list, using the index as a key is acceptable here.

Suggested change
key={msg.id}
key={index}

@@ -0,0 +1,4 @@
#!/bin/bash
source ~/.openclaw/credentials/secrets.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The path to secrets.sh is hardcoded to a user-specific home directory (~/.openclaw/...). This makes the script non-portable. For a sample script, consider parameterizing this path or documenting that it needs to be changed by the user.

@@ -0,0 +1,4 @@
#!/bin/bash
source ~/.openclaw/credentials/secrets.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The path to secrets.sh is hardcoded to a user-specific home directory (~/.openclaw/...). This makes the script non-portable. For a sample script, consider making this path configurable, for example, by reading it from an environment variable or a command-line argument.

seek,
setSpeed
} = useJsonlPlayer({
messages: (scenarios[selectedScenario] as any) || [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The as any type assertion on this line (and also on line 133) bypasses TypeScript's type checking, which can hide potential bugs. It would be more robust to define a proper type for the scenario messages and apply it here. This would improve type safety and code maintainability.

Zaf added 19 commits March 14, 2026 18:58
…or A2UI Dojo

- Redesigned Top Command Center with glassmorphic header and functional timeline scrubber.
- Replaced native scenario select with Shadcn DropdownMenu.
- Polished Data Stream view with active state highlighting, glow effects, and auto-scrolling.
- Replaced native checkboxes with custom Tailwind styled toggles in Config view.
- Added dynamic grid layout for the Renderers Panel with sophisticated styling per surface (React Web, Discord dark mode replica, Lit Components).
- Applied custom slim scrollbars throughout for a premium feel.
- Replace invented v0.9 scenarios with real v0.8 samples from samples/agent/adk/
- Add restaurant-booking, restaurant-list, restaurant-grid, restaurant-confirmation
- Add contact-card, contact-list, org-chart, floor-plan scenarios
- Update index.ts to surface real scenarios as defaults
- Default scenario is now restaurant-booking (verified rendering)
- Fix transcoder.ts: pass v0.8 messages through unchanged
- Fix useA2UISurface.ts: only process v0.8 format components (id + component)
- Fix dataModelUpdate: parse ValueMap format correctly
- Restaurant booking now renders: column, text, image, textfield, datetime, button
- Locally verified with headless Chromium: all A2UI CSS classes present
- Build passes (bun run build)
- Switch import to @copilotkit/a2ui-renderer (npm-published)
- Remove file:../../renderers/react dep that breaks Vercel builds
- @copilotkit/a2ui-renderer uses @a2ui/lit under the hood (npm transitive dep)
- Remove Discord mock pane and multi-renderer grid
- Show single A2UI renderer (full width, centered)
- Add human-readable step summaries to JSONL pane
  (e.g. 'Update 9 components: Column, Text, Image, TextField...')
- Raw JSON collapsed by default behind 'Raw JSON ▸' toggle
- Steps are clickable to seek directly
- Wider left pane (35%) for better readability
- Remove unused renderer toggle state
- Remove northstar-tour, flight-status, weather-widget (v0.9 format)
- Remove kitchen-sink, component-gallery-stream (not real scenarios)
- Keep only verified v0.8 scenarios from samples/agent/adk/
- 12 working scenarios remain in dropdown
…nt doesn't clip

The browser-chrome container had h-full which constrained it to viewport height
and overflow-hidden clipped any content below. This caused the DateTimeInput
and fields below it to be cut off in restaurant-booking with no way to scroll.

Fix: remove h-full from the frame so it grows with content, and drop flex-1 +
overflow-auto from the content area since the outer pane wrapper already handles
scrolling via overflow-y-auto.

Fixes google#243
…nfirmation scenario

The Card component referenced 'confirmation-column' as its child, but that
component was never defined in the surfaceUpdate. This caused the model
processor to return null for the unresolved child, making isResolvedCard()
fail validation with 'Invalid data; expected Card'.

Fix: add the confirmation-column Column component with an explicit children
list wiring up all the confirmation content components.
On small screens (< sm breakpoint), the traffic light dots in the
mock browser chrome header are hidden to reduce visual clutter.
Uses Tailwind's hidden sm:flex responsive pattern.

Task google#237
Zaf added 7 commits March 16, 2026 09:17
The route was incorrectly set to 'force-static' which caused Next.js to
fail with a 500 error on every page load since the CopilotRuntime /
InMemoryAgentRunner cannot be statically exported.

Change to 'force-dynamic' so the route is properly handled server-side.
- URL state: scenario, step, renderer synced to query params
  (e.g. /dojo?scenario=contact-card&step=2&renderer=React)
- Config panel: renderer dropdown + scenario dropdown (synced with header)
- Sidebar nav: added 'Dojo' link with Play icon
- Curated to 5 quality scenarios (removed broken/redundant ones)
- Removed contact-lookup (crashes on step 4 — missing component refs)
…ario

Major architecture change:
- NEW useStreamingPlayer hook: explodes messages into individual JSONL lines
  that stream progressively (line-by-line) instead of whole-message chunks
- Scrubber now has fine-grained control over 60+ stream positions per scenario
- Three left pane tabs:
  (a) Events — lifecycle summaries (surface created, components registered, data bound)
  (b) Data — raw streaming lines appearing chunk by chunk with ↓/↑ badges
  (c) Config — scenario, renderer, transport dropdowns
- Removed scenario dropdown from header (lives only in Config tab)
- Streaming cursor animation shows which message is mid-delivery
- Click any event/line to seek to that position
- Server/client sections grouped with direction badges
- Compact header with streaming status indicator
Each JSONL chunk is one complete JSON object on one line — that's how
real SSE/JSONL works. Not individual formatted lines within a message.

- Data tab now shows raw wire format (compact JSON, one chunk per card)
- Shows byte size per chunk and total bytes received
- Scrubber steps through 3 chunks for restaurant-booking (not 60+ fake lines)
- Streaming cursor shows 'Waiting for next chunk...' between deliveries
- Header shows total bytes received during playback
Adds 3 new chunks to the restaurant-booking scenario:
- Chunk 4 (↑ CLIENT): User submits booking form with party size, date,
  dietary requirements via clientEvent/userAction
- Chunk 5 (↓ SERVER): Agent responds with confirmation surface update
  (checkmark, title, details, summary, Done button)
- Chunk 6 (↓ SERVER): Data model update with confirmation details

Full bidirectional flow: server renders form → user fills and submits →
server confirms with new surface state. Demonstrates the complete
A2UI interaction lifecycle in 6 JSONL chunks.
@zeroasterisk zeroasterisk changed the title feat(dojo): Add A2UI Dojo JSONL player and mock scenarios feat(composer): A2UI Dojo — interactive JSONL playback and streaming viewer Mar 16, 2026
@zeroasterisk zeroasterisk marked this pull request as draft March 16, 2026 21:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

2 participants