This folder documents how the editor is wired end to end, with short pages that keep one concern each.
Read by goal:
- System boundaries: Architecture
- Document structures and mutation API: Data Model
- Typing, caret, formatting pipeline: Input Handling
- Registration and lifecycle contracts: Plugins & Tools
- OT, batching, undo/redo: Collaboration
- Which event bus to listen to: Events
Five core parts:
Coreowns startup and dependency wiring.EditorJSModelis the source of truth for document state.- DOM adapters map model changes to concrete DOM inputs.
- Tools/plugins add behavior through stable interfaces.
CollaborationManagertranslates model changes into OT operations.
Two event transports (never mixed):
- Model events on
EditorJSModel - Core/UI events on the
EventBusheld by the IoC container
new Core(config)binds IoC services and built-ins.core.use(...)registers UI components/plugins byplugin.type.core.initialize():- Initializes the adapter plugin (
DOMAdaptersor a custom replacement). - Instantiates UI plugins (
EditorjsPlugininstances, e.g.EditorjsUI). - Prepares tools and emits
ToolLoadedCoreEventfor each. - Resolves core services (
SelectionManager,BlocksManager,BlockRenderer,UndoRedoManager). - Inserts initial document into
EditorJSModel;BlockRendererreacts to eachBlockAddedEventto create aBlockToolAdapter, render the tool, and emitBlockAddedCoreEvent. - Dispatches
CoreEventType.Ready; collaboration manager connects if server config is provided.
- Initializes the adapter plugin (
- Browser fires
beforeinputinside thecontenteditableblocks holder. BlocksUI(the@editorjs/uiblocks component) intercepts it, wraps it inBeforeInputUIEvent, and dispatches it on the globalEventBus.DOMBlockToolAdapterlistens on theEventBusforBeforeInputUIEventand callsmodel.insertText(...).- Model mutates and emits
TextAddedEvent. DOMBlockToolAdapterupdates the affected DOM range.CollaborationManagerconverts the event to anOperation, adds it to the currentBatchedOperation, and resets the debounce timer.- Browser
selectionchangefires;CaretAdapterbuilds anIndexand updates the model caret. SelectionManageremitsSelectionChangedCoreEvent;CaretAdapterrestores DOM selection from the model index if needed.
The system stays decoupled because each step communicates through interfaces and events, not direct cross-component calls.
EditorjsPlugin: general UI/behavior plugin registered viacore.use()withPluginType.Plugin. UI components likeBlocksUItypically declare astatic typefromUiComponentTypebut are bound toPluginType.Plugininternally.UiComponentType: reserved string keys for UI component slots (shell,blocks,inline-toolbar,toolbox,toolbar). These name components in the UI layer but are not used as arguments tocore.use()— plugins are registered byPluginTypeorToolTypevalues.BlockTool/InlineTool/BlockTune: tool contracts registered viacore.use(ToolConstructor, options)during setup. Thetoolsconfig field provides tool settings thatToolsManagerapplies duringinitialize().Index: serializable location in the document tree, independent of DOM nodes. Fields:documentId,blockIndex,dataKey,textRange,tuneName,tuneKey,propertyName. AcompositeSegmentsarray holds multiple per-input text indices for cross-block selections. Built withIndexBuilder; serialized to a compact string for caret storage and OT operations.DataKey: branded string identifying a data slot inside aBlockNode(e.g."text","caption"). Created viacreateDataKey().BatchedOperation: groups rapid single-character inserts or deletes on the same data key into one logical edit for undo/redo. Lives in@editorjs/collaboration-manager.UndoRedoManager: exists in two forms: in@editorjs/corefor single-user undo/redo (listens to model events and groups by debounce), and in@editorjs/collaboration-managerfor OT-aware undo/redo (manages operation stacks).InputsRegistry: shared map of(blockIndex, dataKey) → HTMLElementmaintained byDOMAdapters. BothDOMBlockToolAdapterandCaretAdapterread from it.BlockRenderer: internal@editorjs/corecomponent that subscribes toBlockAddedEvent/BlockRemovedEventand creates/tears downBlockToolAdapterinstances. Not to be confused withBlocksManagerwhich handles the programmatic insert/delete/move API.CaretManager: owns oneCaretper collaborating user. DispatchesCaretManagerCaretUpdatedEventonEditorJSModelwhen any caret changes.