CharDesk Docs

Editor Runtime

UI-independent coordination of editor commands, tools, state, and extensions.

HEAD a63778e

EditorRuntime 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/keyboard owns 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.