@voithos-labs/aragonite 0.10.1 → 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.
Files changed (256) hide show
  1. package/README.md +12 -22
  2. package/THIRD-PARTY-NOTICES.md +25 -0
  3. package/dist/a11y-strings.d.ts +18 -0
  4. package/dist/a11y-strings.js +18 -0
  5. package/dist/action-contracts.d.ts +7 -1
  6. package/dist/ambient/ambient-dom.js +5 -1
  7. package/dist/block-component.d.ts +14 -0
  8. package/dist/components/BlockDragHandle.svelte +35 -24
  9. package/dist/components/BlockHost.svelte +16 -9
  10. package/dist/components/Editor.svelte +383 -199
  11. package/dist/components/Editor.svelte.d.ts +1 -1
  12. package/dist/components/SelectionOverlay.svelte +17 -3
  13. package/dist/components/TailInsert.svelte +107 -0
  14. package/dist/components/TailInsert.svelte.d.ts +17 -0
  15. package/dist/components/block-content-selector.d.ts +6 -2
  16. package/dist/components/block-content-selector.js +6 -2
  17. package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
  18. package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
  19. package/dist/components/blocks/code/CodeBlock.svelte +211 -30
  20. package/dist/components/blocks/code/CodeBlockRail.svelte +686 -0
  21. package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
  22. package/dist/components/blocks/code/code-bootstrap.js +4 -0
  23. package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
  24. package/dist/components/blocks/code/code-context-actions.js +24 -0
  25. package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
  26. package/dist/components/blocks/code/code-fence-exit.js +26 -0
  27. package/dist/components/blocks/code/code-languages.d.ts +6 -0
  28. package/dist/components/blocks/code/code-languages.js +11 -0
  29. package/dist/components/blocks/code/code-renderer.js +11 -0
  30. package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
  31. package/dist/components/blocks/editable-leaf.d.ts +37 -6
  32. package/dist/components/blocks/editable-leaf.js +243 -29
  33. package/dist/components/blocks/editable-surface.d.ts +9 -0
  34. package/dist/components/blocks/editable-surface.js +22 -3
  35. package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
  36. package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
  37. package/dist/components/blocks/list/task-checkbox.js +11 -2
  38. package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
  39. package/dist/components/blocks/surface-wiring.svelte.js +8 -1
  40. package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
  41. package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
  42. package/dist/components/blocks/table/TableBlock.svelte +205 -186
  43. package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
  44. package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
  45. package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
  46. package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
  47. package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
  48. package/dist/components/blocks/table/cell-clipboard.js +34 -1
  49. package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
  50. package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
  51. package/dist/components/blocks/table/table-cell-paste.js +2 -1
  52. package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
  53. package/dist/components/blocks/table/table-menu-model.js +36 -17
  54. package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
  55. package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
  56. package/dist/components/blocks/text/delimiter-autopair.js +216 -0
  57. package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
  58. package/dist/components/blocks/text/edge-policy-dispatch.js +56 -2
  59. package/dist/components/blocks/text/live-selection-edit.js +27 -0
  60. package/dist/components/blocks/text/text-keydown.d.ts +7 -1
  61. package/dist/components/blocks/text/text-keydown.js +11 -1
  62. package/dist/components/blocks/text/text-render.d.ts +1 -1
  63. package/dist/components/blocks/text/text-render.js +3 -1
  64. package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
  65. package/dist/components/blocks/text/widget-interaction.js +144 -22
  66. package/dist/components/drag-handle.d.ts +35 -0
  67. package/dist/components/drag-handle.js +126 -0
  68. package/dist/components/editor-root-focus.d.ts +19 -0
  69. package/dist/components/editor-root-focus.js +67 -0
  70. package/dist/components/editor-root-geometry.d.ts +39 -0
  71. package/dist/components/editor-root-geometry.js +91 -0
  72. package/dist/components/editor-root-keydown.d.ts +1 -1
  73. package/dist/components/editor-root-keydown.js +12 -3
  74. package/dist/components/editor-root-listeners.d.ts +10 -6
  75. package/dist/components/editor-root-listeners.js +23 -22
  76. package/dist/components/editor-root-mode-flip.d.ts +36 -0
  77. package/dist/components/editor-root-mode-flip.js +92 -0
  78. package/dist/components/image/ImageOverlayHost.svelte +14 -6
  79. package/dist/components/image/ImageProperties.svelte +512 -70
  80. package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
  81. package/dist/components/image/ImageResizeHandles.svelte +59 -34
  82. package/dist/components/image/image-crop.d.ts +39 -0
  83. package/dist/components/image/image-crop.js +74 -0
  84. package/dist/components/image/image-edit-commit.d.ts +1 -0
  85. package/dist/components/image/image-edit-commit.js +21 -5
  86. package/dist/components/image/image-source-bytes.js +10 -3
  87. package/dist/components/image/image-widget-editing.js +1 -0
  88. package/dist/components/image/widget-dom.js +5 -1
  89. package/dist/components/link-card/link-card-commit.js +1 -1
  90. package/dist/components/lrd-map-gate.js +1 -1
  91. package/dist/components/menu/BlockMenu.svelte +315 -0
  92. package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
  93. package/dist/components/menu/MenuIcon.svelte +153 -0
  94. package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
  95. package/dist/components/menu/clipboard-actions.d.ts +12 -0
  96. package/dist/components/menu/clipboard-actions.js +42 -0
  97. package/dist/components/menu/default-context-actions.d.ts +15 -0
  98. package/dist/components/menu/default-context-actions.js +76 -0
  99. package/dist/components/menu/flyout-placement.d.ts +6 -0
  100. package/dist/components/menu/flyout-placement.js +25 -0
  101. package/dist/core/inline/format-toggle.d.ts +12 -4
  102. package/dist/core/inline/format-toggle.js +94 -40
  103. package/dist/core/inline/image-dimensions.d.ts +3 -0
  104. package/dist/core/inline/image-dimensions.js +46 -10
  105. package/dist/core/inline/inline-widgets.d.ts +11 -0
  106. package/dist/core/inline/scan/brackets.js +1 -0
  107. package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
  108. package/dist/core/inline/scan/plugin-syntax.js +15 -1
  109. package/dist/core/inline-render.d.ts +6 -0
  110. package/dist/core/inline-render.js +32 -0
  111. package/dist/core/nodes.d.ts +14 -0
  112. package/dist/cursor/edge-affinity.js +2 -1
  113. package/dist/cursor/overlay-remeasure.js +8 -0
  114. package/dist/cursor/reveal-source.js +7 -2
  115. package/dist/cursor/widget-offset.d.ts +6 -0
  116. package/dist/cursor/widget-offset.js +61 -4
  117. package/dist/debug/interaction-trace.d.ts +4 -0
  118. package/dist/debug/interaction-trace.js +15 -0
  119. package/dist/decorations/decoration-state.svelte.js +1 -1
  120. package/dist/editor-actions/ancestry-folds.d.ts +2 -2
  121. package/dist/editor-actions/ancestry-folds.js +1 -1
  122. package/dist/editor-actions/block-edit-scope.js +1 -1
  123. package/dist/editor-actions/commit/text-batch.d.ts +3 -2
  124. package/dist/editor-actions/commit/text-batch.js +1 -1
  125. package/dist/editor-actions/commit/undo-controller.js +4 -2
  126. package/dist/editor-actions/container-edit.js +2 -1
  127. package/dist/editor-actions/enter-completion.d.ts +2 -0
  128. package/dist/editor-actions/enter-completion.js +23 -2
  129. package/dist/editor-actions/inline-range-commit.js +1 -1
  130. package/dist/editor-actions/reorder-action.js +19 -10
  131. package/dist/editor-actions/reorder-drag.js +29 -1
  132. package/dist/editor-actions/replacement-focus.d.ts +1 -1
  133. package/dist/editor-actions/replacement-focus.js +1 -1
  134. package/dist/editor-actions/search-replace.js +1 -1
  135. package/dist/editor-actions/table-context.d.ts +4 -1
  136. package/dist/editor-actions/table-context.js +57 -1
  137. package/dist/editor-events.d.ts +3 -0
  138. package/dist/editor-keys.d.ts +33 -0
  139. package/dist/editor-props.d.ts +24 -13
  140. package/dist/index.d.ts +1 -1
  141. package/dist/plugin.d.ts +6 -0
  142. package/dist/plugin.js +11 -0
  143. package/dist/plugins/latex/BlockMath.svelte +264 -29
  144. package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
  145. package/dist/plugins/latex/index.d.ts +2 -1
  146. package/dist/plugins/latex/latex-kind.js +36 -3
  147. package/dist/plugins/latex/math-completion.js +4 -1
  148. package/dist/plugins/latex/math-layout.d.ts +15 -0
  149. package/dist/plugins/latex/math-layout.js +13 -0
  150. package/dist/plugins/latex/math-source.d.ts +19 -0
  151. package/dist/plugins/latex/math-source.js +97 -0
  152. package/dist/plugins/latex/register.d.ts +11 -2
  153. package/dist/plugins/latex/register.js +3 -1
  154. package/dist/plugins/latex/renderer.d.ts +3 -3
  155. package/dist/plugins/latex/renderer.js +13 -6
  156. package/dist/plugins/parrot/ParrotBlock.svelte +1 -1
  157. package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
  158. package/dist/reactivity/list-windowing.svelte.js +49 -24
  159. package/dist/schema/block-completions.d.ts +8 -0
  160. package/dist/schema/block-completions.js +11 -0
  161. package/dist/schema/context-actions.d.ts +31 -0
  162. package/dist/schema/context-actions.js +23 -0
  163. package/dist/schema/fenced-code-raw.js +31 -1
  164. package/dist/schema/operations.d.ts +8 -1
  165. package/dist/schema/reserved-chords.js +25 -4
  166. package/dist/schema/table-cell-raw.d.ts +1 -1
  167. package/dist/schema/table-cell-raw.js +1 -1
  168. package/dist/selection/block-hit-test.js +3 -2
  169. package/dist/selection/char-endpoint-snap.js +1 -1
  170. package/dist/selection/clipboard-text.js +6 -1
  171. package/dist/selection/covered-block.d.ts +10 -0
  172. package/dist/selection/covered-block.js +24 -0
  173. package/dist/selection/cross-block/dispatch.d.ts +3 -0
  174. package/dist/selection/cross-block/dispatch.js +13 -1
  175. package/dist/selection/cross-block/format-range.d.ts +1 -1
  176. package/dist/selection/cross-block/format-range.js +4 -15
  177. package/dist/selection/cross-block/format-toggle.js +1 -1
  178. package/dist/selection/cross-block/keydown.js +1 -1
  179. package/dist/selection/cross-block/ops.js +1 -1
  180. package/dist/selection/cross-block/paste.js +32 -30
  181. package/dist/selection/cross-block/type-replace.d.ts +3 -2
  182. package/dist/selection/cross-block/type-replace.js +62 -13
  183. package/dist/selection/dead-space-caret.d.ts +10 -0
  184. package/dist/selection/dead-space-caret.js +47 -1
  185. package/dist/selection/double-click-trim.d.ts +17 -0
  186. package/dist/selection/double-click-trim.js +57 -0
  187. package/dist/selection/drag-pointer.d.ts +7 -2
  188. package/dist/selection/drag-pointer.js +61 -2
  189. package/dist/selection/gap-caret.js +1 -1
  190. package/dist/selection/keyboard-extend.js +1 -1
  191. package/dist/selection/path-lookup.js +1 -1
  192. package/dist/selection/range-delete-ceremony.js +4 -2
  193. package/dist/selection/range-delete-chrome.js +2 -1
  194. package/dist/selection/range-delete-table-coverage.js +2 -1
  195. package/dist/selection/range-delete-table.js +3 -2
  196. package/dist/selection/range-delete.js +30 -2
  197. package/dist/selection/selection-restore.js +1 -1
  198. package/dist/selection/selection-state.svelte.d.ts +6 -0
  199. package/dist/selection/selection-state.svelte.js +36 -1
  200. package/dist/selection/table-endpoint-snap.js +1 -1
  201. package/dist/selection/table-rect-extend.js +1 -1
  202. package/dist/styles/editor-theme.css +57 -28
  203. package/dist/styles/editor.css +217 -18
  204. package/dist/testing/container-conformance.js +2 -2
  205. package/dist/testing/inline-conformance.js +2 -1
  206. package/dist/tree-operations/blockquote.js +1 -1
  207. package/dist/tree-operations/chain-rebuild.d.ts +63 -0
  208. package/dist/tree-operations/chain-rebuild.js +142 -0
  209. package/dist/tree-operations/children.d.ts +1 -1
  210. package/dist/tree-operations/children.js +1 -1
  211. package/dist/tree-operations/cleanup.js +1 -1
  212. package/dist/tree-operations/content-write.d.ts +50 -0
  213. package/dist/tree-operations/content-write.js +263 -0
  214. package/dist/tree-operations/index.d.ts +8 -3
  215. package/dist/tree-operations/index.js +6 -2
  216. package/dist/tree-operations/list/exit-replacement.js +1 -1
  217. package/dist/tree-operations/list/unwrap-merge.js +3 -3
  218. package/dist/tree-operations/node-ops.d.ts +17 -234
  219. package/dist/tree-operations/node-ops.js +47 -1113
  220. package/dist/tree-operations/node-primitives.d.ts +75 -0
  221. package/dist/tree-operations/node-primitives.js +117 -0
  222. package/dist/tree-operations/paste/apply.js +1 -1
  223. package/dist/tree-operations/paste/body-write.d.ts +1 -1
  224. package/dist/tree-operations/paste/body-write.js +2 -2
  225. package/dist/tree-operations/paste/container-match.js +4 -2
  226. package/dist/tree-operations/paste/dispatch.js +2 -1
  227. package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
  228. package/dist/tree-operations/paste/focus-target.d.ts +1 -1
  229. package/dist/tree-operations/paste/list-absorb.js +1 -1
  230. package/dist/tree-operations/paste/list-break-out.js +1 -1
  231. package/dist/tree-operations/paste/parent-scope.js +1 -1
  232. package/dist/tree-operations/paste/paste-replacement.js +1 -1
  233. package/dist/tree-operations/paste/replace-block-at-parent.js +1 -1
  234. package/dist/tree-operations/path-mutate.d.ts +1 -1
  235. package/dist/tree-operations/path-mutate.js +2 -1
  236. package/dist/tree-operations/reorder-unit.js +1 -1
  237. package/dist/tree-operations/reorder.d.ts +5 -2
  238. package/dist/tree-operations/reorder.js +55 -2
  239. package/dist/tree-operations/settle.d.ts +105 -0
  240. package/dist/tree-operations/settle.js +660 -0
  241. package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
  242. package/dist/tree-operations/table-grid-clipboard.js +90 -0
  243. package/dist/tree-operations/unshare.d.ts +15 -77
  244. package/dist/tree-operations/unshare.js +15 -166
  245. package/docs/guide/consumer-guide.md +132 -99
  246. package/docs/guide/plugin-api.md +31 -3
  247. package/docs/guide/plugin-guide.md +31 -6
  248. package/package.json +4 -2
  249. package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
  250. package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
  251. package/dist/components/blocks/table/TableGrip.svelte +0 -91
  252. package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
  253. package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
  254. package/dist/components/blocks/table/table-drop-target.js +0 -16
  255. package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
  256. package/dist/components/blocks/table/table-reorder-drag.js +0 -97
@@ -50,9 +50,12 @@ The editor owns the caret, the tree, and the undo stack. You own load, save, and
50
50
  import '@voithos-labs/aragonite/styles/editor-theme.css';
51
51
 
52
52
  let editor;
53
+ const source = '# Hello\n';
53
54
  </script>
54
55
 
55
- <Editor bind:this={editor} source={'# Hello\n'} theme="dark" />
56
+ <div class="aragonite-editor-theme" data-editor-theme="light">
57
+ <Editor bind:this={editor} {source} theme="light" />
58
+ </div>
56
59
  <button onclick={() => save(editor.getSource())}>Save</button>
57
60
  ```
58
61
 
@@ -60,7 +63,7 @@ A few things in the above example snippet are decently important; you might want
60
63
 
61
64
  1. **`source` seeds the document at mount**, and re-seeds it if the prop later changes. It's not a two way bound: the editor never writes back into it, so the document you read is always `getSource()`.
62
65
  2. **`bind:this` is how you talk to a mounted editor.** For example, you might want to use important read functions like `getSource()` and `getSelection()`, or important write functions like `setSelection()` and `runCommand()`. [The instance surface](#the-instance-surface) covers all of it.
63
- 3. **Theming is CSS custom properties.** [Theming](#theming) has the variables and how to customize yours.
66
+ 3. **The editor paints no background of its own.** It inherits your page, so its mode has to match the page it lands on, and it says `light` twice because there are two things to match: the wrapper carries the built-in look (font, colors) for everything inside it, and the `theme` prop keys the editor's own surfaces. A fresh app's page is white, hence `light`; on a dark page write `dark` in both spots, or write nothing, dark being the default. Skip the wrapper if your app already declares the tokens; [Theming](#theming) has the two tiers and how to customize yours.
64
67
 
65
68
  Two more that aren't in the snippet but bite early: plugin registration is process-global and happens once at mount, but each editor activates exactly the plugins its own `plugins` prop lists ([Plugins](#plugins)); and `editor.__test.*` is internal and will move, so don't build on it.
66
69
 
@@ -94,7 +97,7 @@ Everything supported is exported from `@voithos-labs/aragonite`. Before 1.0 the
94
97
  | Group | What you get |
95
98
  | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
96
99
  | **Component** | `Editor`, plus `EditorProps` and `EditorInstance` (the prop shape and the `bind:this` surface) |
97
- | **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 |
98
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 }`) |
99
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) |
100
103
  | **Commands** | `TOOLBAR_COMMANDS`, the command ids a formatting toolbar calls through `runCommand` |
@@ -108,25 +111,27 @@ Everything supported is exported from `@voithos-labs/aragonite`. Before 1.0 the
108
111
 
109
112
  ## Props
110
113
 
111
- | Prop | What it does |
112
- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
113
- | `source` | Seeds the document at mount, and re-seeds it whenever the prop changes; never two-way bound |
114
- | `theme` | Theme name, reflected to `data-editor-theme` on the editor root: `'dark'` (default), `'light'`, or a name of your own (see [Theming](#theming)) |
115
- | `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)) |
116
- | `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)) |
117
- | `keybindings` | Rebind or disable the editor's shortcuts, per instance (see [Rebinding chords](#rebinding-chords)) |
118
- | `resolveImageUrl` | Rewrite a raw image URL before it reaches `img.src` (to resolve a relative path, say) |
119
- | `resolveLinkUrl` | Rewrite a raw link destination at render time |
120
- | `imageLoadPolicy` | `'auto'` (load images) or `'placeholder'` (defer loading) |
121
- | `onLinkActivate` | Handle an activated link (Ctrl/Cmd+click while editing, plain click in reading mode) instead of the default `window.open` |
122
- | `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)) |
123
- | `header` | Your own UI above the first block, rendered inside the editor's scroll container (see [The header slot](#the-header-slot)) |
124
- | `scrollMode` | `'self'` (default: the editor scrolls itself) or `'host'` (an ancestor of yours scrolls it; see [Host scroll mode](#host-scroll-mode)) |
125
- | `blockDragHandles` | Opt into the pointer affordances: the block drag handle and the table's row and column grips, revealed on hover and shown outright on touch (default off); keyboard reorder (Alt+Arrow) and the cell menu need no opt-in |
126
- | `searchBar` | The built-in find/replace bar and its Mod+F / Mod+H shortcuts (default on) |
127
- | `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)) |
128
-
129
- **Set once at mount:** `resolveImageUrl`, `resolveLinkUrl`, `imageLoadPolicy`, `onLinkActivate`, `onPasteImage`, `blockDragHandles`, `scrollMode`, and `plugins`. Set them at mount and leave them; a swap later isn't guaranteed to reach blocks that are already built.
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.
130
135
 
131
136
  **Read live:** `theme`, `searchBar`, `searchBarAnchor`, `presentationMode`, and `keybindings` may change after mount, and `header` re-renders like any other Svelte snippet.
132
137
 
@@ -154,7 +159,7 @@ And what you can write:
154
159
  | `setSelection(snapshot)` | Puts a `getSelection()` snapshot back on the document (see [Restoring a selection](#restoring-a-selection)) |
155
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)) |
156
161
  | `insertMarkdown(md)` | Inserts Markdown at the caret, exactly as pasting it would (see [Inserting Markdown at the caret](#inserting-markdown-at-the-caret)) |
157
- | `runCommand(id)` | Runs an editor command by name, no keystroke involved (see [Toolbar commands](#toolbar-commands)) |
162
+ | `runCommand(id, arg?)` | Runs an editor command by name, no keystroke involved (see [Toolbar commands](#toolbar-commands)) |
158
163
 
159
164
  ### Reading the document and the selection
160
165
 
@@ -264,7 +269,7 @@ One call runs the whole paste route:
264
269
 
265
270
  ### Toolbar commands
266
271
 
267
- `runCommand(commandId: string): boolean`
272
+ `runCommand(commandId: string, arg?: unknown): boolean`
268
273
 
269
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.
270
275
 
@@ -279,8 +284,11 @@ editor.runCommand('nope'); // false, unknown id, nothing changed
279
284
  The ids you can pass:
280
285
 
281
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.
282
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.
283
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
+
284
292
  What the boolean means:
285
293
 
286
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()`.
@@ -331,15 +339,16 @@ const off = events.on('edit', (e) => console.log(e.op, e.path));
331
339
  off();
332
340
  ```
333
341
 
334
- Five channels:
342
+ Six channels:
335
343
 
336
- | Channel | Fires |
337
- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
338
- | `edit` | After every applied edit: a structural operation, a batch of typing (consecutive keystrokes flush as one), an undo or redo |
339
- | `selectionChange` | Whenever the selection changes; the payload is the snapshot, or `null` |
340
- | `error` | On a failure the editor contained rather than threw |
341
- | `presentationModeChange` | After a `presentationMode` prop change; the payload is the effective mode (never at mount) |
342
- | `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 |
343
352
 
344
353
  Events fire synchronously from wherever they happen, and **a handler must not edit the document**: reentrant edits aren't supported.
345
354
 
@@ -425,7 +434,7 @@ events.on('error', (err) => err);
425
434
 
426
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:
427
436
 
428
- - **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.
429
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.
430
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.
431
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.
@@ -439,7 +448,7 @@ Three more live-mode facts:
439
448
 
440
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.
441
450
 
442
- **The language chip.** Wherever a mode hides a fenced code block's fence, a small chip appears at the code box's top-right on hover or with the caret inside. It shows the block's language, and outside reading mode a click turns it into a field where Enter commits a new one as a single undoable edit. It's 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 chip.
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.
443
452
 
444
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.
445
454
 
@@ -471,6 +480,27 @@ Four props deal with URLs: `resolveImageUrl` and `resolveLinkUrl` rewrite a raw
471
480
 
472
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.
473
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
+
474
504
  ### Which URLs render
475
505
 
476
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.
@@ -534,17 +564,17 @@ import { mermaidPlugin } from '@voithos-labs/aragonite/plugins/mermaid';
534
564
  import { parrotPlugin } from '@voithos-labs/aragonite/plugins/parrot';
535
565
  ```
536
566
 
537
- | Plugin | What it teaches the editor |
538
- | ------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
539
- | `admonitionsPlugin()` | `:::name` directive callouts and native GitHub alerts (`> [!NOTE]` blockquotes) render as styled boxes, GitHub bytes untouched |
540
- | `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 |
541
- | `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 |
542
- | `footnotesPlugin()` | GFM footnotes: `[^label]: content` definitions render as an editable block, and `[^label]` references render as superscript numbers in first-reference order |
543
- | `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 |
544
- | `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 |
545
- | `latexPlugin({ renderer })` | 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) |
546
- | `mermaidPlugin({ renderer? })` | A ` ```mermaid ` fence renders as a diagram through an injected engine; without one, the fence renders statically (the source, styled) |
547
- | `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 |
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 |
548
578
 
549
579
  A few of them take options or need a word more.
550
580
 
@@ -583,6 +613,8 @@ The module owns its CSS. Two stylesheets ship under `styles/`:
583
613
 
584
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.
585
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
+
586
618
  ### Scope
587
619
 
588
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:
@@ -608,17 +640,17 @@ Three paths, by how much you want to change:
608
640
 
609
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.
610
642
 
611
- | Role | Token(s) |
612
- | ------------- | -------------------------------------------------------------------------- |
613
- | **Font** | `--font-editor`, `--editor-font-size` _(mode-independent; one value each)_ |
614
- | **Radius** | `--radius-ui` _(controls)_, `--radius-surface` _(overlays, popovers)_ |
615
- | **Surface** | `--color-surface` |
616
- | **Text** | `--color-text-secondary` _(body)_, `--color-text-primary` |
617
- | **Muted** | `--color-ui-muted`, `--color-ui-dulled` |
618
- | **Accent** | `--color-accent` |
619
- | **Selection** | `--color-selection` |
620
- | **Borders** | `--color-border` |
621
- | **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` |
622
654
 
623
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:
624
656
 
@@ -645,7 +677,7 @@ A live change is supported, and virtual rendering re-estimates the document at t
645
677
 
646
678
  Outside this contract sits the editor's own visual language: the syntax and code-token palettes, the marker colors, the selection, search, and reorder tints (derived from `--color-selection`, above), and the surfaces windowing paints where blocks aren't mounted yet. Those are dark-based or mode-independent; read `editor-theme.css` if you mean to retheme them.
647
679
 
648
- **Plugin fallbacks.** A plugin reading a token keeps an inline fallback (`var(--color-text-muted, #aaaaaa)`) so it renders with no host, and every fallback matches the token's dark base value in `editor-theme.css`, never the light one. Which scopes a fallback fires in follows the tier: an editor-owned token defaults on `.editor`, so its fallback only fires outside the editor, while a host-chrome token defaults behind the opt-in class alone, so in a host that skips the class the fallback fires inside `.editor` too.
680
+ **Plugin fallbacks.** A plugin reading a token keeps an inline fallback (`var(--color-text-muted, #aaaaaa)`) so it renders with no host, and every fallback matches the token's dark base value in `editor-theme.css`, never the light one. The one exception is the text color: it falls back to `currentColor`, so an editor with no host tokens and no wrapper inherits the page's own text instead of painting white on whatever the page is. Which scopes a fallback fires in follows the tier: an editor-owned token defaults on `.editor`, so its fallback only fires outside the editor, while a host-chrome token defaults behind the opt-in class alone, so in a host that skips the class the fallback fires inside `.editor` too.
649
681
 
650
682
  ## Keyboard shortcuts
651
683
 
@@ -655,47 +687,48 @@ Shifted symbols aren't modeled: `Shift+1` reaches the editor as whatever symbol
655
687
 
656
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.
657
689
 
658
- Tables also have pointer affordances the table has no row for: with `blockDragHandles` on, every row and column carries a grip, revealed on hover and shown outright on touch, that you can drag to reorder it or click for a row/column action menu. Right-clicking any cell opens that same menu (with cut/copy/paste) whether the grips are on or off, and Shift+F10 or the Context Menu key opens it from the keyboard.
659
-
660
- | Action | Chord |
661
- | ----------------------------------- | --------------------------------------------------- |
662
- | **Editing** | |
663
- | Bold (toggle strong) | `Mod+B` |
664
- | Italic (toggle emphasis) | `Mod+I` |
665
- | Strikethrough | `Mod+Shift+X` |
666
- | Inline code | `Mod+E` |
667
- | Edit a link's URL (live mode) | `Mod+K` (caret inside a link; opens the link card) |
668
- | Cycle heading level | `Mod+0`–`Mod+6` (0 clears, 1–6 set `#`–`######`) |
669
- | Split a block | `Enter` (in a code block, inserts a newline) |
670
- | Hard line break | `Shift+Enter` |
671
- | Merge into the block before / after | `Backspace` / `Delete` (at the block's start / end) |
672
- | Indent / outdent a list item | `Tab` / `Shift+Tab` |
673
- | Indent / dedent a code line | `Tab` / `Shift+Tab` |
674
- | Insert a tab in prose | `Tab` |
675
- | Undo | `Mod+Z` |
676
- | Redo | `Mod+Y` or `Mod+Shift+Z` |
677
- | **Block reorder** | |
678
- | Move block up / down | `Alt+↑` / `Alt+↓` |
679
- | **Find / replace** | |
680
- | Open find | `Mod+F` |
681
- | Open find + replace | `Mod+H` |
682
- | Next / previous match | `Enter` / `Shift+Enter` (in the find field) |
683
- | Close search | `Esc` |
684
- | **Tables** | |
685
- | Move between cells | `Tab` / `Shift+Tab`, arrow keys |
686
- | Next row (or add one) | `Enter` (from the last cell, appends a row) |
687
- | Insert row below / above | `Mod+Enter` / `Mod+Shift+Enter` |
688
- | Insert column right / left | `Alt+Shift+→` / `Alt+Shift+←` |
689
- | Delete row | `Mod+Shift+Backspace` |
690
- | Delete column | `Alt+Shift+Backspace` |
691
- | Move row up / down | `Alt+↑` / `Alt+↓` |
692
- | Move column left / right | `Alt+←` / `Alt+→` |
693
- | Move the whole table up / down | `Mod+Alt+↑` / `Mod+Alt+↓` |
694
- | Cycle column alignment | `Mod+Shift+A` |
695
- | Create a table | type a header row (`\| a \| b \|`), then `Enter` |
696
- | **Clipboard** | |
697
- | Copy / cut a focused block | `Mod+C` / `Mod+X` |
698
- | Copy / cut a selected image | `Mod+C` / `Mod+X` |
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` |
699
732
 
700
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.
701
734
 
@@ -790,7 +823,7 @@ By default the editor root is the scrollport (the box that scrolls): it owns its
790
823
  What your CSS has to provide:
791
824
 
792
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.
793
- - **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 `0.85rem` on the left. This only matters with `blockDragHandles` on; keyboard reorder (Alt+Arrow) works either way.
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.
794
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).
795
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.
796
829
 
@@ -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 `rewriteImage` |
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 |