Goldie Blocks

Canvas Frame

The one chrome every canvas screen sits in — account rail, work bar, scrolling surface, Inspector — and how the container screens and the document screen fill it differently.

Every screen under /app/canvas is drawn inside the same frame. Only what goes in the middle changes.

In plain terms: one letterhead. The Workspace, a Project and a document are printed on it; the frame around them never moves.

One route, three screens

/app/canvas/$routeKey resolves the key to a folio and picks the screen from its kind. /app/canvas itself provisions the user's Workspace and redirects to it.

Folio kindScreenCode
WorkspaceContainer listsrc/pages/folio-container/
ProjectContainer listsrc/pages/folio-container/
DocumentDocument Work Screensrc/pages/document-work/

Workspace and Project are one screen, not two: the same page, printing the tier's own words. The list screen shows the container's own card with its children filed beneath it; the document screen shows the document as modules and blocks. See Document Work Screen.

What the frame supplies

CanvasShell
├── Account rail      ← collapsed by default, expands from the left edge
├── Work Bar          ← the screen's own bar, passed in
├── Surface           ← the scrolling middle; the screen itself
└── Inspector         ← docked right-hand panel

CanvasShell (src/widgets/canvas-shell/ui/canvas-shell.tsx) owns the two nested sidebar providers and their widths. It takes the work bar, the surface and the Inspector as slots and reads none of them — a screen supplies its own words, never a tier flag.

Clearing the selection belongs to the frame, not the screen. Escape anywhere inside the shell, or a click on empty surface, calls onClearSelection. That is why each page owns its selection state and hands the clearing down: the thing that clears it is the chrome around it.

Every screen goes through WorkScreen (GB-325): one chrome holding the work bar (trail, Builder / Maker, the "…" menu) and the side panel with its 96px heading and Goldie. A screen supplies only its content.

The page header

Every page's frame carries one header (owner, 2026-10-01, second revision, fourth form): at the lid's right a labelled Share button opening one dialog with tabs Share | Library | Download (a Document has all three; Workspace, Project and Library Share alone; a Module or Block page has no Share). Beside it a muted readout: the seat holders' avatars · "Published in N Libraries", each opening its tab. The foot reads "Last edited by … · date ▾", opening the history. Insert stays on the list toolbar. See One Page Header for Every Page.

Inspector Panel rows

The panel is mounted on every canvas screen. Row names are stable; row content follows the screen and the selection.

RowRole
Selection Display RowWhat the panel is currently acting on
Inspector Content RowAssistance for that selection, capability-filtered
Conversation PanelTranscript, tool-call cards, results, and the composer

Where Goldie sits is the real difference between the screens:

  • Document Work Screen — the Inspector carries the document-scoped conversation. Arriving with a posture (?surface=) gives the whole panel to Goldie, headed Goldie - Ask; without one it keeps its Inspector heading and shows the selection rows above the conversation.
  • Container screens — the Inspector is present but empty ("Open a document to start a conversation"). The conversation happens in the open row's own Control Panel, one row at a time.

Readers hold no use-agent capability and get no conversation panel at all.

Narrow windows

Below the sidebar breakpoint (768px) the surface stays put and the Inspector becomes a drawer: on the document screen Goldie is a bottom sheet with a persistent minimized input dock; on a container screen the work bar's right-hand trigger opens the panel. The work bar's left slot becomes the account avatar, opening the account sidebar as a left drawer.

Runtime UI state

Goldie's posture, the outline state and the current selection are runtime UI state. They never create a document version, and a chat turn never clears the selection on its own.

The selection is supplied to the agent as ambient per-turn context — weak context, not an instruction. The agent uses it when the message refers to the selected item ("fill this", "change this clause") or when it materially grounds the answer. A stale or missing selection is refused server-side rather than acted on.

On this page