Editor Runtime
UI-independent coordination of editor commands, tools, state, and extensions.
HEAD a63778eEditorRuntime coordinates execution; Canvas, slides, sessions, and collaboration
retain their state and business ownership.
createApplicationEditorHost() creates isolated editor, canvas, Yjs registry,
and collaboration runtimes. The composition root owns the default host;
widgets resolve it through explicit providers.
Production widgets receive canvas, editor, and collaboration runtimes through providers; fallback injection is test-only. Additional hosts disable persistence or use a distinct non-empty storage key.
CanvasWorkspaceRuntime owns one frame scheduler and a registry of canvas view
runtimes. Each view binds one session and owns its live and committed camera,
size, and imperative renderer managers. CanvasViewportRuntime is the command
boundary: a mounted workspace binds its active view, while a headless host uses
its single-view fallback. Only committed active-view cameras update the session's
saved viewport. Only the active view projects its session into the shared editor
and owns managed input; inactive views render their bound session with an empty
interaction snapshot. Pane
resize preserves the camera's world center. Manager and frame keys are scoped by
view ID. Canvas interaction ports are also registered by view ID; capture-phase
activation selects the port before the workspace switches session, so the first
gesture is routed through the same pane that received it.
Canvas interaction is one atomic snapshot scoped by the active Yjs document and
page address. Session and page activation reset it; metadata-only changes retain
it. The internal Zustand state owns this snapshot without flat field mirrors.
CanvasViewportRuntime is the sole active-camera authority; saved descriptor
viewports are checkpoints and never live Zustand projections.
Entry direction
native KeyboardEvent -> browser adapter -> KeyInput
├─ shortcut dispatcher -> editor keymap -> command/tool
└─ managed Canvas rules -> Canvas Intent -> canvas port
native beforeinput/composition/clipboard -------------------------> native adapter
command/tool or native adapter -> canvas port -> CanvasCommands -> pure transition or Yjs document- Commands own discrete semantic operations; tools own continuous interaction through one hierarchical state tree for freeform and structured drag state.
@chardesk/keyboardowns DOM-free keyboard facts. Its browser entry performs the only native-event conversion.- The shortcut dispatcher owns focus classification, layer priority, and native event effects; the keymap owns configurable semantic bindings.
- Managed Canvas key rules are pure
KeyInput + context -> Intent; the hook executes Intents through its model and editor command ports. - Native clipboard events invoke the same commands through adapters.
- Widgets use provider-bound selectors and commands, never private stores or Yjs maps.
CanvasCommands owns workspace and interaction mutations. Zustand exposes the
read projection; reusable interaction rules are pure transitions applied by the
command boundary. Static-grid focus, range navigation, edit-mode changes, and
selection clearing follow this path. Drawing previews are pure transitions over
the interaction-owned scratch layer. Scratch commits, existing-cell area fills,
and static-range moves are addressed Yjs document commands owned behind
CanvasCommands; they are not Zustand actions. Remaining content commands follow
the same document boundary as they migrate. Selection mutations use that document
boundary directly. Clipboard adapters receive a read-only Canvas snapshot and an
explicit mutation port, so asynchronous I/O cannot invoke Zustand actions.
Extensions
Extensions register before EditorRuntime.start(). Extension, command, tool, and
state-scope IDs have one owner; disposal unwinds lifecycle registrations in reverse order.
Device-local key overrides load after registration and before start, persist
under chardesk-editor-keymap-v3, and never enter document state, collaboration,
or undo history. App chrome, menu navigation, presentation controls, IME,
native clipboard events, and hold gestures remain Host adapters.
The contract is application-public without an npm compatibility commitment.
Composition is authoritative in
apps/canvas/src/app/compositionRoot.ts;
runtime lifecycle is authoritative in
apps/canvas/src/domains/editor/core/runtime.ts.
Use their nearest tests for isolation, ownership, and disposal evidence.