You are reading Nightly documentation for 0.12.6.dev0+g563af09.

This documentation may describe behavior that differs from Stable.

Open Stable documentation

Canvas

Canvas

A canvas is a live visual surface the agent renders beside your chat — a dashboard, a comparison table, a status board — that updates in place as the conversation moves, instead of scrolling past as more chat bubbles.

Ask for one in plain language: "make me a status dashboard for the release", "put the pros and cons side by side on a canvas", "track the sprint on a board and keep it updated". The agent creates the canvas with its update_canvas tool and revises it as things change; every revision replaces the whole surface, so what you see is always the current state.

Where canvases appear

  • The canvas panel. Unless you have closed the panel, the first canvas in a chat opens it: beside the conversation in a wide window, or as a sheet over the message box in a window 64rem wide or less (1024 pixels at the default text size). The panel always lists the chat's canvases as tabs, even when there is only one, and shows one canvas at a time. An update never switches the canvas you are on and never moves keyboard focus: when another canvas is created or updated, its tab says New or Updated until you select it. Updates are announced politely to screen readers ("Canvas “Sprint board” created." or "Canvas “Sprint board” updated."). When one canvas changes several times within 15 seconds, the first change is spoken, Recent status in the Status panel lists the later ones once, as one entry that was not spoken, and one more sentence says it was updated when the 15 seconds end. If an update removes the control you were on, focus moves to that canvas's title instead of being lost. The other exception is withheld islands, described below.
  • The header toggle. A Canvas button appears in the header whenever the chat has canvases — use it to hide or show the panel; screen readers report whether it is pressed. The panel's own close button moves focus to it. Your choice sticks across sessions. When you have closed the panel, a new or updated canvas opens it again only when the window is wide enough to show it beside the conversation, and never changes the saved preference. In a narrower window the panel is a sheet over the message box, so it stays closed until you open it, and then shows the canvas that changed last.
  • The sidebar. Every canvas in the chat is listed under Canvases in the sidebar (next to Artifacts); choosing one opens the panel on that canvas and moves focus to its tab.
  • Its own page. Each canvas has a durable URL (/canvas/<id>, or the Full page link in the panel) that renders it full-width, updates live, and survives across sessions — handy for keeping a dashboard open on a second monitor. In a browser, Full page opens a new tab that is already signed in; a tab opened another way (a middle click, a copied link) asks you to sign in. The page's title names the canvas, and Back to chat returns to the chat it belongs to. The desktop app keeps everything in its one window, so there Full page is a button that opens the canvas in a view that fills the window, without leaving the chat: it updates live, its buttons and forms work as in the panel, the arrow keys, Page Up and Page Down, Home and End scroll it from the moment it opens, and Escape or Close full page view returns you to the Full page button. Text typed into a canvas form there is not copied to the panel, and is lost when the view closes.
  • Everywhere else. Canvases are part of the canonical event stream, so non-visual surfaces stay informed: the terminal prints a one-line summary (canvas updated: 'Sprint board' revision 3 …) and points at the web UI.

Canvases are saved with their chat. Reopening a chat restores its canvases at their latest revision, and deleting a chat deletes them.

Interacting with a canvas

Canvases can carry buttons and small forms (text fields, dropdowns, checkboxes). Pressing a button or submitting a form sends the interaction back to the agent as a clearly framed message in the conversation — [canvas "Release console"] button "Refresh" pressed — so you always see in the transcript exactly what the canvas told the agent, and the agent responds (usually by updating the canvas).

Two properties keep this safe and predictable:

  • Only you can press the controls — canvas input arrives with the same trust as text you type, nothing more.
  • An interaction is validated against the canvas's current revision. If the agent has since replaced a button, a press from the stale view is refused instead of misfiring, and every interaction is recorded as a canvas_input event in the audit stream.

What a canvas can contain

The agent composes canvases from a fixed catalog of building blocks — headings, text, images it has generated, professional-artifact cards pinned to an exact version, badges, big-number stats, progress meters, lists, data tables, cards, sections, columns, and the buttons and forms described above. An artifact card can show one validated preview and an exact-version download without embedding the document binary in the canvas. See Professional artifacts. By default the agent does not write raw HTML or scripts: each block is rendered by the app's own accessible components, so every canvas follows your theme (light/dark), works with screen readers, and can't run code.

That catalog design is deliberate. Labels, image alt text, and table captions are required by validation — a canvas that would be meaningless to a screen-reader user is rejected before it renders, and the agent gets an error telling it exactly what to fix.

The HTML escape hatch

For visuals the catalog genuinely can't express — a custom diagram, a CSS animation — the agent can embed a small HTML island. Islands are deliberately confined:

  • Islands are static: HTML, inline CSS and inline SVG only. Scripts never run, and the agent is told so up front; an island that uses scripts, links, form controls, disclosure widgets, live regions or widget roles, inert, hidden="until-found" or CSS content-visibility (which take content away from screen readers while its text stays), declarative shadow DOM, SVG animation elements, <progress> or <meter> (use the progress meter block), or <marquee> is refused with an explanation of what to use instead. Ordinary text is never refused for mentioning these things.
  • An island shows a picture only as one <img src> (or SVG <image>) holding a still PNG, JPEG or WebP, whose structure is checked before the island is accepted (a picture that still fails to decode shows its alternative text). srcset, <picture> and <source> are refused, and other SVG references can point only inside the island (href="#shape"). Its CSS can refer only to parts of the island itself, such as a gradient in url(#gradient), and can use only functions that load nothing (colours, maths, gradients, transforms, filters, shapes and timing). So CSS background images, including data: ones, are refused: every picture is an <img>, which also gives it a place for alternative text. That text is required: every <img> needs alt (empty for a decorative picture, and otherwise containing a letter or digit), and every SVG <image> needs aria-label, aria-hidden="true" or a named <svg role="img"> around it. Every <svg> itself is either named (role="img" with a label or a title), marked role="none" so only what is inside it speaks, or hidden with aria-hidden="true"; any other element marked role="img" needs a label of its own. A name has to contain a letter or digit, and one borrowed with aria-labelledby has to point at text in the same island that the element holds itself, before any element inside it (text inside a child element, which may be hidden, does not count; the element itself may be hidden), and that element has to be ordinary text: p, span, div, a heading, figcaption, caption, label, li, dt, dd, td, th, strong, em, b, i, small, mark or code, or SVG text, tspan or title. A <title> names the pictures of its <svg role="img"> that come after it. CSS can style only the element itself and one of its ::before, ::after, ::marker, ::first-letter, ::first-line and ::selection at a time, never a browser-specific (-webkit-) part, so the pause control reaches everything that moves.
  • Each island renders in a sandboxed frame with no access to the app: it can't touch the page, your storage, or your session, and a strict policy removes the network for images, styles and fonts, so an island is a self-contained visual. Canvases saved before these rules are checked against them in your browser like any other, and an island that breaks them is withheld (below), so nothing in an island can take you or the frame anywhere or escape the pause control.
  • Islands are display-only — buttons and forms stay in the accessible catalog, so everything interactive remains screen-reader-native. Every island is one keyboard stop, whether or not its content overflows: Tab moves into it, a navy focus ring (the system highlight colour in Windows High Contrast) is drawn just inside its edge, and the arrow keys, Page Up/Down, Space, Shift+Space, Home and End scroll content taller or wider than the frame. The next Tab moves on. One exception: in Chrome and Edge, a box inside the island that scrolls on its own (overflow:auto) is an extra stop; the agent is told to avoid such boxes.
  • An island with CSS animation gets a Pause animation toggle just before it. Its label never changes; screen readers report whether it is pressed. If your system asks for reduced motion, the toggle starts pressed and the animation stays still until you release it. The island cannot override either setting with its own styles. Pausing or resuming reloads the island, so its scroll position returns to the top.
  • Every island requires a real written summary (validated for substance, not just presence: it needs a letter or digit, and at most 150 characters). Screen readers announce the summary as the island's name, and when an island is withheld its summary is shown in its place, so the visual itself may be missing but its description never is.
  • Your browser checks every island again before showing it. The agent's HTML is checked when the canvas is saved, but a browser can read markup differently from that check, so the web UI reads each island with the browser's own HTML parser and style engine and applies the same rules. An island that fails is withheld: in its place you see "Visual not shown:" with the island's summary, one sentence for each reason, and a suggestion to ask the agent to make it again. A withheld island has no frame and no pause toggle and adds no keyboard stop; the rest of the canvas, other islands included, is unaffected, and if the island is shown again later its pause setting is as you left it. An update that withholds a visual says so in its announcement ("… 1 visual is shown as a description."), only when the visual was not already withheld, and the full page's structure outline marks it "Visual not shown". If keyboard focus was in an island (its frame or its pause toggle) when an update withholds it, focus moves to the "Visual not shown" line; this, and an update that removes the control you were on (focus moves to the canvas title), are the only times a canvas update moves focus.

Each reason has a code, which the block also carries in its data-reasons attribute:

  • element: the island uses an element, attribute or CSS property islands may not use, such as a script, a link, a form control, a live region, a widget role, inert or content-visibility.
  • pictureName: a picture in it has no name for screen readers.
  • pictureRoute: it brings in a picture some other way than one <img src> or SVG <image href> holding a still PNG, JPEG or WebP.
  • motion: its styling could move where the Pause animation toggle cannot reach, such as !important in a style attribute, a cascade layer, another pseudo-element or a -webkit- name.
  • reference: it refers to something outside the island, such as @import or a URL other than #id.
  • font: it declares its own font with @font-face.
  • unchecked: it is too complex to check, or its markup reads differently each time it is parsed.
  • unavailable: this browser cannot run the check (it lacks constructable style sheets), so every island is withheld.
  • Islands are rationed (a few per canvas, with a size cap), and the agent is explicitly instructed to treat them as a last resort — if a catalog block can carry the content, the catalog wins.

Accessibility

The canvas panel is a labeled region with a coherent heading structure, updates are announced through a polite live region, tables carry captions and proper headers, progress meters expose their values to assistive tech, and the tab strip is fully keyboard-navigable. If you use a screen reader, a canvas reads as a well-structured document, not a picture of one.

Current limits

  • Canvases exist in web chats (and the desktop app); terminal sessions summarize canvas updates but don't render them.
  • There is no chart block yet — for now the agent expresses numbers with stats, progress meters, and tables.