--- name: ade-scene description: Use this skill when showing something would beat describing it — status across many items, a number that changed, a comparison, a timeline, a pipeline, a distribution, ADE's own lanes, chats or PRs. Emit a fenced `scene` block of ordinary HTML, CSS and JS and ADE renders it as a live, interactive view inside the transcript. Check it first with `ade scene preview`. Use ade-mosaic instead when you need an ANSWER from the user. --- # ADE scenes Emit a fenced code block with language `scene` containing plain HTML. ADE renders it in a sandboxed frame with its own origin and its own Content-Security-Policy, inline in your reply, on the reply's own text column and in ADE's font and colors. You write real HTML, CSS and JavaScript — there is no schema and no component list to stay inside. A scene **shows**. A mosaic **asks**. Never draw your own Approve button: ADE has native surfaces for permission, and a card that approves itself is not a confirmation. Reach for `ade-mosaic` whenever you need a decision back. ## Decide first: does this reply need one? Most replies need no scene. Add one only when a picture makes the answer clearly faster to read than prose would, and only one per reply. Good: several PRs and their check states; status across lanes or chats (use live data); a count or metric that changed; CI stages and where it failed; a comparison of many items; a trend or distribution; anything a reader would want to hover, zoom or click through. Bad: one number or one fact (say it), a short answer, a paragraph (write it), a list a bullet list handles, anything you need answered (use `ade-mosaic`), or a wall of text in a box (that is just text, further away). When unsure, skip it. Scenes cost the reader nothing to wait for: the reply streams on while the scene draws, and a preview takes a second or two. ## The loop: write, preview, fix, then reply 1. Write the scene to a file (`/tmp/.html`), body only or a whole fence. 2. `ade scene preview /tmp/.html --text`. It renders the scene exactly as the chat will, in a hidden window, and prints a screenshot path and every problem: script errors, blocked requests, a scene that never settles, and source mistakes the policy turns into blanks. 3. Open the screenshot and look at it: `ok` only means nothing threw. Check the layout (columns lined up, nothing cut off or overlapping, text readable). Fix and preview again until it is right. Check `--theme light` if colors matter; `--width 980` for wide panes. 4. Put the fence in your reply. Do not skip the preview for anything non-trivial: a scene that throws is a blank box in the user's transcript. ## The block The first line may carry a title and the live data the scene wants. Everything after it is your markup. ```` ```scene
0
#1237Persistent directorpassed ``` ```` No padding is added around your markup: content starts on the reply's left edge. Draw your own card (`background: var(--surface); border: 1px solid var(--border); border-radius: 10px`) when you want one. ## What the frame gives you `window.ade` is injected before your code runs: | Member | What it does | |---|---| | `ade.data` | The live ADE data the scene asked for (see below), or `null`. | | `ade.on("data", fn)` | Called with each new data snapshot, and at once if one already arrived. | | `ade.on("theme", fn)` | Called when the user switches ADE's theme. The CSS variables already changed. | | `ade.theme` | ADE's resolved palette. Also available as CSS variables. | | `ade.open(url)` | Opens an `ade://` deeplink in ADE, or an http(s) page in ADE's browser. Plain `` links do the same. Works only in answer to a click in the scene. | | `ade.reducedMotion` | True when the user asked for less motion. | | `ade.restored` | True when the scene already played once and is being brought back; `ade.animate` and `ade.countUp` skip to their end state then. | | `ade.animate(target, keyframes, options)` | Web Animations, collapsed to the end state under reduced motion or restore. | | `ade.countUp(target, to, { from, duration, decimals })` | Counts a number up. | | `ade.resize()` | Re-measures the scene and asks the host for the new height. Call it after you change the content's size. | | `ade.ready()` | Call it when your first paint is done. | CSS variables, already set on `:root` and kept current when the theme changes: `--bg`, `--surface`, `--border`, `--fg`, `--fg-muted`, `--accent`, `--success`, `--warning`, `--danger`, `--font-sans`, `--font-mono`, `--font-size`. Use them and the scene looks like the rest of ADE in every theme, light ones included. `--font-sans` is ADE's Geist and `--font-mono` its JetBrains Mono. ## Live ADE data Ask on the marker line, and the scene stays true after the turn ends: scrolled back into view tomorrow, it shows tomorrow's lanes. ``` ``` | Source | Each item | |---|---| | `lanes` | `id, name, branch, base, primary, color, ahead, behind, dirty, changedFiles, running, awaitingInput, sessions, url` | | `sessions` | `id, title, laneId, laneName, tool, status, startedAt, endedAt, url` | | `prs` | `number, title, state, checks, review, laneId, additions, deletions, updatedAt, githubUrl, url` | `url` is an `ade://` deeplink: put it in an `` (or pass it to `ade.open`) and a click opens that lane, chat or PR in ADE. The snapshot `{ at, lanes?, sessions?, prs? }` arrives after load and again when it changes, so render from `ade.on("data", render)`, and draw an empty state until the first one arrives. **Use live data instead of typing ADE's numbers in.** Asking for `data=` and then hardcoding values you looked up is the worst of both: the numbers are stale tomorrow and wrong if your lookup was. Start from this and restyle it: ```` ```scene
Loading lanes…
``` ```` `ade scene preview` sends a live-data scene the same snapshot the chat will, so the preview shows real lanes, chats and PRs. ## What the frame does not give you - **No network.** `connect-src 'none'`: no `fetch`, no XHR, no WebSocket, no remote fonts, no remote images, no `