State and Data Flows
Authority, projection, history, persistence, and synchronization direction.
HEAD a63778e| Scope | Authority | History | Sync |
|---|---|---|---|
document | Ordered CellPlane pages in one Yjs document | Page-local local origins | When collaborative |
workspace | Tool, local interaction, active viewport, and preferences | No | No |
session | Canvas identity, presentation mode, saved viewport, and optional external-source binding | No | No |
presence | Remote identity, cursor, transient selection | No | Ephemeral |
derived | Rendering, minimap, preview projections | No | No |
blackboard | Versioned source files in the browser repository or local Reader | Repository revisions | No |
Workspace writes enter through CanvasCommands; EditorState contains data only and
Zustand is its read projection, not an alternate command surface. CanvasRuntime.store
exposes only getState, getInitialState, and subscribe; its mutable Store is test-internal.
Local grid navigation and selection clearing are
pure interaction transitions. Drawing previews update the transient scratch layer
through the same boundary. Scratch commits, existing-cell area fills, and static
range moves are addressed document writes owned by CanvasCommands, not alternate
Zustand actions. Selection delete, erase, fill, and style commands use the same
owner. Clipboard I/O reads a snapshot, validates its addressed selection after the
await boundary, then commits through an explicit mutation port.
interaction.staticGrid is the sole static-grid interaction authority. Its
navigate branch owns selection; its text-edit branch owns the input session.
Selection and cursor views are derived from that discriminated state, so they cannot
represent conflicting modes.
Text mutation enters through the Canvas text command executor. It derives CellPlane
writes and the next interaction.staticGrid from one initial snapshot; text actions
are not part of EditorState.
Session and Slide lifecycle mutations enter through their Canvas command executors.
One activation coordinator orders create, source-open, import, switch, active deletion,
and collaboration join. Asynchronous work runs outside the commit boundary, then validates
the latest activation intent and publishes a short synchronous commit; no draft spans an
await boundary. A stale import still registers its document and descriptor but does not
replace the user's newer active Canvas.
CanvasStateCommitCoordinator owns the synchronous command publication boundary.
Yjs content projection, local interaction, and history availability update one shared
draft; nested commands read that draft, and only the outer command publishes to
Zustand subscribers. Remote Yjs transactions outside a local command publish
immediately. This boundary provides one observable snapshot, not Yjs rollback.
A projection never writes back to its authority. Only local Yjs origins enter
undo history. Remote values pass Canvas decoders before projection; invalid records
stay out of editor state and surface as collaboration integrity issues.
Every write resolves a CanvasDocumentAddress (documentId, pageId) before opening
its transaction. Active-document convenience reads are projections only; commands do not
use Registry focus as an implicit write target.
Canonical flows
- Freeform content: command -> compact
CellPlanePatch-> one ordered CellPlane operation in a Yjs transaction -> 128x64 spatial projection -> viewport query. Text runs remain compact through the write boundary; target-background inheritance is operation composition, not a per-cell read/modify/write pass. - Session activation: active ViewRuntime checkpoint -> catalog descriptor -> restored mode-authoritative Yjs document -> one editor-state activation patch. Content projection is already current; interaction resets against the newly active document/page address.
- Slide activation: deck metadata -> matching page in the session Yjs document -> active projection.
- Slide edit: command -> active Yjs page -> derived active grid.
- Local restore: IndexedDB catalog ->
y-indexeddbdocument restore -> canvas recovery adapter -> active projection. Startup does not expose editing until the active document is ready. - Canvas switch: retain the current frame -> load the target Yjs document -> commit the latest activation request -> release an unpinned least-recently-used document when over budget.
- Import: document parser ->
CanvasImportSnapshot-> canvas session command -> one-shot active-pane camera placement. The placement uses projected content bounds and viewport insets at runtime; it is not part of the document protocol or persistence. - Session persistence: metadata projection -> sessions catalog transaction. Canvas content
remains in its Yjs document and persists as incremental
y-indexeddbupdates. A flush visits dirty documents only; routine edits never force workspace-wide snapshots. - Viewport: pointer, keyboard, minimap, or zoom command -> active-view port ->
live camera -> settled camera -> session saved viewport -> catalog persistence.
CanvasViewportRuntimeis the only active-camera authority; Zustand does not mirror its offset or zoom. Animation frames never write session metadata. - Slide preview: deck metadata + page reader -> explicit grid snapshot -> canonical slide renderer.
- Selection command: DOM dispatcher -> editor keymap -> command registry -> canvas selection port -> Yjs document.
- Artifact appearance: stored Cell style -> canonical default-foreground classification -> active Canvas palette for visual projections or default SGR state for ANSI serialization. A stored background makes its foreground explicit; persistence retains literal colors.
- Block layout text:
|||-recognized layout -> per-field normal text renderer -> composed CellPlane rows. A lone---remains a Markdown thematic break in auto mode. - Source-backed Blackboard: source-tree transaction -> shared compiler -> derived Freeform surface or Slide pages.
Invalid source remains stored while the last valid surface stays mounted. Human
Canvas commands never write back to source files.
sourceBindingowns authority;freeformandslidecontinue to describe presentation only. - Blackboard directory import: selected source tree -> shared compiler -> canonical
.chardeskenvelope -> ordinary editable Freeform or Slide session. This is a detached snapshot: it retains rendered content and Slide page order, not source paths, drafts, or the manifest topology. - Blackboard site tool: active page workspace -> protocol-neutral namespaced command -> source
repository. Explicit workspace IDs remain a compatibility override. Creation and opening bind
the page URL, Canvas session, and IndexedDB workspace before later file calls omit identity.
Each hosted top-level document owns its selection; local CLI reader pages expose materials but
not browser-persistent CRUD. Workspace reads expose the
blackboard.yamlsource graph; mutations report whether the compiled Canvas projection updated, stayed unchanged, or became invalid. A global read-only tool embeds the canonical skill materials at build time so web-only agents can load the same authoring context. The document connector selects one host adapter, preferring the complete WebMCP surface over the imperative subset used by ChatGPT Site Tools. Both consume the same protocol-neutral tool definitions; the subset is a capability profile, not a vendor protocol. Bootstrap registration keeps an adaptive handshake for runtimes injected after page load; an unavailable workspace returns a stable result. Standard WebMCP requires an origin-isolated document plus either Chrome's local testing flag or an origin-bound trial token supplied asVITE_WEBMCP_ORIGIN_TRIAL_TOKEN.?webmcp=polyfillandVITE_WEBMCP_DEV_POLYFILL=1enable the explicit development-only protocol fallback. ChatGPT discovers only imperative tools registered by JavaScript in the top-level page; declarative and iframe tools are out of scope. Inspectdata-webmcp-provider,data-webmcp-capability, anddata-webmcp-statuswhen a host cannot discover the tools.
The application module and Canvas restore load independently. App chrome mounts as
soon as the module is available, while CanvasPersistenceStatus.restore gates the
workspace, onboarding, collaboration, and editor surfaces. Restore failure opens an
editable in-memory workspace instead of an empty editor. Retry prepares persisted
documents off-screen, then atomically activates them; temporary edits are retained in
a distinct recovered session. The mounted workspace becomes inert during that commit
and is never replaced by a second loading screen.
Every editable local session persists one Yjs document. A Freeform document has one page;
a Slide document has one page per slide. The runtime session list contains
CanvasSessionDescriptor values only. The Canvas document owns page order, names, sizes,
and content; the IndexedDB catalog stores the restore metadata needed before a document is resident. Collaborative
sessions persist only their catalog shell and descriptor; room content is restored through
collaboration persistence and never copied into local session persistence.
The catalog records whether a collaborating session is the local host or a guest. A guest with
no cached room content remains in joining and cannot edit until encrypted transport sync and a
document-ready peer handshake both arrive. Collaboration V7 encrypts Yjs sync and awareness in
the browser. The memory-only relay sees room metadata and ciphertext, owns no Yjs document, and
deletes a room when its last connection leaves. Managed links resolve the build-configured endpoint;
an optional endpoint exists only for protocol-level self-hosting. V6 remains a legacy y-websocket
link format.
Collaboration derives one shared document and main-page identity from the room id;
local session ids remain catalog-only and never name shared Yjs pages. Starting or joining a
room rehomes the single local page to that shared identity before transport connects.
Source-backed sessions persist their binding and presentation metadata beside the source
repository. Their runtime Yjs shells are empty and non-persistent; compiled surfaces have no
undo or synchronization history. Closing a session closes only the view, never its source.
The V5 localStorage envelope is a migration input only. Migration seeds and verifies all Yjs documents, commits the catalog, then removes the old key. A failure keeps the old value for retry. Restore probes existing Yjs channels before seeding. Intermediate page descriptors are normalized into a clean document generation. Documents with excessive Yjs struct history use the same generation rotation: write and verify the next generation, commit its catalog pointer, and reclaim older generations after a later verified startup. The active generation is always catalog-owned. If a bootstrap catalog replaced legacy metadata, restore reattaches sessions only when their local Yjs document databases still exist. Historical descriptors restore names and mode; an orphan without metadata receives a recovered shell. Deleted sessions are not resurrected. Canvas content never falls back to localStorage. Every browser tab remains editable. Loaded local Yjs documents exchange updates over a same-origin channel and persist them independently; an exclusive coordinator owns checkpoint rotation, while catalog writes use short exclusive sections and merge changes against the latest snapshot. Undo history remains runtime-only per tab.
The catalog is the startup index; it does not make every Canvas resident. The active Canvas and both visible Split panes are pinned. The document repository retains at most four Canvas documents, using most-recently-used order for the remaining slots. Releasing a document flushes dirty local updates and disposes its runtime projections without deleting IndexedDB content. User deletion is a separate lifecycle operation: a durable catalog intent records its tombstone before the Session disappears from runtime state, then runtime resources are released. Recovery databases remain available but tombstoned Sessions are never restored. Reopening an evicted Canvas restores content but not its undo stack.
Canvas pages contain only ordered cell-plane-operations. Canvas rendering queries
occupied spans inside the viewport, and Minimap caches its reader projection by document
revision; camera movement never materializes a grid snapshot. A legacy local main-grid
is converted once into a bootstrap operation and then cleared. CanvasSurfaceReader is the read
contract for rendering, interaction, selection, and export. CanvasState.contentSurface carries the
active Reader plus a projection revision; it never carries a live GridMap. Freeform and Slide read
their CellPlane indexes directly. A GridMap is created only at an explicit snapshot boundary such as
serialization, clipboard payload construction, or a transient drag preview. Chunk indexes and
projections are never synchronized.
Freeform and Slide session descriptors contain no content at runtime. Imports use
CanvasImportSnapshot; persistence uses CanvasSessionSnapshot, while Slide export and playback
use SlideDeckSnapshot. Switching sessions restores content from the matching Yjs document; the
projector never writes content back into the descriptor. Retired Structured session, checkpoint,
and .chardesk inputs are decoded only at migration boundaries and flattened into CellPlane content;
old Structured collaboration links are rejected as retired.
Slide runtime state uses SlideDeckDescriptor; preview, playback, resize, duplicate, persistence,
and export materialize page content only at their explicit boundary.