TipTap integration
examples/tiptap-editor in the repository is <astl-editor> with everything on: defineEditor() from
astrolabe/editor, with no options, gives TipTap 3, every built-in component and look, the insert menu, the
inspector, the live issues list and a preview that grades. The example depends on the astrolabe package only.
pnpm dev:tiptap # http://localhost:5302 (from the repository root)import { defineEditor } from 'astrolabe/editor';
defineEditor();const editor = document.querySelector('astl-editor');editor.value = '<astl-doc lang="en"><astl-page id="animals" label="Animals">...</astl-page></astl-doc>';editor.addEventListener('astl-editor-change', () => show(editor.value));TipTap lives in @astrolabe/editor-tiptap, the only package that imports it. Nothing in it is specific to the gap or
the closed question: its editor nodes are derived from the component manifests.
| File | What it does |
|---|---|
packages/editor-tiptap/src/nodes.ts |
TipTap nodes from the manifests: inline atoms (a gap, shown as a chip such as [grey]), blocks with rich children (a closed question with its prompt and options), other blocks as atoms, the page with an editable label, the document with its lang |
packages/editor-tiptap/src/adapter.ts |
the EditorAdapter: source in and out, one element by id, the selection, change events |
packages/editor-tiptap/src/surface.ts |
the built-in surface: a TipTap editor with injectCSS: false in the light DOM of <astl-editor>, imported on demand as a chunk of its own |
packages/editor-tiptap/src/element.ts |
<astl-editor>: the kit around the surface, or around your editor in slot="surface" |
To build your own TipTap editor around the kit, take astrolabeExtensions and createTiptapAdapter from
@astrolabe/editor-tiptap/tiptap (Editor).
The pattern
Section titled “The pattern”The package follows five steps, and so does an adapter for any other editor:
- Map elements to your editor's model, from the manifest.
display: 'inline'without rich children is an inline atom: keep all its attributes and its private text children (accepted alternatives) as data. A block withcontent: 'inline'children is a container whose children (prompt, options) are editable rich text; keep child attributes (an option'scorrect) on the child. Keep unknown attributes (lang,dir) too, so nothing is lost. - Serialise to the content format and parse it back (
getSource,setSource). Write the real<astl-*>tags. - One element by id (
getElement,replaceElement): the inspector reads an element, applies a change withauthoring.updateand writes it back. Apply attribute-only changes in place, so the cursor stays where it was. - Selection: report the component at the cursor (a selected atom, the block around the cursor), move to one on
select(id)and focus the editor. - Events: call the
onChangelisteners after every document change and theonSelectionChangelisteners when the cursor enters or leaves a component.
Two details that save debugging time:
- Keep
<astl-*>custom elements out of the editing surface. The preview defines them, and an editor that renders<astl-gap>would get a live player input inside the document. The package's node views render chips and frames instead, and serialising and parsing happen in documents without a window (document.implementation.createHTMLDocument,DOMParser), where custom elements are never upgraded. - Never replace or nest into the component at the cursor when inserting: insert after it.
Other editors
Section titled “Other editors”The same five steps apply; only the vocabulary changes. Put the editor's element in <astl-editor>'s
slot="surface" and its adapter in adapter, and TipTap is never loaded.
- ProseMirror: the package without the TipTap wrappers (node specs with
toDOMandparseDOM, node views). - CKEditor 5: model elements for the atoms and blocks, with upcast and downcast converters for the
<astl-*>tags; the editing view can use UI elements or widgets for the chips. - Lexical: a decorator node for inline atoms, element nodes for blocks and their children,
importDOMandexportDOMfor the content format. - Quill, Slate and others: an embed or void inline element for atoms, a block element for blocks, and the editor's HTML import and export.
- A plain textarea:
createTextareaAdapterdoes all of this on text; see the authoring kit example.