Introduction

What SpineEditor is and why it exists.

Rich text editors often entangle four concerns: input handling, the document model, layout, and undo. A keystroke starts as browser evidence, and the editor has to infer what the user meant while keeping DOM, selection, and history in sync. Bugs cluster at those seams. Undo stops matching what the user saw. Paste and composition often end up taking different code paths from ordinary typing.

SpineEditor keeps those concerns apart. Semantic edits enter as typed Intents. The engine compiles each intent into a Plan, lowers it into a Transaction of apply-time Steps, applies them against an immutable document, normalizes the result, and commits. The DOM is a projection of that committed state.

The pipeline

IntentPlanTransactionApplyNormalizeCommit

Each arrow is implemented by a named function with inspectable artifacts and stable JSON exports where persistence needs them. You can trace, test, or replay the pipeline without asking the DOM what happened.

What the core provides

  • An immutable document model in packages/core/src/doc.
  • A selection model (TextSelection, NodeSelection, BlockSelection) addressed by document positions and paths, not DOM nodes.
  • The pipeline, owned by EditorEngine.
  • History via HistoryState. Each entry stores the committed transaction together with stateBefore and stateAfter; undo restores stateBefore.
  • Canonical JSON for documents and serialized transactions; structured tracing for every stage.
  • A plugin capability graph for planner rules and commands.

What the DOM runtime provides

@spine-editor/dom translates browser events to intents and projects committed state into DOM. It owns the renderer, substrate, selection mapping, selection overlay, clipboard, and IME. Browser evidence can inform an intent, but semantic meaning is still decided by the core pipeline.

How to read these docs

Start with Architecture for the shape of the pipeline, then read the pipeline pages in order: Intent, Plan, Transaction, Steps, Normalize, Engine, History. Then see how the core meets the browser in DOM Runtime.