# Devtools example An AI-powered chat where each assistant reply streams a fresh `Spec` that renders inline. A single `` panel observes **every** rendered spec, **every** streamed patch, **every** state change, and **every** dispatched action across the whole page — demonstrating how one devtools instance works with many renderers. Pairs with [`@json-render/devtools`](../../packages/devtools) and [`@json-render/devtools-react`](../../packages/devtools-react). ## What it shows - **AI-streamed specs (inline mode)** — the agent writes a short conversational reply, then emits a `` ```spec `` fence of RFC 6902 JSON patches. `pipeJsonRender` on the server splits that into `data-spec` parts and plain text parts; the client re-assembles both with `useJsonRenderMessage`. - **One renderer per assistant message, one devtools panel** — every message gets its own ``, but they share a single top-level ``, so the devtools State / Actions / Stream tabs see the whole page, not just one message. - **State namespacing per turn** — the API route hands the agent a unique `messageId` and requires every element key (`-root`, `-counter`, …) and state path (`//count`, `//todos`) to be prefixed with it, so specs from different messages never collide on shared state. - **Built-ins exercised** — `setState`, `pushState`, `removeState`, plus `inc`/`dec`/`toggle`/`add` computed functions, `$bindState`, `$template`, `$state`, `repeat`, `$item`, `$index`, and conditional `visible`. - **The Pick panel across renderers** — because every element is tagged with `data-jr-key`, picking an element in any assistant bubble jumps to its spec in the panel. ## Setup ```bash pnpm install cp .env.example .env.local # Edit .env.local and set AI_GATEWAY_API_KEY ``` Grab an AI Gateway key at . On Vercel the key is auto-authenticated, so you only need this for local dev. ## Run ```bash pnpm dev # http://devtools-demo.json-render.localhost:1355 ``` Press J (or click the floating `{}` badge) to toggle the panel. It starts open by default. ## Files - `app/page.tsx` — chat UI, top-level ``, per-message ``, `` mount - `app/api/chat/route.ts` — streams the agent through `pipeJsonRender` - `lib/agent.ts` — `ToolLoopAgent` with a system prompt that enforces inline mode + `messageId`-based namespacing - `lib/catalog.ts` — compact catalog (Card, Stack, Grid, Metric, Button, TextInput, Checkbox, List, ProgressBar, Callout, …) tuned to show off devtools - `lib/registry.tsx` — component renderers with plain inline styles, no UI framework ## Try these prompts - "Build an interactive counter with + and - buttons" — Actions tab lights up with `setState` dispatches. - "Make a todo list where I can add items, mark them done, and remove them" — exercises `pushState` / `removeState` and `$bindState` inputs. - "Show me a fitness dashboard with three metrics and progress bars" — metric-heavy spec; handy for the Spec tree + State inspector. - "Quiz me on three geography questions with a submit button that reveals my score" — uses bindings plus conditional visibility. Then send a second prompt in the same session and watch the Stream tab keep appending patches while the State tab shows both turns' namespaced keys side by side.