@voithos-labs/aragonite 0.10.2 → 0.10.3
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 +18 -0
- package/dist/a11y-strings.js +18 -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 +35 -24
- package/dist/components/BlockHost.svelte +16 -9
- package/dist/components/Editor.svelte +381 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +17 -3
- 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 +686 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +4 -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 +11 -0
- 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 +243 -29
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
- 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/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 +56 -2
- package/dist/components/blocks/text/live-selection-edit.js +27 -0
- 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 +144 -22
- package/dist/components/drag-handle.d.ts +35 -0
- package/dist/components/drag-handle.js +126 -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 +10 -6
- package/dist/components/editor-root-listeners.js +23 -22
- 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 +59 -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/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 +11 -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-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/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/widget-offset.d.ts +6 -0
- package/dist/cursor/widget-offset.js +61 -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/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/inline-range-commit.js +1 -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 +21 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +11 -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 +36 -3
- 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 +97 -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/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +49 -24
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- 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 +25 -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 +1 -1
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +32 -30
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +62 -13
- package/dist/selection/dead-space-caret.d.ts +10 -0
- package/dist/selection/dead-space-caret.js +47 -1
- package/dist/selection/double-click-trim.d.ts +17 -0
- package/dist/selection/double-click-trim.js +57 -0
- package/dist/selection/drag-pointer.d.ts +7 -2
- package/dist/selection/drag-pointer.js +61 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.js +1 -1
- package/dist/selection/path-lookup.js +1 -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.js +30 -2
- 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 +57 -28
- package/dist/styles/editor.css +217 -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.js +1 -1
- 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 +126 -96
- package/docs/guide/plugin-api.md +31 -3
- package/docs/guide/plugin-guide.md +26 -3
- package/package.json +4 -2
- 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
|
@@ -97,7 +97,7 @@ Everything supported is exported from `@voithos-labs/aragonite`. Before 1.0 the
|
|
|
97
97
|
| Group | What you get |
|
|
98
98
|
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
99
|
| **Component** | `Editor`, plus `EditorProps` and `EditorInstance` (the prop shape and the `bind:this` surface) |
|
|
100
|
-
| **Policy types** | `ResolveImageUrl`, `ResolveLinkUrl`, `ImageLoadPolicy` for the URL and image props; `PastedImage` and `PasteImageHook` for the image-import hook
|
|
100
|
+
| **Policy types** | `ResolveImageUrl`, `ResolveLinkUrl`, `ImageLoadPolicy` for the URL and image props; `PastedImage` and `PasteImageHook` for the image-import hook; `CodeRunRequest`, `RunCodeHook`, `CodeMenuItem` and `CodeMenuItemsHook` for the code-block hooks |
|
|
101
101
|
| **Plugins** | `installPlugins` for a parse-only pipeline with no editor mounted; `EditorPlugin` (the unit a plugin exports) and `EditorPluginEntry` (a `plugins` array entry: a bare unit, or `{ plugin, options }`) |
|
|
102
102
|
| **Selection + keymap** | `EditorSelection` (what `getSelection()` returns) and `normalizeSelection`, which puts a selection's two endpoints in document order; `KeybindingOverride` and `CommandId` (what the `keybindings` prop takes) |
|
|
103
103
|
| **Commands** | `TOOLBAR_COMMANDS`, the command ids a formatting toolbar calls through `runCommand` |
|
|
@@ -111,25 +111,27 @@ Everything supported is exported from `@voithos-labs/aragonite`. Before 1.0 the
|
|
|
111
111
|
|
|
112
112
|
## Props
|
|
113
113
|
|
|
114
|
-
| Prop | What it does
|
|
115
|
-
| ------------------ |
|
|
116
|
-
| `source` | Seeds the document at mount, and re-seeds it whenever the prop changes; never two-way bound
|
|
117
|
-
| `theme` | Theme name, reflected to `data-editor-theme` on the editor root: `'dark'` (default), `'light'`, or a name of your own (see [Theming](#theming))
|
|
118
|
-
| `presentationMode` | How the document presents, from raw source to fully rendered: `'source'` (default), `'reading'`, `'preview-block'`, `'preview-inline'`, or `'live'` (see [Presentation modes](#presentation-modes))
|
|
119
|
-
| `plugins` | Plugin units installed once at mount, in array order, before the first parse; the array is also the set this editor activates (see [Plugins](#plugins))
|
|
120
|
-
| `keybindings` | Rebind or disable the editor's shortcuts, per instance (see [Rebinding chords](#rebinding-chords))
|
|
121
|
-
| `resolveImageUrl` | Rewrite a raw image URL before it reaches `img.src` (to resolve a relative path, say)
|
|
122
|
-
| `resolveLinkUrl` | Rewrite a raw link destination at render time
|
|
123
|
-
| `imageLoadPolicy` | `'auto'` (load images) or `'placeholder'` (defer loading)
|
|
124
|
-
| `onLinkActivate` | Handle an activated link (Ctrl/Cmd+click while editing, plain click in reading mode) instead of the default `window.open`
|
|
125
|
-
| `onPasteImage` | Import hook for a paste that carries image files: you store them, and return the Markdown that stands in (see [Image paste](#image-paste))
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
|
|
132
|
-
|
|
114
|
+
| Prop | What it does |
|
|
115
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
116
|
+
| `source` | Seeds the document at mount, and re-seeds it whenever the prop changes; never two-way bound |
|
|
117
|
+
| `theme` | Theme name, reflected to `data-editor-theme` on the editor root: `'dark'` (default), `'light'`, or a name of your own (see [Theming](#theming)) |
|
|
118
|
+
| `presentationMode` | How the document presents, from raw source to fully rendered: `'source'` (default), `'reading'`, `'preview-block'`, `'preview-inline'`, or `'live'` (see [Presentation modes](#presentation-modes)) |
|
|
119
|
+
| `plugins` | Plugin units installed once at mount, in array order, before the first parse; the array is also the set this editor activates (see [Plugins](#plugins)) |
|
|
120
|
+
| `keybindings` | Rebind or disable the editor's shortcuts, per instance (see [Rebinding chords](#rebinding-chords)) |
|
|
121
|
+
| `resolveImageUrl` | Rewrite a raw image URL before it reaches `img.src` (to resolve a relative path, say) |
|
|
122
|
+
| `resolveLinkUrl` | Rewrite a raw link destination at render time |
|
|
123
|
+
| `imageLoadPolicy` | `'auto'` (load images) or `'placeholder'` (defer loading) |
|
|
124
|
+
| `onLinkActivate` | Handle an activated link (Ctrl/Cmd+click while editing, plain click in reading mode) instead of the default `window.open` |
|
|
125
|
+
| `onPasteImage` | Import hook for a paste that carries image files: you store them, and return the Markdown that stands in (see [Image paste](#image-paste)) |
|
|
126
|
+
| `onRunCode` | Execution hook for code blocks: installing it is what puts a run button on every code block's rail, and the editor runs nothing itself (see [Running a code block](#running-a-code-block)) |
|
|
127
|
+
| `codeMenuItems` | Overflow-menu hook for code blocks, consulted each time a block's menu opens so its items can read live state; absent, or answering nothing, renders no menu (see [Running a code block](#running-a-code-block)) |
|
|
128
|
+
| `header` | Your own UI above the first block, rendered inside the editor's scroll container (see [The header slot](#the-header-slot)) |
|
|
129
|
+
| `scrollMode` | `'self'` (default: the editor scrolls itself) or `'host'` (an ancestor of yours scrolls it; see [Host scroll mode](#host-scroll-mode)) |
|
|
130
|
+
| `blockDragHandles` | The block drag handle, revealed on hover and shown outright on touch (default on; reading mode hides it). Only object blocks carry one — code, tables, equations, diagrams, pictures, list items, dividers, cards — never prose. `false` removes them, except on a picture; keyboard reorder (Alt+Arrow) and the cell menu need no opt-in |
|
|
131
|
+
| `searchBar` | The built-in find/replace bar and its Mod+F / Mod+H shortcuts (default on) |
|
|
132
|
+
| `searchBarAnchor` | An element to render that same bar into, instead of inside the editor root (see [Where the find bar lives](#where-the-find-bar-lives)) |
|
|
133
|
+
|
|
134
|
+
**Set once at mount:** `resolveImageUrl`, `resolveLinkUrl`, `imageLoadPolicy`, `onLinkActivate`, `onPasteImage`, `onRunCode`, `codeMenuItems`, `blockDragHandles`, `scrollMode`, and `plugins`. Set them at mount and leave them; a swap later isn't guaranteed to reach blocks that are already built.
|
|
133
135
|
|
|
134
136
|
**Read live:** `theme`, `searchBar`, `searchBarAnchor`, `presentationMode`, and `keybindings` may change after mount, and `header` re-renders like any other Svelte snippet.
|
|
135
137
|
|
|
@@ -157,7 +159,7 @@ And what you can write:
|
|
|
157
159
|
| `setSelection(snapshot)` | Puts a `getSelection()` snapshot back on the document (see [Restoring a selection](#restoring-a-selection)) |
|
|
158
160
|
| `placeCaretAtPoint(x, y)` | Lands the caret at a viewport point, exactly as a click there would (see [Placing the caret at a point](#placing-the-caret-at-a-point)) |
|
|
159
161
|
| `insertMarkdown(md)` | Inserts Markdown at the caret, exactly as pasting it would (see [Inserting Markdown at the caret](#inserting-markdown-at-the-caret)) |
|
|
160
|
-
| `runCommand(id)`
|
|
162
|
+
| `runCommand(id, arg?)` | Runs an editor command by name, no keystroke involved (see [Toolbar commands](#toolbar-commands)) |
|
|
161
163
|
|
|
162
164
|
### Reading the document and the selection
|
|
163
165
|
|
|
@@ -267,7 +269,7 @@ One call runs the whole paste route:
|
|
|
267
269
|
|
|
268
270
|
### Toolbar commands
|
|
269
271
|
|
|
270
|
-
`runCommand(commandId: string): boolean`
|
|
272
|
+
`runCommand(commandId: string, arg?: unknown): boolean`
|
|
271
273
|
|
|
272
274
|
Runs an editor command by name at the focused block, no keystroke involved. It's what a formatting button calls: the button means "toggle bold", not "press Ctrl+B", so a user who rebinds the shortcut moves it without silently rewiring your button. The command behaves exactly as it would from the keyboard: same edit, one undo entry, caret and selection left where the keystroke would leave them.
|
|
273
275
|
|
|
@@ -282,8 +284,11 @@ editor.runCommand('nope'); // false, unknown id, nothing changed
|
|
|
282
284
|
The ids you can pass:
|
|
283
285
|
|
|
284
286
|
- **`TOOLBAR_COMMANDS`** (exported from the package) has what a selection toolbar needs: `toggleStrong`, `toggleEmphasis`, `toggleStrikethrough`, `toggleCode`, and `editLink`. The rest of the built-in commands stay internal for now.
|
|
287
|
+
- **`heading.cycle`, with a level.** The arm behind `Mod+0` to `Mod+6`: `runCommand('heading.cycle', 2)` re-marks the focused prose block as a level-2 heading and `0` makes it a paragraph, which is what a heading picker calls.
|
|
285
288
|
- **A plugin's global command name.** `registerGlobalCommand` registers it (see the [plugin guide](plugin-guide.md)), and it resolves ahead of the focused block, so you can fire a plugin's editor-wide action without a keystroke. A plugin's per-block command stays keyboard-only.
|
|
286
289
|
|
|
290
|
+
`arg` is the argument a keymap binding would bake in (`{ chord: 'Mod+2', command: 'heading.cycle', arg: 2 }`), handed to the command as it is; a command that takes none ignores it.
|
|
291
|
+
|
|
287
292
|
What the boolean means:
|
|
288
293
|
|
|
289
294
|
- **`true` means the editor took the command, not that the edit has landed.** A toggle inside a construct whose markers a preview mode has revealed (see [Presentation modes](#presentation-modes)) settles that reveal first, so read the outcome on the `edit` channel rather than polling `getSource()`.
|
|
@@ -334,15 +339,16 @@ const off = events.on('edit', (e) => console.log(e.op, e.path));
|
|
|
334
339
|
off();
|
|
335
340
|
```
|
|
336
341
|
|
|
337
|
-
|
|
342
|
+
Six channels:
|
|
338
343
|
|
|
339
|
-
| Channel | Fires
|
|
340
|
-
| ------------------------ |
|
|
341
|
-
| `edit` | After every applied edit: a structural operation, a batch of typing (consecutive keystrokes flush as one), an undo or redo
|
|
342
|
-
| `selectionChange` | Whenever the selection changes; the payload is the snapshot, or `null`
|
|
343
|
-
| `error` | On a failure the editor contained rather than threw
|
|
344
|
-
| `presentationModeChange` | After a `presentationMode` prop change; the payload is the effective mode (never at mount)
|
|
345
|
-
| `themeChange` | After a `theme` prop change; the payload is the theme name (never at mount)
|
|
344
|
+
| Channel | Fires |
|
|
345
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
346
|
+
| `edit` | After every applied edit: a structural operation, a batch of typing (consecutive keystrokes flush as one), an undo or redo |
|
|
347
|
+
| `selectionChange` | Whenever the selection changes; the payload is the snapshot, or `null` |
|
|
348
|
+
| `error` | On a failure the editor contained rather than threw |
|
|
349
|
+
| `presentationModeChange` | After a `presentationMode` prop change; the payload is the effective mode (never at mount) |
|
|
350
|
+
| `themeChange` | After a `theme` prop change; the payload is the theme name (never at mount) |
|
|
351
|
+
| `menuChange` | `true` when an editor-owned menu (right-click, insert `+`) opens and `false` when it closes; hide selection chrome meanwhile |
|
|
346
352
|
|
|
347
353
|
Events fire synchronously from wherever they happen, and **a handler must not edit the document**: reentrant edits aren't supported.
|
|
348
354
|
|
|
@@ -428,7 +434,7 @@ events.on('error', (err) => err);
|
|
|
428
434
|
|
|
429
435
|
Hiding every marker means one screen position can mean two raw offsets wherever a construct's delimiters sit. Live answers that with five rules, each applied in one place so it holds for every gesture:
|
|
430
436
|
|
|
431
|
-
- **A character typed at a hidden edge follows how the caret got there.** Arriving from outside a construct types outside it; walking into it types inside. A construct that never grows at its edges (a link) always takes the outside.
|
|
437
|
+
- **A character typed at a hidden edge follows how the caret got there.** Arriving from outside a construct types outside it; walking into it types inside. A construct that never grows at its edges (a link) always takes the outside. A delimiter you type closes itself (`*`, `**`, `` ` ``, `~~`, and a plugin's `$`), typing the closer over its twin steps past it, and the next character after a closer you typed lands outside the construct.
|
|
432
438
|
- **A caret seated at an extreme lands outside.** `Home`, `End`, and collapsing a selection put the caret past the delimiters, not between them. A seat isn't a step, so the direction of the key that produced it doesn't decide the side.
|
|
433
439
|
- **`Enter` inside a construct closes it and reopens it.** Splitting `**bold**` down the middle leaves two balanced constructs rather than one stranded delimiter in each half, and a split link carries its destination into both halves. Where no balanced rewrite shows what the screen showed (a code span whose reopened backticks would collide with its own, say), the split falls back to a plain byte cut.
|
|
434
440
|
- **A join cleans up after itself.** `Backspace`, `Delete`, a range delete, typing over a selection, and a paste all go through the same code: a delimiter run the cut orphaned goes with the cut instead of appearing on screen, and a closer meeting an opener around nothing is dropped. Every candidate cleanup is checked against what the two sides showed, and the byte-literal join stands when it can't be.
|
|
@@ -442,7 +448,7 @@ Three more live-mode facts:
|
|
|
442
448
|
|
|
443
449
|
Bytes only change where a rule above says so; a gesture that strands nothing writes exactly what source mode writes. One exception: `Backspace` at the very start of a `# ` with no heading text drops the construct, where source mode does nothing.
|
|
444
450
|
|
|
445
|
-
**The
|
|
451
|
+
**The code rail.** Wherever a mode hides a fenced code block's fence, a small rail appears at the code box's top-right on hover or with the caret inside: the block's language (outside reading mode a click opens a picker over every registered language, and Enter or a pick commits as a single undoable edit), a copy button, and whatever your app installed through `onRunCode` and `codeMenuItems` (see [Running a code block](#running-a-code-block)). A fence that has just taken the caret with no language opens the picker by itself, unless the caret arrowed in from a neighbouring block. The rail is the only way to reach an info string (the text after the opening fence that names the language) in those modes; source mode shows the fence itself and gets no rail.
|
|
446
452
|
|
|
447
453
|
The effective mode is reflected as `data-presentation` on the editor root (absent in source mode, so default-mode DOM is unchanged) and announced on the `presentationModeChange` channel.
|
|
448
454
|
|
|
@@ -474,6 +480,27 @@ Four props deal with URLs: `resolveImageUrl` and `resolveLinkUrl` rewrite a raw
|
|
|
474
480
|
|
|
475
481
|
For the curious, where the Markdown lands when the user moves the caret during a slow upload: a paste inside one block freezes its anchor at paste time, so a caret moved mid-upload doesn't drag the insertion with it. A paste over a selection spanning blocks follows the live selection instead, because that route resolves its endpoints by path at insertion time, so a selection extended during the import is the one that gets replaced. The difference is deliberate; snapshotting the second case would mean fighting the code that owns delete-and-insert as one operation.
|
|
476
482
|
|
|
483
|
+
### Running a code block
|
|
484
|
+
|
|
485
|
+
The editor runs nothing. `onRunCode` is the hook that says your app can: installing it puts a run button on every code block's rail (the top-right controls a marker-hiding mode shows on hover or with the caret inside), and pressing it hands you the block, then everything after is yours: the engine, the result, and where the output goes.
|
|
486
|
+
|
|
487
|
+
```svelte
|
|
488
|
+
<Editor
|
|
489
|
+
{source}
|
|
490
|
+
onRunCode={({ code, info, path }) => {
|
|
491
|
+
// code: the fence body alone, never the fence lines; info: the whole info string
|
|
492
|
+
// ("py {1-3}"); path: child indices from the document root to the block.
|
|
493
|
+
runInMyKernel(code, info.split(/\s+/)[0]).then((out) => showOutputBeside(path, out));
|
|
494
|
+
}}
|
|
495
|
+
codeMenuItems={(request) => [
|
|
496
|
+
{ id: 'clear', label: 'Clear output', run: () => clearOutput(request.path) },
|
|
497
|
+
{ id: 'export', label: 'Export', run: () => exportCell(request), disabled: !canExport }
|
|
498
|
+
]}
|
|
499
|
+
/>
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
`codeMenuItems` is the same idea for the rail's overflow menu. It is consulted each time a menu opens, so an item can read live state (a `disabled` item renders dimmed and refuses activation), and a host answering nothing renders no menu at all: the editor has no app-level actions of its own to put there. Both hooks are set once at mount, like `onPasteImage`. Neither reaches the document: a run is not an edit, fires no `edit` event, and creates no undo entry. Writing a result back into the document is an `insertMarkdown` or a `source` rewrite of your own.
|
|
503
|
+
|
|
477
504
|
### Which URLs render
|
|
478
505
|
|
|
479
506
|
The scheme check runs at render time, on whatever `resolveImageUrl` / `resolveLinkUrl` returned. A URL outside the admitted set renders inert: the image never loads and its widget is marked blocked, a link becomes an unlinked span, and the Markdown bytes are untouched either way. That blocked state isn't `imageLoadPolicy: 'placeholder'`, which defers loading an image the policy allows.
|
|
@@ -537,17 +564,17 @@ import { mermaidPlugin } from '@voithos-labs/aragonite/plugins/mermaid';
|
|
|
537
564
|
import { parrotPlugin } from '@voithos-labs/aragonite/plugins/parrot';
|
|
538
565
|
```
|
|
539
566
|
|
|
540
|
-
| Plugin
|
|
541
|
-
|
|
|
542
|
-
| `admonitionsPlugin()`
|
|
543
|
-
| `detailsPlugin()`
|
|
544
|
-
| `tocPlugin()`
|
|
545
|
-
| `footnotesPlugin()`
|
|
546
|
-
| `emojiPlugin()`
|
|
547
|
-
| `highlightOccurrencesPlugin()`
|
|
548
|
-
| `latexPlugin({ renderer })`
|
|
549
|
-
| `mermaidPlugin({ renderer? })`
|
|
550
|
-
| `parrotPlugin()`
|
|
567
|
+
| Plugin | What it teaches the editor |
|
|
568
|
+
| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
569
|
+
| `admonitionsPlugin()` | `:::name` directive callouts and native GitHub alerts (`> [!NOTE]` blockquotes) render as styled boxes, GitHub bytes untouched |
|
|
570
|
+
| `detailsPlugin()` | A canonical `<details>` HTML block (`<details>` or `<details open>`, a `<summary>` line, a Markdown body, `</details>`) becomes an editable collapsible section whose summary is a real editable child; a non-canonical `<details …>` stays a plain HTML block |
|
|
571
|
+
| `tocPlugin()` | A `[[toc]]` line becomes a live table of contents: every heading in the document, indented by level, each entry navigating to its heading on click or on Enter from the keyboard |
|
|
572
|
+
| `footnotesPlugin()` | GFM footnotes: `[^label]: content` definitions render as an editable block, and `[^label]` references render as superscript numbers in first-reference order |
|
|
573
|
+
| `emojiPlugin()` | GitHub `:shortcode:` emoji: a bare `:name:` renders as a glyph while the literal `:name:` bytes stay in the source; without the plugin, `:name:` is ordinary prose |
|
|
574
|
+
| `highlightOccurrencesPlugin()` | Every other occurrence of the word under the caret is highlighted across the document's prose blocks once you stop typing, as a view-only decoration, never a byte change |
|
|
575
|
+
| `latexPlugin({ renderer, blockLayout? })` | All three GitHub math forms through one injected engine: inline `$…$`, block `$$…$$`, and the fenced ` ```math ` form; uninstalled, each stays its plain reading (prose, or a plain `math` code block). `blockLayout` (`'split'` default, `'stacked'`, `'source'`) is how a block opens for editing; an editor's `{ plugin, options: { blockLayout } }` entry overrides it per instance |
|
|
576
|
+
| `mermaidPlugin({ renderer? })` | A ` ```mermaid ` fence renders as a diagram through an injected engine; without one, the fence renders statically (the source, styled) |
|
|
577
|
+
| `parrotPlugin()` | A `%%parrot` line renders as an animated ASCII party parrot, with whatever follows the marker as its caption; uninstalled, the line is ordinary prose |
|
|
551
578
|
|
|
552
579
|
A few of them take options or need a word more.
|
|
553
580
|
|
|
@@ -586,6 +613,8 @@ The module owns its CSS. Two stylesheets ship under `styles/`:
|
|
|
586
613
|
|
|
587
614
|
A plugin's render engine may carry its own stylesheet (KaTeX's `katex.min.css`, say). That CSS is the plugin's to load, not the editor module's.
|
|
588
615
|
|
|
616
|
+
No font ships either. The `/` showcase and the harness load Inter and JetBrains Mono from `@fontsource/*` devDependencies to dress as the app the editor ships into; those packages never reach the published module, so the `--font-*` tokens resolve to whatever your page provides, and to their fallback stacks otherwise.
|
|
617
|
+
|
|
589
618
|
### Scope
|
|
590
619
|
|
|
591
620
|
Nothing is declared on `:root`; the module never puts custom properties into your global scope. The tokens come in two tiers, and the tier decides where you override:
|
|
@@ -611,17 +640,17 @@ Three paths, by how much you want to change:
|
|
|
611
640
|
|
|
612
641
|
The role table below is the stable **host-chrome contract**: the tokens the editor and its plugins read to blend into your app, named the way a host theme system names them. Declare them anywhere in your cascade, or take the defaults through the opt-in class.
|
|
613
642
|
|
|
614
|
-
| Role | Token(s)
|
|
615
|
-
| ------------- |
|
|
616
|
-
| **Font** | `--font-editor
|
|
617
|
-
| **Radius** | `--radius-ui` _(controls)_, `--radius-surface` _(overlays, popovers)_
|
|
618
|
-
| **Surface** | `--color-surface`
|
|
619
|
-
| **Text** | `--color-text-secondary` _(body)_, `--color-text-primary`
|
|
620
|
-
| **Muted** | `--color-ui-muted`, `--color-ui-dulled`
|
|
621
|
-
| **Accent** | `--color-accent`
|
|
622
|
-
| **Selection** | `--color-selection`
|
|
623
|
-
| **Borders** | `--color-border`
|
|
624
|
-
| **Error** | `--color-error`
|
|
643
|
+
| Role | Token(s) |
|
|
644
|
+
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
645
|
+
| **Font** | `--font-editor` _(the surface)_, `--font-code` _(what stays monospace whatever the surface is: code blocks, code spans, revealed source)_, `--font-ui` _(menus and popovers)_, `--editor-font-size` _(mode-independent; one value each)_ |
|
|
646
|
+
| **Radius** | `--radius-ui` _(controls)_, `--radius-surface` _(overlays, popovers)_ |
|
|
647
|
+
| **Surface** | `--color-bg` _(the page ground; menus and the image toolbar paint on it)_, `--color-surface` |
|
|
648
|
+
| **Text** | `--color-text-secondary` _(body)_, `--color-text-primary` |
|
|
649
|
+
| **Muted** | `--color-ui-muted`, `--color-ui-dulled` |
|
|
650
|
+
| **Accent** | `--color-accent` |
|
|
651
|
+
| **Selection** | `--color-selection` |
|
|
652
|
+
| **Borders** | `--color-border` |
|
|
653
|
+
| **Error** | `--color-error` |
|
|
625
654
|
|
|
626
655
|
The editor supplies these host-family surfaces itself, in both modes, because a host vocabulary rarely names them. Override them at `.editor`; a `:root` declaration would lose to the default:
|
|
627
656
|
|
|
@@ -658,47 +687,48 @@ Shifted symbols aren't modeled: `Shift+1` reaches the editor as whatever symbol
|
|
|
658
687
|
|
|
659
688
|
This table is for a reader. An app deriving an accelerator map should read `editor.reservedChords()` instead, since that set is composed from the live keymaps and covers chords claimed outside them (see [Which shortcuts the editor consumes](#which-shortcuts-the-editor-consumes)). The selection chords are one example: Shift+Arrow, `Mod+Shift+Home` / `Mod+Shift+End`, and the repeated `Mod+A` escalation go through the cross-block selection code rather than the keymap, so they aren't rebindable and aren't listed here.
|
|
660
689
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
| Action | Chord
|
|
664
|
-
| ----------------------------------- |
|
|
665
|
-
| **Editing** |
|
|
666
|
-
| Bold (toggle strong) | `Mod+B`
|
|
667
|
-
| Italic (toggle emphasis) | `Mod+I`
|
|
668
|
-
| Strikethrough | `Mod+Shift+X`
|
|
669
|
-
| Inline code | `Mod+E`
|
|
670
|
-
| Edit a link's URL (live mode) | `Mod+K` (caret inside a link; opens the link card)
|
|
671
|
-
| Cycle heading level | `Mod+0`–`Mod+6` (0 clears, 1–6 set `#`–`######`)
|
|
672
|
-
| Split a block | `Enter` (in a code block, inserts a newline)
|
|
673
|
-
|
|
|
674
|
-
|
|
|
675
|
-
|
|
|
676
|
-
| Indent /
|
|
677
|
-
|
|
|
678
|
-
|
|
|
679
|
-
|
|
|
680
|
-
|
|
|
681
|
-
|
|
|
682
|
-
|
|
|
683
|
-
|
|
|
684
|
-
| Open find
|
|
685
|
-
|
|
|
686
|
-
|
|
|
687
|
-
|
|
|
688
|
-
|
|
|
689
|
-
|
|
|
690
|
-
|
|
|
691
|
-
| Insert
|
|
692
|
-
|
|
|
693
|
-
| Delete
|
|
694
|
-
|
|
|
695
|
-
| Move
|
|
696
|
-
| Move
|
|
697
|
-
|
|
|
698
|
-
|
|
|
699
|
-
|
|
|
700
|
-
|
|
|
701
|
-
| Copy / cut a
|
|
690
|
+
Right-clicking any cell opens the table's action menu: cut/copy/paste, Row and Column flyouts (insert, move), the two deletes, and the column's alignment. Shift+F10 or the Context Menu key opens it from the keyboard. A table has no per-row or per-column grips — its one drag handle, in the editor's gutter, moves the whole table.
|
|
691
|
+
|
|
692
|
+
| Action | Chord |
|
|
693
|
+
| ----------------------------------- | ----------------------------------------------------------------------------- |
|
|
694
|
+
| **Editing** | |
|
|
695
|
+
| Bold (toggle strong) | `Mod+B` |
|
|
696
|
+
| Italic (toggle emphasis) | `Mod+I` |
|
|
697
|
+
| Strikethrough | `Mod+Shift+X` |
|
|
698
|
+
| Inline code | `Mod+E` |
|
|
699
|
+
| Edit a link's URL (live mode) | `Mod+K` (caret inside a link; opens the link card) |
|
|
700
|
+
| Cycle heading level | `Mod+0`–`Mod+6` (0 clears, 1–6 set `#`–`######`) |
|
|
701
|
+
| Split a block | `Enter` (in a code block, inserts a newline) |
|
|
702
|
+
| Leave a code block | `Enter` on its empty last line (typing the closing fence there does the same) |
|
|
703
|
+
| Hard line break | `Shift+Enter` |
|
|
704
|
+
| Merge into the block before / after | `Backspace` / `Delete` (at the block's start / end) |
|
|
705
|
+
| Indent / outdent a list item | `Tab` / `Shift+Tab` |
|
|
706
|
+
| Indent / dedent a code line | `Tab` / `Shift+Tab` |
|
|
707
|
+
| Insert a tab in prose | `Tab` |
|
|
708
|
+
| Undo | `Mod+Z` |
|
|
709
|
+
| Redo | `Mod+Y` or `Mod+Shift+Z` |
|
|
710
|
+
| **Block reorder** | |
|
|
711
|
+
| Move block up / down | `Alt+↑` / `Alt+↓` |
|
|
712
|
+
| **Find / replace** | |
|
|
713
|
+
| Open find | `Mod+F` |
|
|
714
|
+
| Open find + replace | `Mod+H` |
|
|
715
|
+
| Next / previous match | `Enter` / `Shift+Enter` (in the find field) |
|
|
716
|
+
| Close search | `Esc` |
|
|
717
|
+
| **Tables** | |
|
|
718
|
+
| Move between cells | `Tab` / `Shift+Tab`, arrow keys |
|
|
719
|
+
| Next row (or add one) | `Enter` (from the last cell, appends a row) |
|
|
720
|
+
| Insert row below / above | `Mod+Enter` / `Mod+Shift+Enter` |
|
|
721
|
+
| Insert column right / left | `Alt+Shift+→` / `Alt+Shift+←` |
|
|
722
|
+
| Delete row | `Mod+Shift+Backspace` |
|
|
723
|
+
| Delete column | `Alt+Shift+Backspace` |
|
|
724
|
+
| Move row up / down | `Alt+↑` / `Alt+↓` |
|
|
725
|
+
| Move column left / right | `Alt+←` / `Alt+→` |
|
|
726
|
+
| Move the whole table up / down | `Mod+Alt+↑` / `Mod+Alt+↓` |
|
|
727
|
+
| Cycle column alignment | `Mod+Shift+A` |
|
|
728
|
+
| Create a table | type a header row (`\| a \| b \|`), then `Enter` |
|
|
729
|
+
| **Clipboard** | |
|
|
730
|
+
| Copy / cut a focused block | `Mod+C` / `Mod+X` |
|
|
731
|
+
| Copy / cut a selected image | `Mod+C` / `Mod+X` |
|
|
702
732
|
|
|
703
733
|
**Typing a table into existence.** A table's header and delimiter lines have to be adjacent, which Enter alone could never produce, so a paragraph holding just a header row (`| a | b |`) is completed by `Enter` into a finished table (delimiter, one empty body row, caret in the first body cell) as one undoable step. It needs the leading pipe, so a paragraph that merely contains one (`ls | grep foo`) is left alone, and one undo restores the row you typed.
|
|
704
734
|
|
|
@@ -793,7 +823,7 @@ By default the editor root is the scrollport (the box that scrolls): it owns its
|
|
|
793
823
|
What your CSS has to provide:
|
|
794
824
|
|
|
795
825
|
- **Resolve the scroller before the editor's first use.** The editor finds the ancestor that scrolls it once, at first need. A shell that swaps its scroller in afterwards (a panel that expands, a wrapper replaced on a route transition) leaves the editor measuring against the wrong box. Settle the layout first, or remount the editor.
|
|
796
|
-
- **A clipping wrapper needs left padding.** Host mode drops the editor's own padding, and the drag handle sits in a gutter outside the block box. A wrapper with `overflow: hidden` and no padding clips the handle away entirely, so pointer drag-reorder silently disappears. Reserve at least `
|
|
826
|
+
- **A clipping wrapper needs left padding.** Host mode drops the editor's own padding, and the drag handle sits in a gutter outside the block box. A wrapper with `overflow: hidden` and no padding clips the handle away entirely, so pointer drag-reorder silently disappears. Reserve at least `1.5rem` on the left, which is what the editor's own padding gives it. Keyboard reorder (Alt+Arrow) works either way.
|
|
797
827
|
- **The reading column's side inset belongs to the editor, not an ancestor.** Host mode drops the editor's own padding, so the inset that narrows the text column is yours to add, and where you put it decides whether the margin beside the text is clickable. On the editor element or the block list inside it, the editor claims the whole gutter and a click there lands the caret on the nearest line. On any ancestor, that band is your shell's: the click never reaches a surface the editor can claim, and the margin beside every line goes dead while looking like part of the document. If the band genuinely is your chrome, answer the click yourself and hand the point to [`placeCaretAtPoint(x, y)`](#placing-the-caret-at-a-point).
|
|
798
828
|
- **A drag autoscrolls whatever actually scrolls.** That's the nearest ancestor you made scrollable, or the page's own viewport when nothing between the editor and the document scrolls. One box it will never scroll is a fixed-height `overflow: hidden` wrapper: a reader can't wheel one back, so a drag that scrolled it would strand content out of reach. A programmatic reveal does move such a box, deliberately: it can put the block on screen and leave it there.
|
|
799
829
|
|
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 |
|
|
@@ -118,7 +119,7 @@ _(pre-freeze / unstable)_ The recipe: [Typing a multi-line construct into existe
|
|
|
118
119
|
| Export | Role |
|
|
119
120
|
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
120
121
|
| `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
|
|
122
|
+
| `BlockCompleter` | The contract: `tryComplete(line)` claims with a result, or declines with null; `onType: true` also consults it as the line is typed |
|
|
122
123
|
| `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
124
|
|
|
124
125
|
### Registration probes
|
|
@@ -177,6 +178,29 @@ _(pre-freeze / unstable)_ The container factory's sibling for leaves; the full s
|
|
|
177
178
|
| `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
179
|
| `StickyColumnDirection` | Which vertical direction the caret is entering your block from, handed to `focusAtColumn` so the column carries across lines |
|
|
179
180
|
|
|
181
|
+
### Code-block languages
|
|
182
|
+
|
|
183
|
+
_(pre-freeze / unstable)_ The syntax-highlighting registry behind fenced code. The editor bootstraps a curated set — javascript, typescript, python, rust, go, bash, json, yaml, sql, html, css, java, c, cpp, ruby, markdown, diff, plus their aliases — because every grammar is static bundle weight for every consumer. A host needing more registers them itself.
|
|
184
|
+
|
|
185
|
+
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.
|
|
186
|
+
|
|
187
|
+
| Export | Role |
|
|
188
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
189
|
+
| `registerLanguage` | Add a grammar under a name, with optional aliases; idempotent, so a repeat call with the same name is a no-op |
|
|
190
|
+
| `listLanguages` | Every registered name and alias, sorted — what the code block's language picker offers |
|
|
191
|
+
| `highlightCode` | The code block's tokenizer: `(body, language)` to a text-preserving fragment of `code-tok-*` spans, for a plugin's own source surface |
|
|
192
|
+
| `LanguageGrammar` | The registry's read shape: the resolved name and its definition |
|
|
193
|
+
| `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) |
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { registerLanguage } from '@voithos-labs/aragonite/plugin';
|
|
197
|
+
import elixir from 'highlight.js/lib/languages/elixir';
|
|
198
|
+
|
|
199
|
+
registerLanguage('elixir', elixir, ['ex', 'exs']);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Aliases are offered as their own rows in the picker, so a user typing `ex` finds it without knowing it resolves to `elixir`.
|
|
203
|
+
|
|
180
204
|
### Inline authoring
|
|
181
205
|
|
|
182
206
|
_(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).
|
|
@@ -189,14 +213,14 @@ _(pre-freeze / unstable)_ Syntax inside a paragraph: recognize it at a trigger c
|
|
|
189
213
|
| `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
214
|
| `INLINE_PRIORITIES` | The inline ladder, lower consulted first: `prefixOverride` outranks a reserved trigger's built-in case, `plugin` is the bare-trigger default |
|
|
191
215
|
| `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 `
|
|
216
|
+
| `InlineSyntaxOptions` | The options bag: the multi-character `prefix`, the `priority`, `rewriteImage`, and `autoPair` |
|
|
193
217
|
| `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
218
|
| `registerInlineWidgetKind` | Render an inline kind as a live atomic widget: a Svelte `component` (recommended) or a hand-built `buildWidget`, never both |
|
|
195
219
|
| `mintWidgetShell` | Mint the marked, source-stamped span a `buildWidget` returns; its attributes are what the caret's position walk reads |
|
|
196
220
|
| `PluginInlineKind`, `InlineNode` | The inline kind type, and the node your recognizer builds |
|
|
197
221
|
| `InlineWidgetDescriptor` | The widget registration: the is-this-a-widget test, one render path, the editing policy |
|
|
198
222
|
| `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
|
|
223
|
+
| `InlineWidgetEditingPolicy`, `InlineWidgetEditingContext` | The policy (reveal source on caret entry, where the content sits inside the delimiters so a revealing click seats the caret there, delete granularity, edge behavior, a selected-key handler, whether the widget claims the activation click), and the context that handler receives |
|
|
200
224
|
| `isWidgetActivationClick` | Whether a click activates a widget: a Ctrl/Cmd chord while editing, a plain click in reading mode |
|
|
201
225
|
|
|
202
226
|
### Commands and keybindings
|
|
@@ -206,6 +230,10 @@ _(pre-freeze / unstable)_ Which tier dispatches what: [Block commands](plugin-gu
|
|
|
206
230
|
| Export | Role |
|
|
207
231
|
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
208
232
|
| `registerBlockCommand` | Mint a `(kind, name)` command and get back its id, for a keymap binding to target |
|
|
233
|
+
| `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 |
|
|
234
|
+
| `BlockContextAction` | One such action: id, label, optional glyph and danger flag, and `run(ctx)` |
|
|
235
|
+
| `BlockActionContext` | What `run` receives: the node, its path, `deleteBlock()` and `replaceRaw(raw)` |
|
|
236
|
+
| `BlockContextActionProvider` | The registered function: `(node, path) => BlockContextAction[]`, consulted on every open |
|
|
209
237
|
| `registerGlobalCommand` | Mint a process-wide command run against whichever editor dispatched it, optionally on a global chord; also returns its id |
|
|
210
238
|
| `CommandId` | A built-in command's id; a vocabulary your keymaps may bind too |
|
|
211
239
|
| `KeyBinding` | One keymap entry: a chord (fixed-order `Mod` / `Alt` / `Shift` plus the key), a command id, an optional baked argument |
|
|
@@ -1161,9 +1161,11 @@ leaf.getOptions(); // this editor's options for your plugin, typed unknown
|
|
|
1161
1161
|
|
|
1162
1162
|
**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
1163
|
|
|
1164
|
-
**One spread wires the source surface.** Write `<div {...leaf.surfaceProps}>` on your source contenteditable and the
|
|
1164
|
+
**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
1165
|
|
|
1166
|
-
That
|
|
1166
|
+
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.
|
|
1167
|
+
|
|
1168
|
+
**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 as it is revealed. Block math is the worked example.
|
|
1167
1169
|
|
|
1168
1170
|
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
1171
|
|
|
@@ -1188,7 +1190,7 @@ Editing past your own fence therefore re-splits the document instead of wedging
|
|
|
1188
1190
|
|
|
1189
1191
|
**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
1192
|
|
|
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`.
|
|
1193
|
+
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
1194
|
|
|
1193
1195
|
## Presentation modes
|
|
1194
1196
|
|
|
@@ -1381,6 +1383,8 @@ If your `revealSource` widget takes a click of its own, declare `claimsActivatio
|
|
|
1381
1383
|
|
|
1382
1384
|
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
1385
|
|
|
1386
|
+
**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 the recognizer rejects (a `$` followed by a digit 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.
|
|
1387
|
+
|
|
1384
1388
|
**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
1389
|
|
|
1386
1390
|
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.
|
|
@@ -1606,6 +1610,25 @@ registerGlobalCommand('mine.bold', handler, { chord: 'Mod+B' }); // fine: fires
|
|
|
1606
1610
|
|
|
1607
1611
|
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
1612
|
|
|
1613
|
+
## Block context actions
|
|
1614
|
+
|
|
1615
|
+
**`registerBlockContextActions(kind, provider)`**
|
|
1616
|
+
|
|
1617
|
+
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.
|
|
1618
|
+
|
|
1619
|
+
```ts
|
|
1620
|
+
registerBlockContextActions(conspiracy, (node) => [
|
|
1621
|
+
{
|
|
1622
|
+
id: 'conspiracy.debunk',
|
|
1623
|
+
label: 'Mark debunked',
|
|
1624
|
+
icon: 'check',
|
|
1625
|
+
run: (ctx) => ctx.replaceRaw(node.raw.replace(/^:::conspiracy/, ':::debunked'))
|
|
1626
|
+
}
|
|
1627
|
+
]);
|
|
1628
|
+
```
|
|
1629
|
+
|
|
1630
|
+
`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.
|
|
1631
|
+
|
|
1609
1632
|
## Paste transforms
|
|
1610
1633
|
|
|
1611
1634
|
`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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voithos-labs/aragonite",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"description": "Svelte 5 block editor for GFM Markdown with byte-for-byte lossless round-trip and a plugin platform",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"markdown",
|
|
@@ -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",
|
|
@@ -227,7 +229,7 @@
|
|
|
227
229
|
"typescript-eslint": "^8.67.0",
|
|
228
230
|
"vite": "^8.2.2",
|
|
229
231
|
"vitest": "^4.1.0",
|
|
230
|
-
"wrangler": "4.
|
|
232
|
+
"wrangler": "4.131.0"
|
|
231
233
|
},
|
|
232
234
|
"overrides": {
|
|
233
235
|
"cookie": "^0.7.2"
|