@voithos-labs/aragonite 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +19 -0
- package/dist/a11y-strings.js +19 -0
- package/dist/action-contracts.d.ts +7 -1
- package/dist/ambient/ambient-dom.js +5 -1
- package/dist/block-component.d.ts +14 -0
- package/dist/components/BlockDragHandle.svelte +37 -24
- package/dist/components/BlockHost.svelte +18 -11
- package/dist/components/Editor.svelte +434 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +25 -17
- package/dist/components/SelectionOverlay.svelte.d.ts +1 -1
- package/dist/components/TailInsert.svelte +107 -0
- package/dist/components/TailInsert.svelte.d.ts +17 -0
- package/dist/components/block-content-selector.d.ts +6 -2
- package/dist/components/block-content-selector.js +6 -2
- package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/code/CodeBlock.svelte +211 -30
- package/dist/components/blocks/code/CodeBlockRail.svelte +704 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +16 -0
- package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
- package/dist/components/blocks/code/code-context-actions.js +24 -0
- package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
- package/dist/components/blocks/code/code-fence-exit.js +26 -0
- package/dist/components/blocks/code/code-languages.d.ts +6 -0
- package/dist/components/blocks/code/code-languages.js +22 -3
- package/dist/components/blocks/code/code-renderer.js +11 -0
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
- package/dist/components/blocks/editable-leaf.d.ts +37 -6
- package/dist/components/blocks/editable-leaf.js +248 -30
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListBlock.svelte +1 -0
- package/dist/components/blocks/list/ListItemBlock.svelte +14 -5
- package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.js +11 -2
- package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
- package/dist/components/blocks/surface-wiring.svelte.js +8 -1
- package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
- package/dist/components/blocks/table/TableBlock.svelte +205 -186
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
- package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
- package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
- package/dist/components/blocks/table/cell-clipboard.js +34 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
- package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
- package/dist/components/blocks/table/table-cell-paste.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
- package/dist/components/blocks/table/table-menu-model.js +36 -17
- package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
- package/dist/components/blocks/text/click-snap-guard.d.ts +3 -0
- package/dist/components/blocks/text/click-snap-guard.js +8 -0
- package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
- package/dist/components/blocks/text/delimiter-autopair.js +216 -0
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
- package/dist/components/blocks/text/edge-policy-dispatch.js +89 -20
- package/dist/components/blocks/text/live-selection-edit.d.ts +5 -5
- package/dist/components/blocks/text/live-selection-edit.js +37 -7
- package/dist/components/blocks/text/text-clipboard.js +2 -2
- package/dist/components/blocks/text/text-keydown.d.ts +7 -1
- package/dist/components/blocks/text/text-keydown.js +11 -1
- package/dist/components/blocks/text/text-render.d.ts +1 -1
- package/dist/components/blocks/text/text-render.js +3 -1
- package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
- package/dist/components/blocks/text/widget-interaction.js +181 -37
- package/dist/components/drag-handle.d.ts +41 -0
- package/dist/components/drag-handle.js +134 -0
- package/dist/components/editor-root-focus.d.ts +19 -0
- package/dist/components/editor-root-focus.js +67 -0
- package/dist/components/editor-root-geometry.d.ts +39 -0
- package/dist/components/editor-root-geometry.js +91 -0
- package/dist/components/editor-root-keydown.d.ts +1 -1
- package/dist/components/editor-root-keydown.js +12 -3
- package/dist/components/editor-root-listeners.d.ts +3 -6
- package/dist/components/editor-root-listeners.js +3 -24
- package/dist/components/editor-root-mode-flip.d.ts +36 -0
- package/dist/components/editor-root-mode-flip.js +92 -0
- package/dist/components/image/ImageOverlayHost.svelte +14 -6
- package/dist/components/image/ImageProperties.svelte +512 -70
- package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
- package/dist/components/image/ImageResizeHandles.svelte +67 -34
- package/dist/components/image/image-crop.d.ts +39 -0
- package/dist/components/image/image-crop.js +74 -0
- package/dist/components/image/image-edit-commit.d.ts +1 -0
- package/dist/components/image/image-edit-commit.js +21 -5
- package/dist/components/image/image-source-bytes.js +10 -3
- package/dist/components/image/image-widget-editing.js +1 -0
- package/dist/components/image/widget-dom.js +5 -1
- package/dist/components/link-card/link-card-commit.js +1 -1
- package/dist/components/lrd-map-gate.js +1 -1
- package/dist/components/menu/BlockMenu.svelte +315 -0
- package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
- package/dist/components/menu/MenuIcon.svelte +153 -0
- package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
- package/dist/components/menu/SelectionToolbar.svelte +385 -0
- package/dist/components/menu/SelectionToolbar.svelte.d.ts +16 -0
- package/dist/components/menu/clipboard-actions.d.ts +12 -0
- package/dist/components/menu/clipboard-actions.js +42 -0
- package/dist/components/menu/default-context-actions.d.ts +15 -0
- package/dist/components/menu/default-context-actions.js +76 -0
- package/dist/components/menu/flyout-placement.d.ts +6 -0
- package/dist/components/menu/flyout-placement.js +25 -0
- package/dist/core/inline/format-toggle.d.ts +12 -4
- package/dist/core/inline/format-toggle.js +94 -40
- package/dist/core/inline/image-dimensions.d.ts +3 -0
- package/dist/core/inline/image-dimensions.js +46 -10
- package/dist/core/inline/inline-widgets.d.ts +19 -0
- package/dist/core/inline/inline-widgets.js +5 -0
- package/dist/core/inline/scan/brackets.js +1 -0
- package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
- package/dist/core/inline/scan/plugin-syntax.js +15 -1
- package/dist/core/inline/transparency.js +3 -3
- package/dist/core/inline-render.d.ts +6 -0
- package/dist/core/inline-render.js +32 -0
- package/dist/core/nodes.d.ts +14 -0
- package/dist/cursor/edge-affinity.js +2 -1
- package/dist/cursor/height-oracle.d.ts +2 -3
- package/dist/cursor/height-oracle.js +0 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/scroll-hold.d.ts +10 -0
- package/dist/cursor/scroll-hold.js +22 -0
- package/dist/cursor/scrollport.d.ts +9 -0
- package/dist/cursor/scrollport.js +30 -1
- package/dist/cursor/visual-lines.d.ts +5 -4
- package/dist/cursor/visual-lines.js +56 -11
- package/dist/cursor/widget-edge-snap.d.ts +28 -0
- package/dist/cursor/widget-edge-snap.js +38 -0
- package/dist/cursor/widget-offset.d.ts +9 -0
- package/dist/cursor/widget-offset.js +71 -4
- package/dist/debug/interaction-trace.d.ts +4 -0
- package/dist/debug/interaction-trace.js +15 -0
- package/dist/decorations/decoration-state.svelte.js +1 -1
- package/dist/decorations/reserved-attrs.js +1 -0
- package/dist/editor-actions/ancestry-folds.d.ts +2 -2
- package/dist/editor-actions/ancestry-folds.js +1 -1
- package/dist/editor-actions/block-edit-scope.js +1 -1
- package/dist/editor-actions/commit/text-batch.d.ts +3 -2
- package/dist/editor-actions/commit/text-batch.js +1 -1
- package/dist/editor-actions/commit/undo-controller.js +4 -2
- package/dist/editor-actions/container-edit.js +2 -1
- package/dist/editor-actions/enter-completion.d.ts +2 -0
- package/dist/editor-actions/enter-completion.js +23 -2
- package/dist/editor-actions/focus/focus-dispatch.js +7 -4
- package/dist/editor-actions/focus/focus-landing.d.ts +10 -1
- package/dist/editor-actions/focus/focus-landing.js +20 -6
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/plugin/container.d.ts +7 -0
- package/dist/editor-actions/plugin/container.js +3 -1
- package/dist/editor-actions/reorder-action.js +19 -10
- package/dist/editor-actions/reorder-drag.js +29 -1
- package/dist/editor-actions/replacement-focus.d.ts +1 -1
- package/dist/editor-actions/replacement-focus.js +1 -1
- package/dist/editor-actions/search-replace.js +1 -1
- package/dist/editor-actions/table-context.d.ts +4 -1
- package/dist/editor-actions/table-context.js +57 -1
- package/dist/editor-events.d.ts +3 -0
- package/dist/editor-keys.d.ts +33 -0
- package/dist/editor-props.d.ts +24 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +16 -0
- package/dist/plugins/latex/BlockMath.svelte +264 -29
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
- package/dist/plugins/latex/index.d.ts +2 -1
- package/dist/plugins/latex/latex-kind.js +60 -10
- package/dist/plugins/latex/math-completion.js +4 -1
- package/dist/plugins/latex/math-layout.d.ts +15 -0
- package/dist/plugins/latex/math-layout.js +13 -0
- package/dist/plugins/latex/math-source.d.ts +19 -0
- package/dist/plugins/latex/math-source.js +103 -0
- package/dist/plugins/latex/register.d.ts +11 -2
- package/dist/plugins/latex/register.js +3 -1
- package/dist/plugins/latex/renderer.d.ts +3 -3
- package/dist/plugins/latex/renderer.js +13 -6
- package/dist/plugins/mermaid/MermaidBlock.svelte +19 -9
- package/dist/plugins/parrot/ParrotBlock.svelte +3 -1
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +75 -30
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/commands.d.ts +9 -5
- package/dist/schema/commands.js +12 -7
- package/dist/schema/context-actions.d.ts +31 -0
- package/dist/schema/context-actions.js +23 -0
- package/dist/schema/fenced-code-raw.js +31 -1
- package/dist/schema/operations.d.ts +8 -1
- package/dist/schema/reserved-chords.js +37 -4
- package/dist/schema/table-cell-raw.d.ts +1 -1
- package/dist/schema/table-cell-raw.js +1 -1
- package/dist/selection/block-hit-test.js +3 -2
- package/dist/selection/char-endpoint-snap.js +1 -1
- package/dist/selection/clipboard-text.js +6 -1
- package/dist/selection/covered-block.d.ts +10 -0
- package/dist/selection/covered-block.js +24 -0
- package/dist/selection/cross-block/dispatch.d.ts +3 -0
- package/dist/selection/cross-block/dispatch.js +13 -1
- package/dist/selection/cross-block/format-range.d.ts +1 -1
- package/dist/selection/cross-block/format-range.js +4 -15
- package/dist/selection/cross-block/format-toggle.js +1 -1
- package/dist/selection/cross-block/keydown.js +3 -29
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +31 -35
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +52 -13
- package/dist/selection/dead-space-caret.d.ts +13 -0
- package/dist/selection/dead-space-caret.js +57 -1
- package/dist/selection/drag-pointer.d.ts +26 -2
- package/dist/selection/drag-pointer.js +103 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.d.ts +3 -2
- package/dist/selection/keyboard-extend.js +4 -3
- package/dist/selection/multi-click.d.ts +42 -0
- package/dist/selection/multi-click.js +203 -0
- package/dist/selection/native-bridge.d.ts +3 -0
- package/dist/selection/native-bridge.js +31 -2
- package/dist/selection/path-lookup.d.ts +10 -2
- package/dist/selection/path-lookup.js +11 -4
- package/dist/selection/pointer-gesture.d.ts +9 -0
- package/dist/selection/pointer-gesture.js +11 -0
- package/dist/selection/primitives.d.ts +7 -0
- package/dist/selection/primitives.js +19 -1
- package/dist/selection/range-delete-ceremony.js +4 -2
- package/dist/selection/range-delete-chrome.js +2 -1
- package/dist/selection/range-delete-table-coverage.js +2 -1
- package/dist/selection/range-delete-table.js +3 -2
- package/dist/selection/range-delete.d.ts +4 -0
- package/dist/selection/range-delete.js +31 -3
- package/dist/selection/selection-drop.d.ts +34 -0
- package/dist/selection/selection-drop.js +253 -0
- package/dist/selection/selection-restore.js +1 -1
- package/dist/selection/selection-state.svelte.d.ts +6 -0
- package/dist/selection/selection-state.svelte.js +36 -1
- package/dist/selection/table-endpoint-snap.js +1 -1
- package/dist/selection/table-rect-extend.js +1 -1
- package/dist/styles/editor-theme.css +75 -31
- package/dist/styles/editor.css +268 -18
- package/dist/testing/container-conformance.js +2 -2
- package/dist/testing/inline-conformance.js +2 -1
- package/dist/tree-operations/blockquote.js +1 -1
- package/dist/tree-operations/chain-rebuild.d.ts +63 -0
- package/dist/tree-operations/chain-rebuild.js +142 -0
- package/dist/tree-operations/children.d.ts +1 -1
- package/dist/tree-operations/children.js +1 -1
- package/dist/tree-operations/cleanup.js +1 -1
- package/dist/tree-operations/content-write.d.ts +50 -0
- package/dist/tree-operations/content-write.js +263 -0
- package/dist/tree-operations/index.d.ts +8 -3
- package/dist/tree-operations/index.js +6 -2
- package/dist/tree-operations/list/exit-replacement.js +1 -1
- package/dist/tree-operations/list/unwrap-merge.js +3 -3
- package/dist/tree-operations/node-ops.d.ts +17 -234
- package/dist/tree-operations/node-ops.js +47 -1113
- package/dist/tree-operations/node-primitives.d.ts +75 -0
- package/dist/tree-operations/node-primitives.js +117 -0
- package/dist/tree-operations/paste/apply.js +1 -1
- package/dist/tree-operations/paste/body-write.d.ts +1 -1
- package/dist/tree-operations/paste/body-write.js +2 -2
- package/dist/tree-operations/paste/container-match.js +4 -2
- package/dist/tree-operations/paste/dispatch.js +2 -1
- package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
- package/dist/tree-operations/paste/focus-target.d.ts +1 -1
- package/dist/tree-operations/paste/list-absorb.js +1 -1
- package/dist/tree-operations/paste/list-break-out.js +1 -1
- package/dist/tree-operations/paste/parent-scope.js +1 -1
- package/dist/tree-operations/paste/paste-replacement.js +1 -1
- package/dist/tree-operations/paste/replace-block-at-parent.d.ts +3 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +5 -2
- package/dist/tree-operations/paste/replacement-parse.d.ts +17 -0
- package/dist/tree-operations/paste/replacement-parse.js +22 -0
- package/dist/tree-operations/path-mutate.d.ts +1 -1
- package/dist/tree-operations/path-mutate.js +2 -1
- package/dist/tree-operations/reorder-unit.js +1 -1
- package/dist/tree-operations/reorder.d.ts +5 -2
- package/dist/tree-operations/reorder.js +55 -2
- package/dist/tree-operations/settle.d.ts +105 -0
- package/dist/tree-operations/settle.js +660 -0
- package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
- package/dist/tree-operations/table-grid-clipboard.js +90 -0
- package/dist/tree-operations/unshare.d.ts +15 -77
- package/dist/tree-operations/unshare.js +15 -166
- package/docs/guide/consumer-guide.md +136 -104
- package/docs/guide/plugin-api.md +73 -29
- package/docs/guide/plugin-guide.md +55 -23
- package/package.json +9 -7
- package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
- package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
- package/dist/components/blocks/table/TableGrip.svelte +0 -91
- package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
- package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
- package/dist/components/blocks/table/table-drop-target.js +0 -16
- package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
- package/dist/components/blocks/table/table-reorder-drag.js +0 -97
package/docs/guide/plugin-api.md
CHANGED
|
@@ -26,6 +26,7 @@ The groups, in page order:
|
|
|
26
26
|
| [Kind declaration](#kind-declaration) | Minting a new block type's identity |
|
|
27
27
|
| [The block-kind descriptor](#the-block-kind-descriptor) | Telling the editor how your block type behaves, and the checklist every one must fill in |
|
|
28
28
|
| [The component registry](#the-component-registry) | Binding a block type to the Svelte component that renders it |
|
|
29
|
+
| [Code-block languages](#code-block-languages) | Adding syntax-highlighting grammars beyond the bundled set |
|
|
29
30
|
| [The parser opener](#the-parser-opener) | Teaching the parser to recognize your block's syntax |
|
|
30
31
|
| [Enter completion](#enter-completion) | Letting one typed line become a construct whose lines must sit together |
|
|
31
32
|
| [Registration probes](#registration-probes) | Checking what's already registered, so a module that runs twice stays safe |
|
|
@@ -39,6 +40,7 @@ The groups, in page order:
|
|
|
39
40
|
| [Events](#events) | What the editor tells your plugin has happened, and the shapes it says it in |
|
|
40
41
|
| [Rects](#rects) | Where things are on screen: block boxes, ranges, the caret, scrolling to a block |
|
|
41
42
|
| [Caret geometry](#caret-geometry) | Answering where a press inside your block puts the caret |
|
|
43
|
+
| [Pointer gestures](#pointer-gestures) | Keeping a drag that belongs to your block (a pan, a brush) from starting a selection |
|
|
42
44
|
| [Selection geometry](#selection-geometry) | The shapes that describe what the user has selected |
|
|
43
45
|
| [Parse and serialize](#parse-and-serialize) | Markdown in, tree out, and back again |
|
|
44
46
|
| [Grammar scanners](#grammar-scanners) | The editor's own code-fence, HTML-tag, and blockquote rules, reusable so you never fork them |
|
|
@@ -118,7 +120,7 @@ _(pre-freeze / unstable)_ The recipe: [Typing a multi-line construct into existe
|
|
|
118
120
|
| Export | Role |
|
|
119
121
|
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
122
|
| `registerBlockCompleter` | Let one typed line complete into a grammar whose lines must sit adjacent, which Enter alone can never type |
|
|
121
|
-
| `BlockCompleter` | The contract: `tryComplete(line)` claims with a result, or declines with null
|
|
123
|
+
| `BlockCompleter` | The contract: `tryComplete(line)` claims with a result, or declines with null; `onType: true` also consults it as the line is typed |
|
|
122
124
|
| `CompletionResult` | A claim: the lines to insert, endings omitted (the editor attaches the document's own), plus where the caret seats inside the insertion |
|
|
123
125
|
|
|
124
126
|
### Registration probes
|
|
@@ -152,17 +154,17 @@ _(pre-freeze / unstable)_ The `:::name` grammar itself is the [directives guide]
|
|
|
152
154
|
|
|
153
155
|
_(pre-freeze / unstable)_ A container's **chrome** is its own furniture: the border, the title line, an icon if you like. The factory hides everything else (child-list state, ancestor wiring, the mounting of only what's visible), so your component only has to supply the chrome. Worked end to end in [the walkthrough](plugin-guide.md#walkthrough-a-conspiracy-container-end-to-end).
|
|
154
156
|
|
|
155
|
-
| Export | Role
|
|
156
|
-
| ----------------------------------------------- |
|
|
157
|
-
| `createContainerBlock` | Wire a nested-child-list container so your component is as thin as the built-in blockquote's
|
|
158
|
-
| `BlockList` | The child-list component your container renders, spread with the factory's props, as a direct child of your box
|
|
159
|
-
| `registerChromeLeaf` | Register a container's title or summary line as a kind of its own, with a sensible default keymap
|
|
160
|
-
| `chromeChild` | Build the reserved child-0 node for that line: the title text plus its newline (an empty title keeps the bare newline)
|
|
161
|
-
| `isCollapsedContainer` | Read a container's collapse state through its descriptor, so your component and the editor's own walks agree
|
|
162
|
-
| `ContainerBlock`, `ContainerBlockComponent` | What the factory returns (the child-list props, the `containerApi` you publish, the keydown handler, plus the commit, focus-exit, mode, theme and
|
|
163
|
-
| `ContainerBlockDeps`, `ContainerBlockListProps` | The factory's inputs, live getters rather than captured values, and the props `BlockList` takes
|
|
164
|
-
| `RefSlots` | The per-child reference accessors the child-list props carry
|
|
165
|
-
| `ChromeLeafOptions` | `registerChromeLeaf`'s options: a CSS class for styling the line, keymap overrides, the merge role
|
|
157
|
+
| Export | Role |
|
|
158
|
+
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
159
|
+
| `createContainerBlock` | Wire a nested-child-list container so your component is as thin as the built-in blockquote's |
|
|
160
|
+
| `BlockList` | The child-list component your container renders, spread with the factory's props, as a direct child of your box |
|
|
161
|
+
| `registerChromeLeaf` | Register a container's title or summary line as a kind of its own, with a sensible default keymap |
|
|
162
|
+
| `chromeChild` | Build the reserved child-0 node for that line: the title text plus its newline (an empty title keeps the bare newline) |
|
|
163
|
+
| `isCollapsedContainer` | Read a container's collapse state through its descriptor, so your component and the editor's own walks agree |
|
|
164
|
+
| `ContainerBlock`, `ContainerBlockComponent` | What the factory returns (the child-list props, the `containerApi` you publish, the keydown handler, plus the commit, focus-exit, mode, theme, options and scroll-hold entries), and the shape that `containerApi` must satisfy |
|
|
165
|
+
| `ContainerBlockDeps`, `ContainerBlockListProps` | The factory's inputs, live getters rather than captured values, and the props `BlockList` takes |
|
|
166
|
+
| `RefSlots` | The per-child reference accessors the child-list props carry |
|
|
167
|
+
| `ChromeLeafOptions` | `registerChromeLeaf`'s options: a CSS class for styling the line, keymap overrides, the merge role |
|
|
166
168
|
|
|
167
169
|
### Editable-leaf authoring
|
|
168
170
|
|
|
@@ -177,27 +179,51 @@ _(pre-freeze / unstable)_ The container factory's sibling for leaves; the full s
|
|
|
177
179
|
| `EditableLeafDeps` | The factory's inputs: live getters for the node, the index, the path, and your source element, plus the static `mode` and `singleLine` settings |
|
|
178
180
|
| `StickyColumnDirection` | Which vertical direction the caret is entering your block from, handed to `focusAtColumn` so the column carries across lines |
|
|
179
181
|
|
|
182
|
+
### Code-block languages
|
|
183
|
+
|
|
184
|
+
_(pre-freeze / unstable)_ The syntax-highlighting registry behind fenced code. The editor bootstraps a curated couple dozen (the usual suspects: javascript, python, rust, bash, sql, the C family, and friends) plus their aliases, because every grammar is static bundle weight for every consumer. `listLanguages()` tells you exactly which ones you have; a host needing more registers them itself.
|
|
185
|
+
|
|
186
|
+
Register **before mounting an editor**: a block already on screen re-tokenizes only when its own bytes next change. An unregistered language is not an error: the fence still authors, commits and round-trips, and its body renders untokenized.
|
|
187
|
+
|
|
188
|
+
| Export | Role |
|
|
189
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
190
|
+
| `registerLanguage` | Add a grammar under a name, with optional aliases; idempotent, so a repeat call with the same name is a no-op |
|
|
191
|
+
| `listLanguages` | Every registered language once, under its canonical name, sorted: the rows the code block's language picker offers |
|
|
192
|
+
| `getLanguageAliases` | The other spellings a language answers to, asked by any of them: what a picker's filter matches on |
|
|
193
|
+
| `highlightCode` | The code block's tokenizer: `(body, language)` to a text-preserving fragment of `code-tok-*` spans, for a plugin's own source surface |
|
|
194
|
+
| `LanguageGrammar` | The registry's read shape: the resolved name and its definition |
|
|
195
|
+
| `LanguageFn` | highlight.js's grammar-definition type, re-exported so you needn't import highlight.js directly (you hold it only as a transitive dep) |
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { registerLanguage } from '@voithos-labs/aragonite/plugin';
|
|
199
|
+
import elixir from 'highlight.js/lib/languages/elixir';
|
|
200
|
+
|
|
201
|
+
registerLanguage('elixir', elixir, ['ex', 'exs']);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The picker shows `elixir` once, and `ex` and `exs` are search keys for that row, so a user typing `ex` finds it without knowing the full name.
|
|
205
|
+
|
|
180
206
|
### Inline authoring
|
|
181
207
|
|
|
182
208
|
_(pre-freeze / unstable)_ Syntax inside a paragraph: recognize it at a trigger character, render it as an **atomic widget** (one indivisible rendered thing the caret can sit beside but not inside), give it an editing policy. A **rung** is one level in the ordered ladder of recognizers a trigger consults. The render paths and the tier's limits: [Inline kinds](plugin-guide.md#inline-kinds).
|
|
183
209
|
|
|
184
|
-
| Export | Role
|
|
185
|
-
| --------------------------------------------------------- |
|
|
186
|
-
| `declarePluginInlineKind` | Mint an inline kind, the one-level-down twin of `declarePluginKind`
|
|
187
|
-
| `declaredPluginInlineKind` | Recover a declared inline kind in another module
|
|
188
|
-
| `isInlineKindDeclared` | The is-it-there check for an inline kind
|
|
189
|
-
| `registerInlineSyntax` | Hook the inline scanner on one trigger character with your recognizer; a reserved trigger (one a built-in owns, like `[`) takes a prefix rung
|
|
190
|
-
| `INLINE_PRIORITIES` | The inline ladder, lower consulted first: `prefixOverride` outranks a reserved trigger's built-in case, `plugin` is the bare-trigger default
|
|
191
|
-
| `InlineSyntaxRecognizer` | The recognizer contract: inspect the raw at the trigger, claim a span by returning a node, or decline with null
|
|
192
|
-
| `InlineSyntaxOptions` | The options bag: the multi-character `prefix`, the `priority`, and `
|
|
193
|
-
| `ImageSyntaxRewriter`, `ImageFields` | The `rewriteImage` contract, for a rung whose recognizer builds built-in image nodes and must write edits back in its own syntax, and the edited fields it receives
|
|
194
|
-
| `registerInlineWidgetKind` | Render an inline kind as a live atomic widget: a Svelte `component` (recommended) or a hand-built `buildWidget`, never both
|
|
195
|
-
| `mintWidgetShell` | Mint the marked, source-stamped span a `buildWidget` returns; its attributes are what the caret's position walk reads
|
|
196
|
-
| `PluginInlineKind`, `InlineNode` | The inline kind type, and the node your recognizer builds
|
|
197
|
-
| `InlineWidgetDescriptor` | The widget registration: the is-this-a-widget test, one render path, the editing policy
|
|
198
|
-
| `InlineWidgetComponentProps` | A component widget's props: frozen `{ inline, source }`, plus live getters for the mode, the theme, the document, and the content version, and `navigateTo` to jump to another block, optionally at an offset in it (aim at a leaf: a container path scrolls into view but seats no caret)
|
|
199
|
-
| `InlineWidgetEditingPolicy`, `InlineWidgetEditingContext` | The policy (reveal source on caret entry, delete granularity, edge behavior, a selected-key handler, whether the widget claims the activation click), and the context that handler receives
|
|
200
|
-
| `isWidgetActivationClick` | Whether a click activates a widget: a Ctrl/Cmd chord while editing, a plain click in reading mode
|
|
210
|
+
| Export | Role |
|
|
211
|
+
| --------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
212
|
+
| `declarePluginInlineKind` | Mint an inline kind, the one-level-down twin of `declarePluginKind` |
|
|
213
|
+
| `declaredPluginInlineKind` | Recover a declared inline kind in another module |
|
|
214
|
+
| `isInlineKindDeclared` | The is-it-there check for an inline kind |
|
|
215
|
+
| `registerInlineSyntax` | Hook the inline scanner on one trigger character with your recognizer; a reserved trigger (one a built-in owns, like `[`) takes a prefix rung |
|
|
216
|
+
| `INLINE_PRIORITIES` | The inline ladder, lower consulted first: `prefixOverride` outranks a reserved trigger's built-in case, `plugin` is the bare-trigger default |
|
|
217
|
+
| `InlineSyntaxRecognizer` | The recognizer contract: inspect the raw at the trigger, claim a span by returning a node, or decline with null |
|
|
218
|
+
| `InlineSyntaxOptions` | The options bag: the multi-character `prefix`, the `priority`, `rewriteImage`, and `autoPair` |
|
|
219
|
+
| `ImageSyntaxRewriter`, `ImageFields` | The `rewriteImage` contract, for a rung whose recognizer builds built-in image nodes and must write edits back in its own syntax, and the edited fields it receives |
|
|
220
|
+
| `registerInlineWidgetKind` | Render an inline kind as a live atomic widget: a Svelte `component` (recommended) or a hand-built `buildWidget`, never both |
|
|
221
|
+
| `mintWidgetShell` | Mint the marked, source-stamped span a `buildWidget` returns; its attributes are what the caret's position walk reads |
|
|
222
|
+
| `PluginInlineKind`, `InlineNode` | The inline kind type, and the node your recognizer builds |
|
|
223
|
+
| `InlineWidgetDescriptor` | The widget registration: the is-this-a-widget test, one render path, the editing policy |
|
|
224
|
+
| `InlineWidgetComponentProps` | A component widget's props: frozen `{ inline, source }`, plus live getters for the mode, the theme, the document, and the content version, and `navigateTo` to jump to another block, optionally at an offset in it (aim at a leaf: a container path scrolls into view but seats no caret) |
|
|
225
|
+
| `InlineWidgetEditingPolicy`, `InlineWidgetEditingContext` | The policy (reveal source on caret entry, where the content sits inside the delimiters, which offset a press on the rendered widget names via `revealOffsetAtPoint` so a click seats the caret where it landed, delete granularity, edge behavior, a selected-key handler, whether the widget claims the activation click), and the context that handler receives |
|
|
226
|
+
| `isWidgetActivationClick` | Whether a click activates a widget: a Ctrl/Cmd chord while editing, a plain click in reading mode |
|
|
201
227
|
|
|
202
228
|
### Commands and keybindings
|
|
203
229
|
|
|
@@ -206,6 +232,10 @@ _(pre-freeze / unstable)_ Which tier dispatches what: [Block commands](plugin-gu
|
|
|
206
232
|
| Export | Role |
|
|
207
233
|
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
208
234
|
| `registerBlockCommand` | Mint a `(kind, name)` command and get back its id, for a keymap binding to target |
|
|
235
|
+
| `registerBlockContextActions` | Add the actions a right-click on a block of `kind` offers (its context menu), ahead of the editor's own copy, replace and remove rows |
|
|
236
|
+
| `BlockContextAction` | One such action: id, label, optional glyph and danger flag, and `run(ctx)` |
|
|
237
|
+
| `BlockActionContext` | What `run` receives: the node, its path, `deleteBlock()` and `replaceRaw(raw)` |
|
|
238
|
+
| `BlockContextActionProvider` | The registered function: `(node, path) => BlockContextAction[]`, consulted on every open |
|
|
209
239
|
| `registerGlobalCommand` | Mint a process-wide command run against whichever editor dispatched it, optionally on a global chord; also returns its id |
|
|
210
240
|
| `CommandId` | A built-in command's id; a vocabulary your keymaps may bind too |
|
|
211
241
|
| `KeyBinding` | One keymap entry: a chord (fixed-order `Mod` / `Alt` / `Shift` plus the key), a command id, an optional baked argument |
|
|
@@ -264,6 +294,20 @@ _(pre-freeze / unstable)_ What a kind fills its descriptor's `caretTargetAtPoint
|
|
|
264
294
|
| `CaretTarget` | What the hook answers: the child path to the leaf (empty when your block is the leaf) and the offset inside it |
|
|
265
295
|
| `CURSOR_END`, `CursorEnd` | The offset meaning "wherever that leaf ends", and its type; a plain `0` is the other end, since that one is a real offset |
|
|
266
296
|
|
|
297
|
+
### Pointer gestures
|
|
298
|
+
|
|
299
|
+
_(pre-freeze / unstable)_ One attribute, for a block whose drags are its own (a diagram you pan, a canvas you draw on). Without it the editor reads the press as the start of a selection, and your pan runs under a painted block range. Calling `stopPropagation()` in your own handler doesn't help: Svelte delivers pointer events from the app root, so the editor's listener has already run.
|
|
300
|
+
|
|
301
|
+
| Export | Role |
|
|
302
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
303
|
+
| `POINTER_GESTURE_ATTR` | Put it on the element whose drags are yours. A press inside it is left alone by drag-to-select, and by the double and triple click too, as long as the element sits outside any `contenteditable` (an island inside your own editable text is the inline widget's door instead). The editor reads it at press time, so declare it always or only while your gesture is armed |
|
|
304
|
+
|
|
305
|
+
```svelte
|
|
306
|
+
<div class="viewport" {...{ [POINTER_GESTURE_ATTR]: focused ? '' : undefined }} onpointerdown={beginPan}>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
That is what mermaid does: unfocused, a drag on the diagram selects like it would on any block; focused, it pans.
|
|
310
|
+
|
|
267
311
|
### Selection geometry
|
|
268
312
|
|
|
269
313
|
_(pre-freeze / unstable)_ The selection shapes a decoration source or geometry consumer reads.
|
|
@@ -260,7 +260,9 @@ cNo.....................................oc
|
|
|
260
260
|
font-size: 1.1em;
|
|
261
261
|
line-height: 1.1;
|
|
262
262
|
letter-spacing: 0.05em;
|
|
263
|
-
/* one frame tall, in the reel's own rows so a step lands on the next frame exactly
|
|
263
|
+
/* one frame tall, in the reel's own rows so a step lands on the next frame exactly; the
|
|
264
|
+
em line is the same height for engines without lh (Safari before 16.4) */
|
|
265
|
+
height: calc(var(--parrot-rows) * 1.1em);
|
|
264
266
|
height: calc(var(--parrot-rows) * 1lh);
|
|
265
267
|
/* wider than a phone column, and the editor root pans if it isn't contained; the bar
|
|
266
268
|
would sit across the bird, which is decoration rather than a pane to scroll */
|
|
@@ -878,20 +880,25 @@ Three rules for that file, each earned the hard way:
|
|
|
878
880
|
|
|
879
881
|
The factory returns more than the walkthrough destructures:
|
|
880
882
|
|
|
881
|
-
| Return
|
|
882
|
-
|
|
|
883
|
-
| `updateOwnMetadata`
|
|
884
|
-
| `moveFocusOut`
|
|
885
|
-
| `getPresentationMode`
|
|
886
|
-
| `getTheme`
|
|
887
|
-
| `getOptions`
|
|
883
|
+
| Return | When you reach for it |
|
|
884
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
885
|
+
| `updateOwnMetadata` | Your component writes its own node's metadata (a collapse toggle, an edited setting). The sanctioned commit path; in reading mode, which writes no bytes, it declines as a no-op and dev builds warn |
|
|
886
|
+
| `moveFocusOut` | A plugin-owned editing surface whose caret ran off its own edge; hands the caret to the neighbour a plain arrow points at, through the editor's focus traversal, so the landing skips non-focusable blocks, enters containers, and reveals an unmounted target like any other arrow |
|
|
887
|
+
| `getPresentationMode` | Your rendering or a gesture needs the live presentation mode ([Presentation modes](#presentation-modes)) |
|
|
888
|
+
| `getTheme` | Your content's colors are painted by an engine rather than styled by CSS; token-styled chrome needs neither this nor `getPresentationMode`, it rethemes through the cascade |
|
|
889
|
+
| `getOptions` | This editor instance's options for the plugin owning your kind, typed `unknown`; the per-instance channel a factory argument can't reach ([the options recipe](#recipe-per-instance-options-and-the-factory-closure-trap)) |
|
|
890
|
+
| `captureScrollPosition` | Your component is about to swap its view for one of a different height (a tall diagram for its short source card) and the reader is scrolled right at it. Call it before the swap, await what it hands back after, and the page stays where the reader left it instead of clamping to the shorter layout in between |
|
|
888
891
|
|
|
889
892
|
```ts
|
|
890
|
-
const { updateOwnMetadata, getPresentationMode, getTheme, getOptions } =
|
|
893
|
+
const { updateOwnMetadata, getPresentationMode, getTheme, getOptions, captureScrollPosition } =
|
|
894
|
+
createContainerBlock(deps);
|
|
891
895
|
updateOwnMetadata({ name: 'debunked' }); // one undo entry; rebuildRaw re-emits the opener line as :::debunked
|
|
892
896
|
getPresentationMode(); // 'source'
|
|
893
897
|
getTheme(); // 'dark'
|
|
894
898
|
getOptions(); // whatever this editor's { plugin, options } entry carried; undefined for a bare unit
|
|
899
|
+
const restore = captureScrollPosition(); // before the swap...
|
|
900
|
+
editing = true;
|
|
901
|
+
await restore(); // ...and after; a no-op when nothing moved
|
|
895
902
|
```
|
|
896
903
|
|
|
897
904
|
One dep is worth knowing about too. A marker-bearing container (a footnote definition's `[^label]: `, mirroring a list item's `- `) hands the factory a **`getAmbientPrefix`** getter. Its first child then paints that prefix as a dimmed, read-only run before its own bytes, and the caret and offset walk skip it exactly as they do a list marker. Read it live, so a marker derived from metadata re-renders after an edit. Return a string, or `{ text, interactive }` to make ranges of it clickable: each range gets its own span, class and click handler, which is how a task list's checkbox toggles and how a footnote definition's `[^label]` takes the click back to its reference.
|
|
@@ -1161,9 +1168,11 @@ leaf.getOptions(); // this editor's options for your plugin, typed unknown
|
|
|
1161
1168
|
|
|
1162
1169
|
**Native parity is the tier's whole claim**: the editor's caret enters and leaves your block like any built-in text block (including keeping its column as it walks up or down lines), IME composition is respected, undo batches like prose, the clipboard is intercepted for plain-Markdown copy/cut/paste like every editable surface, and a cross-block selection sweeps through your text.
|
|
1163
1170
|
|
|
1164
|
-
**One spread wires the source surface.** Write `<div {...leaf.surfaceProps}>` on your source contenteditable and the
|
|
1171
|
+
**One spread wires the source surface.** Write `<div {...leaf.surfaceProps}>` on your source contenteditable and the DOM handlers, the `contenteditable` / `role` / `tabindex` / `spellcheck` attributes, and two view-lifecycle contracts all land at once, so a forgotten handler (a dropped `oncompositionend` that silently breaks IME) simply can't happen to you. The two contracts the spread owns are the ones every consumer used to hand-write: the source is populated so that **`textContent === source`** (the walk that maps DOM positions to byte offsets depends on it), and focus is parked on the editor root when the source unmounts.
|
|
1165
1172
|
|
|
1166
|
-
That
|
|
1173
|
+
That text carries every newline your source holds, which makes **`white-space: pre-wrap` (or `pre`) on your source element part of the contract** for any leaf whose bytes can span lines. Without it the browser collapses the line breaks on screen while the offset walk goes on counting them, and the caret sits nowhere near where it looks.
|
|
1174
|
+
|
|
1175
|
+
**A painted source.** By default the source is one text node. A `renderSource(text)` dep paints it as DOM instead (fence lines the marker-hiding modes collapse, highlight tokens; the `highlightCode` export is the code block's own tokenizer), and the factory asserts `textContent === text` on every paint, so a painter that drops a byte fails loudly in dev rather than corrupting a commit. A painted source takes its plain-text edits from the leaf, not the browser: typing, Enter, deletes and pastes splice the text and repaint, each reported through `onSourceEdit(text)` so a live preview can follow the draft, and undo inside the open reveal walks those edits back before it reaches the document's history. `repaintSource()` re-runs the painter after a native edit (an IME commit), and `completeBareSource(text)` lets a kind complete a chrome-only source (a `$$` straight over `$$`) to the shape a caret can sit in, asked as the source is revealed and again after any edit that empties it, so a one-line `$$x^2$$` that loses its `x^2` never shows bare fences. Block math is the worked example.
|
|
1167
1176
|
|
|
1168
1177
|
A leaf whose bytes are one line (the parrot's opener claims exactly one) declares `singleLine: true` and needs none of that. Enter in one of those ends the block: the text after the caret becomes a paragraph below and the caret goes with it, which is what Enter does in a heading. With the flag off, the default, Enter types a newline.
|
|
1169
1178
|
|
|
@@ -1188,7 +1197,7 @@ Editing past your own fence therefore re-splits the document instead of wedging
|
|
|
1188
1197
|
|
|
1189
1198
|
**Per-instance configuration.** `leaf.getOptions()` returns this editor instance's options for the plugin owning your kind, typed `unknown` for you to narrow. It's the same route as the container factory's `getOptions()`, one tier down, and the same rule applies ([the options recipe](#recipe-per-instance-options-and-the-factory-closure-trap)). The bundled toc block resolves `maxDepth` this way and falls back to the factory argument, which then serves as the default for an instance declaring none.
|
|
1190
1199
|
|
|
1191
|
-
Block math (`$$…$$` in the bundled `@voithos-labs/aragonite/plugins/latex` plugin) is the worked example, and it's smaller than you'd expect: its component script is the factory call, one render effect (KaTeX), a `{...leaf.surfaceProps}` spread on the source, and one-line re-exports of the returned surface. Registration is the ordinary leaf recipe: `registerBlockKind` (no container group), `registerBlockOpener`, `registerBlockComponent`.
|
|
1200
|
+
Block math (`$$…$$` in the bundled `@voithos-labs/aragonite/plugins/latex` plugin) is the worked example, and it's smaller than you'd expect: its component script is the factory call, one render effect (KaTeX), a `{...leaf.surfaceProps}` spread on the source, and one-line re-exports of the returned surface. Registration is the ordinary leaf recipe: `registerBlockKind` (no container group), `registerBlockOpener`, `registerBlockComponent`. Its `caretTargetAtPoint` is the other half of the parrot's: where the parrot's caption is the source bytes minus a prefix, KaTeX paints glyphs no offset maps back to, so the render effect stamps the body's span on the rendered element and the hook walks that span in proportion to how far along the press fell.
|
|
1192
1201
|
|
|
1193
1202
|
## Presentation modes
|
|
1194
1203
|
|
|
@@ -1268,7 +1277,7 @@ fence claim ──▶ opaque container, NO children ──▶ component renders
|
|
|
1268
1277
|
- **Edit mode commits through `updateOwnMetadata`.** The component swaps its body to a plugin-owned `<textarea>` seeded from metadata; commit (Ctrl+Enter, blur) writes the new code with the container factory's `updateOwnMetadata`, which is one undoable entry, with your `rebuildRaw` re-emitting the fence so `getSource()` reflects the edit byte-exactly. Escape cancels without touching the tree.
|
|
1269
1278
|
- **Inject the renderer, memoize it, own its CSS.** The engine is the consumer's dependency: take it as a plugin option (`mermaidPlugin({ renderer })`) and pass it by module to the component. Wrap it in `createBoundedMemo` so re-renders of unchanged code do zero engine work. An async renderer stores the render promise as the cached value (in-flight work is shared, and a failure is cached like a success), and a renderer whose result holds a live DOM node passes a `cloneOnRead` so each caller gets its own copy. Resolve failures to a legible inline error, never a throw, and render a static code fallback with a note when no renderer is configured. The engine's stylesheet travels with the renderer module, so import it there, where no route can forget it: a KaTeX-based renderer needs `katex/dist/katex.min.css`, or its MathML accessibility tree lays out unclipped and every equation paints twice.
|
|
1270
1279
|
- **If the engine paints its own colors, the theme is a render input.** An engine that emits markup carrying color literals (a diagram SVG) can't be rethemed by a stylesheet after the fact; the diagram has to be redrawn. So the theme belongs in three places at once, and any one of them alone leaves a broken half: **the renderer's parameters** (so it can draw for the theme), **the memo key** (so a flip misses and a flip back is still a hit, never a cache reset, which throws away work you'll want again), and **the component's render read** (`getTheme()` off the container or leaf factory), because THAT read is what subscribes the block to the flip. Mermaid keys `theme\0code`; its engine adapter maps the editor theme name to a mermaid theme and re-initializes when it changes, serializing renders because that config is process-global. An engine styled by CSS variables needs none of this.
|
|
1271
|
-
- **Interior interactivity stays inside your DOM.** Pan/zoom, buttons, overlays:
|
|
1280
|
+
- **Interior interactivity stays inside your DOM.** Pan/zoom, buttons, overlays: put `POINTER_GESTURE_ATTR` on the element whose drags are yours (only while the gesture is armed, if it isn't always), or the editor reads the press as the start of a selection and paints a range over your pan. `stopPropagation()` on pointerdown can't do this, since Svelte delivers pointer events from the app root and the editor's listener has already run. A focus view is just a fixed-position overlay in the component's own tree, so mount it in place, focus it on open, close on Escape.
|
|
1272
1281
|
- **View-state commands reach the component through `ctx.hooks`.** See [Block commands](#block-commands).
|
|
1273
1282
|
|
|
1274
1283
|
The two helpers from that list, with what they hand back:
|
|
@@ -1381,6 +1390,8 @@ If your `revealSource` widget takes a click of its own, declare `claimsActivatio
|
|
|
1381
1390
|
|
|
1382
1391
|
If your widget derives from the whole document, read the version inside the same `$derived` and use it as your memo key. The document itself isn't a usable key: the editor mutates it in place, so its identity never changes, and an identity-keyed memo hits forever on a stale answer. Reading the version inside the derived is also what subscribes your widget to edits anywhere, so N widgets sharing one memoized walk stay as live as N widgets each walking the document.
|
|
1383
1392
|
|
|
1393
|
+
**A symmetric delimiter can close itself as it is typed.** Pass `autoPair: true` on a bare trigger whose construct opens and closes on the same byte, the way the bundled latex plugin does for `$…$`: typing the trigger lands its twin after the caret, typing it again over that twin steps past it, and a first body byte that leaves the pair no construct (`$5` is a price) drops the twin again. Without it a lone `$` typed ahead of an existing formula pairs with that formula's closer and wraps the prose between them. The built-in backtick, `*`, `_` and `~~` behave this way without registration.
|
|
1394
|
+
|
|
1384
1395
|
**A bare trigger must be a character no built-in scanner claims.** Registering a bare recognizer on a reserved trigger (`` ` ``, `&`, `<`, `*`, `_`, `~`, `[`, `]`, `!`, `\`, or newline) throws: built-in dispatch runs first, so a bare recognizer there would never fire, and a silent no-op is the one failure a public API must not have.
|
|
1385
1396
|
|
|
1386
1397
|
The bundled **emoji** plugin (`@voithos-labs/aragonite/plugins/emoji`) is this bare-trigger recipe end to end and the worked reference for an inline kind on an unreserved trigger: `:shortcode:` recognizes on the bare `:` trigger, renders as an atomic glyph widget through `buildWidget` + `mintWidgetShell`, and carries the `{ deleteGranularity: 'atomic', onEdge: 'step-over' }` edge policy so a caret-adjacent Backspace removes the whole `:name:` in one press and a plain arrow steps over it. It shares the `:` trigger with the directive text tier, because disjoint grammars coexist happily on one trigger: a table-lookup miss declines and falls through with the bytes untouched. The literal `:name:` bytes stay in the raw, so an uninstalled document round-trips as ordinary prose.
|
|
@@ -1400,7 +1411,7 @@ The scanner consults the rung ahead of the built-in `[` case, but only when `[^`
|
|
|
1400
1411
|
|
|
1401
1412
|
A rung on `!` is consulted ahead of the built-in `!` case, so it outranks the image grammar wherever its prefix matches. And the two grammars do overlap: an image whose alt text opens with `[` starts on `![[` as well, so `![[a.png]]` carrying a parenthesized destination after it is a built-in image with the alt text `[a.png]`, not an embed. Deciding that overlap is your recognizer's job. Decline it (return `null`) and the built-in image reads the bytes unchanged. **Getting it wrong fails silently.** An ungated `![[` recognizer swallows the image with no throw and no dev-warn, and since the raw bytes are untouched the document still round-trips cleanly, so no round-trip check and no conformance cell in your own suite will ever see it. The first report comes from a reader whose picture stopped rendering.
|
|
1402
1413
|
|
|
1403
|
-
**Bound the decline, not just the claim.** Your recognizer is consulted at every occurrence of its trigger, so a decline that searches to the end of the block costs one block scan per trigger, which goes quadratic on a large paragraph, and the trigger is often ordinary prose (`$HOME $PATH …` for `$`). Stop at the first character your grammar can't contain, the way the emoji recognizer stops at the first non-shortcode byte. Where the grammar has no such character, index the candidate positions once per block with `createScanIndex` (hand it your position collector, get back a "first candidate at or after this offset" lookup), the way the bundled math and footnote
|
|
1414
|
+
**Bound the decline, not just the claim.** Your recognizer is consulted at every occurrence of its trigger, so a decline that searches to the end of the block costs one block scan per trigger, which goes quadratic on a large paragraph, and the trigger is often ordinary prose (`$HOME $PATH …` for `$`). Stop at the first character your grammar can't contain, the way the emoji recognizer stops at the first non-shortcode byte. Where the grammar has no such character, index the candidate positions once per block with `createScanIndex` (hand it your position collector, get back a "first candidate at or after this offset" lookup), the way the bundled math recognizer indexes every `$` and the footnote one its closers:
|
|
1404
1415
|
|
|
1405
1416
|
```ts
|
|
1406
1417
|
const dollarAt = createScanIndex((raw) => {
|
|
@@ -1444,17 +1455,19 @@ Three edges the snippet above is shaped by, and each one bites if you drop it:
|
|
|
1444
1455
|
|
|
1445
1456
|
**Errors in a component widget are half yours.** A **synchronous mount-time throw** is caught, so the widget falls back to its raw source and an `error` event fires, but the component mounts as its own effect root and nothing catches its post-mount runtime errors. Render a legible error for bad input instead of throwing (the KaTeX widget shows an inline message). A render engine's stylesheet is likewise yours: import it in the module that owns the renderer, so no route can forget it.
|
|
1446
1457
|
|
|
1447
|
-
**The inline tier isn't the block surface in miniature.** An inline kind gets recognition, rendering, atomic caret addressing at its edges, and an editing policy on its widget registration. The policy
|
|
1458
|
+
**The inline tier isn't the block surface in miniature.** An inline kind gets recognition, rendering, atomic caret addressing at its edges, and an editing policy on its widget registration. The policy's fields, all optional:
|
|
1448
1459
|
|
|
1449
|
-
| Field | What it decides
|
|
1450
|
-
| ----------------------- |
|
|
1451
|
-
| `revealSource` | Open the source (the `$…$` bytes) for editing on caret entry; inline math's model
|
|
1452
|
-
| `
|
|
1453
|
-
| `
|
|
1454
|
-
| `
|
|
1455
|
-
| `
|
|
1460
|
+
| Field | What it decides |
|
|
1461
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
1462
|
+
| `revealSource` | Open the source (the `$…$` bytes) for editing on caret entry; inline math's model |
|
|
1463
|
+
| `revealContentSpan` | Where the editable content sits inside the source (`$x$` answers `{ start: 1, end: 2 }`), so a caret entering the source stays between the delimiters; absent, it keeps the leading edge |
|
|
1464
|
+
| `revealOffsetAtPoint` | Which source offset a press on the rendered widget names, so a click seats the caret where it landed; inline math walks its KaTeX glyphs for this, and `null` falls back to the content span's end |
|
|
1465
|
+
| `onSelectedKey` | A handler for keys while the widget is selected; image resize rides it |
|
|
1466
|
+
| `onEdge` | `'select' \| 'step-over'`: an edge press selects the whole widget, or steps transparently over it; `'step-over'` also makes a press on the widget seat the caret at the edge it landed by, where `'select'` leaves the island its own click; and Up or Down onto a block holding only a step-over widget seats the caret beside it, one press in and one out |
|
|
1467
|
+
| `deleteGranularity` | `'atomic' \| 'select-then-delete'`: one press deletes the whole widget, or the first press selects and the second deletes |
|
|
1468
|
+
| `claimsActivationClick` | Your component handles the activation click itself, so the surface's reveal stands down for it; the footnote jump's model |
|
|
1456
1469
|
|
|
1457
|
-
Both edge fields are live today: the built-in decoded-entity widget (`©` → ©) ships `{ deleteGranularity: 'atomic', onEdge: 'step-over' }`, so a caret-adjacent Backspace removes it whole and a plain arrow walks the caret across it like a character, the caret-edge dispatch reading both off the widget registration. The inline tier gets **no keymap, no minted commands, and no per-node metadata**: `InlineNode` has no metadata field, so unlike a block kind it stores nothing at all on the node.
|
|
1470
|
+
Both edge fields are live today: the built-in decoded-entity widget (`©` → ©) ships `{ deleteGranularity: 'atomic', onEdge: 'step-over' }`, so a caret-adjacent Backspace removes it whole and a plain arrow walks the caret across it like a character, the caret-edge dispatch and the click seat reading both off the widget registration. The inline tier gets **no keymap, no minted commands, and no per-node metadata**: `InlineNode` has no metadata field, so unlike a block kind it stores nothing at all on the node.
|
|
1458
1471
|
|
|
1459
1472
|
## Decorations
|
|
1460
1473
|
|
|
@@ -1606,6 +1619,25 @@ registerGlobalCommand('mine.bold', handler, { chord: 'Mod+B' }); // fine: fires
|
|
|
1606
1619
|
|
|
1607
1620
|
Chord strings follow the consumer guide's chord model: fixed-order `Mod` / `Alt` / `Shift` plus the key's own value. Shifted-symbol chords aren't modeled, so bind plain digits and letters.
|
|
1608
1621
|
|
|
1622
|
+
## Block context actions
|
|
1623
|
+
|
|
1624
|
+
**`registerBlockContextActions(kind, provider)`**
|
|
1625
|
+
|
|
1626
|
+
The right-click menu on a block of `kind` (a code block, a table, a plugin's own block) lists what its providers return, ahead of the editor's own rows (copy, replace with the clipboard, remove). Register from `setup`. The provider is consulted on every open, so it reads the block as it is then; several may stack on one kind, and `EVERY_KIND` (`'*'`) registers for every kind. Prose is the page's background: a paragraph or heading keeps the browser's own menu and consults no provider.
|
|
1627
|
+
|
|
1628
|
+
```ts
|
|
1629
|
+
registerBlockContextActions(conspiracy, (node) => [
|
|
1630
|
+
{
|
|
1631
|
+
id: 'conspiracy.debunk',
|
|
1632
|
+
label: 'Mark debunked',
|
|
1633
|
+
icon: 'check',
|
|
1634
|
+
run: (ctx) => ctx.replaceRaw(node.raw.replace(/^:::conspiracy/, ':::debunked'))
|
|
1635
|
+
}
|
|
1636
|
+
]);
|
|
1637
|
+
```
|
|
1638
|
+
|
|
1639
|
+
`run` receives a `BlockActionContext`: the node, its path, `deleteBlock()`, and `replaceRaw(raw)`, which rewrites the block's bytes wholesale and reparses them, the road the default replace row takes. Each is one undo entry. `icon` names a glyph the editor's menus already draw (the same set the code rail and the table menu use); a row without one shows none. `danger` paints the row in the error colour, for an action that is not one undo away.
|
|
1640
|
+
|
|
1609
1641
|
## Paste transforms
|
|
1610
1642
|
|
|
1611
1643
|
`registerPasteTransform` records a **content-keyed, pre-parse** rewrite of pasted plain text. Each transform is a `{ name, transform(text) }` unit: `transform` returns a replacement string, or `null` to decline ("not mine"). Transforms run at every paste site before the clipboard text is parsed, in **install order**, each one seeing the previous transform's output, so a plugin keys off the _content_ it recognizes rather than the block it lands in. The name is unique (register-once; a duplicate throws, naming the owning plugin) and scopes the transform for attribution.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voithos-labs/aragonite",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.10.4",
|
|
4
|
+
"description": "cute svelte-based markdown editor lib",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
7
7
|
"gfm",
|
|
@@ -188,7 +188,7 @@
|
|
|
188
188
|
},
|
|
189
189
|
"peerDependencies": {
|
|
190
190
|
"katex": "^0.17.0 || ^0.18.0",
|
|
191
|
-
"mermaid": "^11.16.0",
|
|
191
|
+
"mermaid": "^11.16.0 || ^12.0.0",
|
|
192
192
|
"svelte": "^5.29.0"
|
|
193
193
|
},
|
|
194
194
|
"peerDependenciesMeta": {
|
|
@@ -202,6 +202,8 @@
|
|
|
202
202
|
"devDependencies": {
|
|
203
203
|
"@axe-core/playwright": "^4.13.0",
|
|
204
204
|
"@eslint/js": "^10.0.1",
|
|
205
|
+
"@fontsource/inter": "^5.3.0",
|
|
206
|
+
"@fontsource/jetbrains-mono": "^5.3.0",
|
|
205
207
|
"@playwright/test": "^1.62.0",
|
|
206
208
|
"@sveltejs/adapter-static": "^3.0.0",
|
|
207
209
|
"@sveltejs/kit": "^2.70.3",
|
|
@@ -209,7 +211,7 @@
|
|
|
209
211
|
"@sveltejs/vite-plugin-svelte": "^7.3.0",
|
|
210
212
|
"@types/commonmark": "^0.27.10",
|
|
211
213
|
"@types/node": "^26.2.0",
|
|
212
|
-
"@vitest/browser": "^
|
|
214
|
+
"@vitest/browser": "^5.0.0",
|
|
213
215
|
"commonmark": "0.31.2",
|
|
214
216
|
"eslint": "^10.9.0",
|
|
215
217
|
"eslint-plugin-svelte": "^3.23.0",
|
|
@@ -217,7 +219,7 @@
|
|
|
217
219
|
"globals": "^17.11.0",
|
|
218
220
|
"jsdom": "^30.0.0",
|
|
219
221
|
"katex": "^0.18.4",
|
|
220
|
-
"mermaid": "^
|
|
222
|
+
"mermaid": "^12.0.0",
|
|
221
223
|
"prettier": "^3.9.6",
|
|
222
224
|
"prettier-plugin-svelte": "^4.1.1",
|
|
223
225
|
"svelte": "^5.56.10",
|
|
@@ -226,8 +228,8 @@
|
|
|
226
228
|
"typescript": "^6.0.3",
|
|
227
229
|
"typescript-eslint": "^8.67.0",
|
|
228
230
|
"vite": "^8.2.2",
|
|
229
|
-
"vitest": "^
|
|
230
|
-
"wrangler": "4.
|
|
231
|
+
"vitest": "^5.0.0",
|
|
232
|
+
"wrangler": "4.131.1"
|
|
231
233
|
},
|
|
232
234
|
"overrides": {
|
|
233
235
|
"cookie": "^0.7.2"
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
import { tick } from 'svelte';
|
|
3
|
-
import { CODE_LANGUAGE_FIELD, codeLanguageLabel } from '../../../a11y-strings';
|
|
4
|
-
|
|
5
|
-
// The fence-info door for the modes that paint no fence. The draft lives here and only a
|
|
6
|
-
// commit reaches the tree.
|
|
7
|
-
let {
|
|
8
|
-
info,
|
|
9
|
-
editable,
|
|
10
|
-
onCommit,
|
|
11
|
-
onCancel
|
|
12
|
-
}: {
|
|
13
|
-
/** The opener's full info string; the button shows its first token. */
|
|
14
|
-
info: string;
|
|
15
|
-
/** False in reading mode, which writes no bytes — the chip is then a label. */
|
|
16
|
-
editable: boolean;
|
|
17
|
-
/** Only Enter calls this, and it owns the caret's landing afterwards. */
|
|
18
|
-
onCommit: (info: string) => void;
|
|
19
|
-
/** Nothing written. Escape asks for the caret back; a blur must not yank it from
|
|
20
|
-
* wherever the user just clicked, so it does not. */
|
|
21
|
-
onCancel: (returnCaret: boolean) => void;
|
|
22
|
-
} = $props();
|
|
23
|
-
|
|
24
|
-
const language = $derived(info.split(/\s+/)[0] || 'text');
|
|
25
|
-
|
|
26
|
-
let editing = $state(false);
|
|
27
|
-
let draft = $state('');
|
|
28
|
-
let inputEl: HTMLInputElement | undefined = $state();
|
|
29
|
-
|
|
30
|
-
function open(): void {
|
|
31
|
-
if (!editable) return;
|
|
32
|
-
draft = info;
|
|
33
|
-
editing = true;
|
|
34
|
-
void tick().then(() => {
|
|
35
|
-
inputEl?.focus();
|
|
36
|
-
inputEl?.select();
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function onFieldKeyDown(e: KeyboardEvent): void {
|
|
41
|
-
// The field owns its keys while open: no Escape of this state may reach whatever else
|
|
42
|
-
// listens for one. A composing Enter/Escape is the IME's, never the field's.
|
|
43
|
-
e.stopPropagation();
|
|
44
|
-
if (e.isComposing) return;
|
|
45
|
-
if (e.key === 'Enter') {
|
|
46
|
-
e.preventDefault();
|
|
47
|
-
const committed = draft;
|
|
48
|
-
editing = false;
|
|
49
|
-
onCommit(committed);
|
|
50
|
-
} else if (e.key === 'Escape') {
|
|
51
|
-
e.preventDefault();
|
|
52
|
-
editing = false;
|
|
53
|
-
onCancel(true);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function onFieldBlur(): void {
|
|
58
|
-
if (!editing) return;
|
|
59
|
-
editing = false;
|
|
60
|
-
onCancel(false);
|
|
61
|
-
}
|
|
62
|
-
</script>
|
|
63
|
-
|
|
64
|
-
<span class="code-lang-chip">
|
|
65
|
-
{#if editing}
|
|
66
|
-
<input
|
|
67
|
-
bind:this={inputEl}
|
|
68
|
-
bind:value={draft}
|
|
69
|
-
type="text"
|
|
70
|
-
spellcheck="false"
|
|
71
|
-
aria-label={CODE_LANGUAGE_FIELD}
|
|
72
|
-
onkeydown={onFieldKeyDown}
|
|
73
|
-
onfocusout={onFieldBlur}
|
|
74
|
-
/>
|
|
75
|
-
{:else}
|
|
76
|
-
<button type="button" aria-label={codeLanguageLabel(language)} onclick={open}>{language}</button
|
|
77
|
-
>
|
|
78
|
-
{/if}
|
|
79
|
-
</span>
|
|
80
|
-
|
|
81
|
-
<style>
|
|
82
|
-
/* Positioned against the block host, whose box the code box fills, and out of the code
|
|
83
|
-
box's own scroller so a horizontal scroll leaves it where it is. */
|
|
84
|
-
.code-lang-chip {
|
|
85
|
-
position: absolute;
|
|
86
|
-
top: 5px;
|
|
87
|
-
right: 5px;
|
|
88
|
-
z-index: 1;
|
|
89
|
-
display: flex;
|
|
90
|
-
font-family: var(--font-editor, ui-monospace, monospace);
|
|
91
|
-
font-size: 0.75em;
|
|
92
|
-
line-height: 1;
|
|
93
|
-
opacity: 0;
|
|
94
|
-
pointer-events: none;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* Transient, like the drag handle: the pointer over the block or the caret inside it, plus
|
|
98
|
-
the open field, which outlives both. Child and sibling combinators, so an outer container's
|
|
99
|
-
hover never reveals a nested block's chip. */
|
|
100
|
-
:global(.block-host:hover) > .code-lang-chip,
|
|
101
|
-
:global(.code-block:focus) ~ .code-lang-chip,
|
|
102
|
-
.code-lang-chip:focus-within {
|
|
103
|
-
opacity: 1;
|
|
104
|
-
pointer-events: auto;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
button,
|
|
108
|
-
input {
|
|
109
|
-
padding: 2px 6px;
|
|
110
|
-
border: 1px solid var(--color-ui-muted, #a4a4a4);
|
|
111
|
-
border-radius: var(--radius-ui, 3px);
|
|
112
|
-
background: var(--color-bg-elevated, #2a2a2a);
|
|
113
|
-
color: var(--color-accent, #567b67);
|
|
114
|
-
font: inherit;
|
|
115
|
-
cursor: pointer;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
button:hover {
|
|
119
|
-
background: var(--color-ui-faint, rgba(255, 255, 255, 0.07));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
input {
|
|
123
|
-
width: 9ch;
|
|
124
|
-
color: var(--color-text-secondary, #eee);
|
|
125
|
-
cursor: text;
|
|
126
|
-
}
|
|
127
|
-
</style>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
type $$ComponentProps = {
|
|
2
|
-
/** The opener's full info string; the button shows its first token. */
|
|
3
|
-
info: string;
|
|
4
|
-
/** False in reading mode, which writes no bytes — the chip is then a label. */
|
|
5
|
-
editable: boolean;
|
|
6
|
-
/** Only Enter calls this, and it owns the caret's landing afterwards. */
|
|
7
|
-
onCommit: (info: string) => void;
|
|
8
|
-
/** Nothing written. Escape asks for the caret back; a blur must not yank it from
|
|
9
|
-
* wherever the user just clicked, so it does not. */
|
|
10
|
-
onCancel: (returnCaret: boolean) => void;
|
|
11
|
-
};
|
|
12
|
-
declare const CodeLanguageChip: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
-
type CodeLanguageChip = ReturnType<typeof CodeLanguageChip>;
|
|
14
|
-
export default CodeLanguageChip;
|