@voithos-labs/aragonite 0.10.4 → 0.10.7

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 (1021) hide show
  1. package/README.md +2 -2
  2. package/dist/a11y-strings.d.ts +11 -5
  3. package/dist/a11y-strings.js +60 -5
  4. package/dist/action-contracts.d.ts +242 -155
  5. package/dist/action-contracts.js +2 -2
  6. package/dist/active-editor.d.ts +3 -3
  7. package/dist/active-editor.js +8 -9
  8. package/dist/ambient/ambient-dom.d.ts +2 -5
  9. package/dist/ambient/ambient-dom.js +16 -62
  10. package/dist/assert.js +6 -5
  11. package/dist/block-component.d.ts +135 -109
  12. package/dist/block-component.js +30 -22
  13. package/dist/block-id.d.ts +6 -10
  14. package/dist/block-id.js +25 -14
  15. package/dist/bounded-memo.d.ts +5 -5
  16. package/dist/bounded-memo.js +6 -6
  17. package/dist/components/BlockDragHandle.svelte +12 -21
  18. package/dist/components/BlockHost.svelte +56 -134
  19. package/dist/components/BlockList.svelte +22 -19
  20. package/dist/components/DecorationOverlay.svelte +9 -10
  21. package/dist/components/DecorationOverlay.svelte.d.ts +2 -2
  22. package/dist/components/Editor.svelte +603 -1126
  23. package/dist/components/Editor.svelte.d.ts +9 -20
  24. package/dist/components/GapCaret.svelte +26 -59
  25. package/dist/components/SelectionOverlay.svelte +26 -51
  26. package/dist/components/SelectionOverlay.svelte.d.ts +5 -3
  27. package/dist/components/TailInsert.svelte +8 -55
  28. package/dist/components/TailInsert.svelte.d.ts +4 -6
  29. package/dist/components/block-content-selector.d.ts +11 -11
  30. package/dist/components/block-content-selector.js +11 -11
  31. package/dist/components/block-el-lookup.d.ts +6 -0
  32. package/dist/components/block-el-lookup.js +27 -0
  33. package/dist/components/blocks/BlockquoteBlock.svelte +3 -3
  34. package/dist/components/blocks/ThematicBreakBlock.svelte +22 -63
  35. package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +0 -2
  36. package/dist/components/blocks/code/CodeBlock.svelte +255 -276
  37. package/dist/components/blocks/code/CodeBlock.svelte.d.ts +2 -3
  38. package/dist/components/blocks/code/CodeBlockRail.svelte +72 -87
  39. package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +9 -4
  40. package/dist/components/blocks/code/code-beforeinput.d.ts +1 -1
  41. package/dist/components/blocks/code/code-bootstrap.d.ts +3 -5
  42. package/dist/components/blocks/code/code-bootstrap.js +29 -34
  43. package/dist/components/blocks/code/code-context-actions.js +5 -4
  44. package/dist/components/blocks/code/code-enter.js +2 -2
  45. package/dist/components/blocks/code/code-fence-boundary.d.ts +29 -46
  46. package/dist/components/blocks/code/code-fence-boundary.js +40 -66
  47. package/dist/components/blocks/code/code-fence-exit.d.ts +6 -11
  48. package/dist/components/blocks/code/code-fence-exit.js +24 -25
  49. package/dist/components/blocks/code/code-indent.d.ts +2 -2
  50. package/dist/components/blocks/code/code-indent.js +4 -7
  51. package/dist/components/blocks/code/code-languages.d.ts +16 -12
  52. package/dist/components/blocks/code/code-languages.js +57 -39
  53. package/dist/components/blocks/code/code-paste-surface.d.ts +3 -2
  54. package/dist/components/blocks/code/code-paste-surface.js +9 -18
  55. package/dist/components/blocks/code/code-renderer.d.ts +21 -5
  56. package/dist/components/blocks/code/code-renderer.js +90 -125
  57. package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +5 -8
  58. package/dist/components/blocks/directive/activate-directives.d.ts +4 -4
  59. package/dist/components/blocks/directive/activate-directives.js +18 -13
  60. package/dist/components/blocks/editable-leaf.d.ts +50 -74
  61. package/dist/components/blocks/editable-leaf.js +141 -203
  62. package/dist/components/blocks/editable-surface.d.ts +96 -100
  63. package/dist/components/blocks/editable-surface.js +101 -109
  64. package/dist/components/blocks/list/ListBlock.svelte +45 -90
  65. package/dist/components/blocks/list/ListItemBlock.svelte +93 -147
  66. package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +1 -0
  67. package/dist/components/blocks/list/task-checkbox.d.ts +5 -2
  68. package/dist/components/blocks/list/task-checkbox.js +12 -7
  69. package/dist/components/blocks/plain-text-backend.d.ts +4 -20
  70. package/dist/components/blocks/plain-text-backend.js +9 -43
  71. package/dist/components/blocks/surface-wiring.svelte.d.ts +8 -9
  72. package/dist/components/blocks/surface-wiring.svelte.js +15 -34
  73. package/dist/components/blocks/table/TableActionMenu.svelte +19 -18
  74. package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +3 -1
  75. package/dist/components/blocks/table/TableBlock.svelte +100 -165
  76. package/dist/components/blocks/table/TableBlock.svelte.d.ts +3 -3
  77. package/dist/components/blocks/table/TableCellBlock.svelte +307 -385
  78. package/dist/components/blocks/table/TableCellBlock.svelte.d.ts +1 -0
  79. package/dist/components/blocks/table/TableRowBlock.svelte +60 -104
  80. package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -1
  81. package/dist/components/blocks/table/cell-keydown-plan.d.ts +4 -9
  82. package/dist/components/blocks/table/cell-keydown-plan.js +8 -12
  83. package/dist/components/blocks/table/cell-pointer.d.ts +18 -23
  84. package/dist/components/blocks/table/cell-pointer.js +49 -71
  85. package/dist/components/blocks/table/cell-render.d.ts +24 -29
  86. package/dist/components/blocks/table/cell-render.js +29 -31
  87. package/dist/components/blocks/table/selected-cells.d.ts +7 -7
  88. package/dist/components/blocks/table/selected-cells.js +9 -13
  89. package/dist/components/blocks/table/table-caret-at-point.d.ts +4 -4
  90. package/dist/components/blocks/table/table-caret-at-point.js +5 -5
  91. package/dist/components/blocks/table/table-cell-paste.d.ts +5 -11
  92. package/dist/components/blocks/table/table-cell-paste.js +27 -43
  93. package/dist/components/blocks/table/table-drag-hit-test.js +2 -1
  94. package/dist/components/blocks/table/table-menu-model.d.ts +4 -8
  95. package/dist/components/blocks/table/table-menu-model.js +6 -10
  96. package/dist/components/blocks/table/table-navigation.js +2 -2
  97. package/dist/components/blocks/text/TextEditableBlock.svelte +392 -310
  98. package/dist/components/blocks/text/TextEditableBlock.svelte.d.ts +2 -1
  99. package/dist/components/blocks/text/auto-pair-record.d.ts +23 -0
  100. package/dist/components/blocks/text/auto-pair-record.js +51 -0
  101. package/dist/components/blocks/text/click-snap-guard.d.ts +8 -6
  102. package/dist/components/blocks/text/click-snap-guard.js +10 -7
  103. package/dist/components/blocks/text/composition-seat.d.ts +17 -14
  104. package/dist/components/blocks/text/composition-seat.js +11 -11
  105. package/dist/components/blocks/text/construct-edge-delete.d.ts +20 -20
  106. package/dist/components/blocks/text/construct-edge-delete.js +50 -65
  107. package/dist/components/blocks/text/construct-reveal.d.ts +16 -21
  108. package/dist/components/blocks/text/construct-reveal.js +13 -14
  109. package/dist/components/blocks/text/delimiter-autopair.d.ts +51 -34
  110. package/dist/components/blocks/text/delimiter-autopair.js +87 -103
  111. package/dist/components/blocks/text/edge-policy-dispatch.d.ts +44 -46
  112. package/dist/components/blocks/text/edge-policy-dispatch.js +150 -181
  113. package/dist/components/blocks/text/edge-seat.d.ts +17 -26
  114. package/dist/components/blocks/text/edge-seat.js +43 -70
  115. package/dist/components/blocks/text/link-at-point.d.ts +9 -9
  116. package/dist/components/blocks/text/link-at-point.js +12 -15
  117. package/dist/components/blocks/text/live-join-seam.d.ts +6 -9
  118. package/dist/components/blocks/text/live-join-seam.js +75 -112
  119. package/dist/components/blocks/text/live-selection-edit.d.ts +14 -43
  120. package/dist/components/blocks/text/live-selection-edit.js +32 -108
  121. package/dist/components/blocks/text/live-split-rebalance.d.ts +9 -13
  122. package/dist/components/blocks/text/live-split-rebalance.js +50 -67
  123. package/dist/components/blocks/text/marker-completion.d.ts +7 -12
  124. package/dist/components/blocks/text/marker-completion.js +17 -10
  125. package/dist/components/blocks/text/pending-mark-insert.d.ts +11 -18
  126. package/dist/components/blocks/text/pending-mark-insert.js +30 -49
  127. package/dist/components/blocks/text/screen-diff.d.ts +5 -15
  128. package/dist/components/blocks/text/screen-diff.js +5 -19
  129. package/dist/components/blocks/text/text-clipboard.d.ts +23 -32
  130. package/dist/components/blocks/text/text-clipboard.js +27 -32
  131. package/dist/components/blocks/text/text-keydown.d.ts +20 -30
  132. package/dist/components/blocks/text/text-keydown.js +54 -51
  133. package/dist/components/blocks/text/text-render.d.ts +25 -35
  134. package/dist/components/blocks/text/text-render.js +82 -61
  135. package/dist/components/blocks/text/widget-adjacency.d.ts +9 -9
  136. package/dist/components/blocks/text/widget-adjacency.js +17 -17
  137. package/dist/components/blocks/text/widget-interaction.d.ts +67 -43
  138. package/dist/components/blocks/text/widget-interaction.js +212 -199
  139. package/dist/components/blocks/widget-portal.d.ts +20 -29
  140. package/dist/components/blocks/widget-portal.js +16 -14
  141. package/dist/components/built-in-blocks.d.ts +3 -4
  142. package/dist/components/built-in-blocks.js +17 -18
  143. package/dist/components/drag-handle.d.ts +12 -35
  144. package/dist/components/drag-handle.js +22 -68
  145. package/dist/components/editor-built-ins.d.ts +1 -0
  146. package/dist/components/editor-built-ins.js +16 -0
  147. package/dist/components/editor-root-clipboard.d.ts +6 -6
  148. package/dist/components/editor-root-clipboard.js +16 -27
  149. package/dist/components/editor-root-document-swap.d.ts +49 -0
  150. package/dist/components/editor-root-document-swap.js +54 -0
  151. package/dist/components/editor-root-focus.d.ts +5 -4
  152. package/dist/components/editor-root-focus.js +51 -22
  153. package/dist/components/editor-root-focused-surface.d.ts +35 -0
  154. package/dist/components/editor-root-focused-surface.js +65 -0
  155. package/dist/components/editor-root-geometry.d.ts +15 -24
  156. package/dist/components/editor-root-geometry.js +27 -31
  157. package/dist/components/editor-root-gestures.d.ts +39 -0
  158. package/dist/components/editor-root-gestures.js +189 -0
  159. package/dist/components/editor-root-keydown.d.ts +10 -25
  160. package/dist/components/editor-root-keydown.js +19 -43
  161. package/dist/components/editor-root-listeners.d.ts +22 -24
  162. package/dist/components/editor-root-listeners.js +49 -24
  163. package/dist/components/editor-root-menus.d.ts +51 -0
  164. package/dist/components/editor-root-menus.js +147 -0
  165. package/dist/components/editor-root-mode-flip.d.ts +16 -11
  166. package/dist/components/editor-root-mode-flip.js +36 -34
  167. package/dist/components/editor-root-scroll-host.d.ts +22 -0
  168. package/dist/components/editor-root-scroll-host.js +34 -0
  169. package/dist/components/editor-root-test-surface.d.ts +48 -0
  170. package/dist/components/editor-root-test-surface.js +6 -0
  171. package/dist/components/image/ImageOverlayHost.svelte +44 -35
  172. package/dist/components/image/ImageOverlayHost.svelte.d.ts +5 -6
  173. package/dist/components/image/ImageProperties.svelte +37 -41
  174. package/dist/components/image/ImageProperties.svelte.d.ts +9 -4
  175. package/dist/components/image/ImageResizeHandles.svelte +14 -26
  176. package/dist/components/image/image-crop.d.ts +5 -11
  177. package/dist/components/image/image-crop.js +5 -11
  178. package/dist/components/image/image-edit-commit.d.ts +19 -16
  179. package/dist/components/image/image-edit-commit.js +74 -38
  180. package/dist/components/image/image-resize.d.ts +2 -5
  181. package/dist/components/image/image-resize.js +3 -6
  182. package/dist/components/image/image-widget-editing.d.ts +3 -3
  183. package/dist/components/image/image-widget-editing.js +9 -9
  184. package/dist/components/image/widget-dom.d.ts +2 -3
  185. package/dist/components/image/widget-dom.js +19 -24
  186. package/dist/components/image/widget-selection-state.svelte.d.ts +10 -11
  187. package/dist/components/image/widget-selection-state.svelte.js +26 -19
  188. package/dist/components/kind-cue.svelte.d.ts +21 -0
  189. package/dist/components/kind-cue.svelte.js +35 -0
  190. package/dist/components/link-card/LinkCard.svelte +115 -64
  191. package/dist/components/link-card/LinkCard.svelte.d.ts +7 -5
  192. package/dist/components/link-card/LinkCardHost.svelte +42 -33
  193. package/dist/components/link-card/LinkCardHost.svelte.d.ts +10 -8
  194. package/dist/components/link-card/link-card-commit.d.ts +16 -19
  195. package/dist/components/link-card/link-card-commit.js +21 -30
  196. package/dist/components/link-card/link-card-entry.d.ts +13 -23
  197. package/dist/components/link-card/link-card-entry.js +16 -24
  198. package/dist/components/link-card/link-card-state.svelte.d.ts +17 -21
  199. package/dist/components/link-card/link-card-state.svelte.js +2 -2
  200. package/dist/components/lrd-map-gate.d.ts +4 -11
  201. package/dist/components/lrd-map-gate.js +5 -11
  202. package/dist/components/menu/BlockMenu.svelte +20 -81
  203. package/dist/components/menu/BlockMenu.svelte.d.ts +11 -9
  204. package/dist/components/menu/InlineMenuHost.svelte +191 -0
  205. package/dist/components/menu/InlineMenuHost.svelte.d.ts +13 -0
  206. package/dist/components/menu/MenuIcon.svelte +3 -103
  207. package/dist/components/menu/MenuIcon.svelte.d.ts +2 -44
  208. package/dist/components/menu/SelectionToolbar.svelte +30 -35
  209. package/dist/components/menu/SelectionToolbar.svelte.d.ts +3 -5
  210. package/dist/components/menu/clipboard-actions.d.ts +3 -4
  211. package/dist/components/menu/clipboard-actions.js +3 -4
  212. package/dist/components/menu/default-context-actions.d.ts +4 -13
  213. package/dist/components/menu/default-context-actions.js +49 -67
  214. package/dist/components/menu/flyout-placement.d.ts +2 -2
  215. package/dist/components/menu/flyout-placement.js +2 -2
  216. package/dist/components/menu/menu-presence.svelte.d.ts +12 -0
  217. package/dist/components/menu/menu-presence.svelte.js +19 -0
  218. package/dist/components/paste-image-arm.d.ts +6 -11
  219. package/dist/components/paste-image-arm.js +11 -18
  220. package/dist/components/portal.d.ts +2 -5
  221. package/dist/components/portal.js +4 -7
  222. package/dist/core/directive/activate.d.ts +4 -5
  223. package/dist/core/directive/activate.js +8 -9
  224. package/dist/core/directive/container-opener.js +13 -12
  225. package/dist/core/directive/grammar.d.ts +2 -5
  226. package/dist/core/directive/grammar.js +10 -10
  227. package/dist/core/directive/kinds.d.ts +4 -7
  228. package/dist/core/directive/kinds.js +14 -16
  229. package/dist/core/directive/registry.d.ts +12 -9
  230. package/dist/core/directive/registry.js +19 -20
  231. package/dist/core/directive/text-recognizer.d.ts +3 -2
  232. package/dist/core/directive/text-recognizer.js +5 -9
  233. package/dist/core/escapable.d.ts +1 -5
  234. package/dist/core/escapable.js +1 -5
  235. package/dist/core/inline/backticks.d.ts +3 -4
  236. package/dist/core/inline/backticks.js +3 -4
  237. package/dist/core/inline/character-refs.js +2 -1
  238. package/dist/core/inline/destination-bytes.d.ts +6 -3
  239. package/dist/core/inline/destination-bytes.js +50 -8
  240. package/dist/core/inline/entity-widget.d.ts +4 -3
  241. package/dist/core/inline/entity-widget.js +6 -6
  242. package/dist/core/inline/format-toggle.d.ts +22 -28
  243. package/dist/core/inline/format-toggle.js +97 -129
  244. package/dist/core/inline/html-entities.d.ts +2 -3
  245. package/dist/core/inline/html-entities.js +2 -3
  246. package/dist/core/inline/html-tag-grammar.js +6 -2
  247. package/dist/core/inline/image-dimensions.d.ts +2 -0
  248. package/dist/core/inline/image-dimensions.js +19 -2
  249. package/dist/core/inline/image-source-bytes.d.ts +11 -0
  250. package/dist/core/inline/image-source-bytes.js +71 -0
  251. package/dist/core/inline/index.d.ts +28 -21
  252. package/dist/core/inline/index.js +56 -34
  253. package/dist/core/inline/inline-cache.d.ts +12 -15
  254. package/dist/core/inline/inline-cache.js +11 -14
  255. package/dist/core/inline/inline-widgets.d.ts +54 -75
  256. package/dist/core/inline/inline-widgets.js +54 -62
  257. package/dist/core/inline/link-destination.d.ts +31 -0
  258. package/dist/core/inline/link-destination.js +129 -0
  259. package/dist/core/inline/link-reference-resolver.d.ts +8 -6
  260. package/dist/core/inline/link-reference-resolver.js +10 -6
  261. package/dist/core/inline/link-source-bytes.d.ts +31 -0
  262. package/dist/{components/blocks/text → core/inline}/link-source-bytes.js +43 -54
  263. package/dist/core/inline/live-edit/read-back.d.ts +29 -0
  264. package/dist/core/inline/live-edit/read-back.js +50 -0
  265. package/dist/core/inline/picture.d.ts +10 -0
  266. package/dist/core/inline/picture.js +40 -0
  267. package/dist/core/inline/scan/autolinks.d.ts +7 -8
  268. package/dist/core/inline/scan/autolinks.js +80 -59
  269. package/dist/core/inline/scan/brackets.d.ts +2 -2
  270. package/dist/core/inline/scan/brackets.js +6 -103
  271. package/dist/core/inline/scan/code-spans.d.ts +4 -0
  272. package/dist/core/inline/scan/code-spans.js +8 -0
  273. package/dist/core/inline/scan/emphasis.js +10 -14
  274. package/dist/core/inline/scan/index.d.ts +3 -1
  275. package/dist/core/inline/scan/index.js +32 -33
  276. package/dist/core/inline/scan/plugin-syntax.d.ts +28 -36
  277. package/dist/core/inline/scan/plugin-syntax.js +75 -89
  278. package/dist/core/inline/scan/scan-state.d.ts +2 -0
  279. package/dist/core/inline/scan/scan-state.js +1 -0
  280. package/dist/core/inline/scan/simple-nodes.d.ts +2 -5
  281. package/dist/core/inline/scan/simple-nodes.js +2 -5
  282. package/dist/core/inline/scan/triggers.d.ts +17 -0
  283. package/dist/core/inline/scan/triggers.js +26 -0
  284. package/dist/core/inline/scan/url.js +8 -10
  285. package/dist/core/inline/transparency.d.ts +2 -1
  286. package/dist/core/inline/transparency.js +31 -15
  287. package/dist/core/inline/visibility.d.ts +30 -49
  288. package/dist/core/inline/visibility.js +31 -49
  289. package/dist/core/inline/walk.d.ts +4 -10
  290. package/dist/core/inline/walk.js +4 -10
  291. package/dist/core/inline-render.d.ts +22 -27
  292. package/dist/core/inline-render.js +51 -60
  293. package/dist/core/lines.d.ts +67 -26
  294. package/dist/core/lines.js +164 -33
  295. package/dist/core/metadata-parity.d.ts +9 -0
  296. package/dist/core/metadata-parity.js +25 -0
  297. package/dist/core/node-views.d.ts +7 -6
  298. package/dist/core/node-views.js +5 -5
  299. package/dist/core/nodes.d.ts +34 -44
  300. package/dist/core/nodes.js +4 -12
  301. package/dist/core/parser.d.ts +27 -34
  302. package/dist/core/parser.js +53 -67
  303. package/dist/core/parsers/blockquote.d.ts +5 -8
  304. package/dist/core/parsers/blockquote.js +11 -10
  305. package/dist/core/parsers/built-in-openers.d.ts +2 -5
  306. package/dist/core/parsers/built-in-openers.js +5 -8
  307. package/dist/core/parsers/fence-syntax.d.ts +36 -7
  308. package/dist/core/parsers/fence-syntax.js +61 -11
  309. package/dist/core/parsers/fenced-code.d.ts +16 -0
  310. package/dist/core/parsers/fenced-code.js +16 -15
  311. package/dist/core/parsers/heading.d.ts +19 -2
  312. package/dist/core/parsers/heading.js +50 -2
  313. package/dist/core/parsers/html-block.d.ts +3 -6
  314. package/dist/core/parsers/html-block.js +10 -11
  315. package/dist/core/parsers/indented-code.d.ts +1 -1
  316. package/dist/core/parsers/indented-code.js +2 -8
  317. package/dist/core/parsers/link-reference.d.ts +5 -3
  318. package/dist/core/parsers/link-reference.js +116 -120
  319. package/dist/core/parsers/list.d.ts +12 -5
  320. package/dist/core/parsers/list.js +53 -36
  321. package/dist/core/parsers/paragraph.d.ts +3 -3
  322. package/dist/core/parsers/paragraph.js +14 -5
  323. package/dist/core/parsers/table-completion.d.ts +2 -5
  324. package/dist/core/parsers/table-completion.js +7 -10
  325. package/dist/core/parsers/table.d.ts +8 -8
  326. package/dist/core/parsers/table.js +23 -26
  327. package/dist/core/parsers/thematic-break.js +15 -14
  328. package/dist/core/paths.d.ts +13 -0
  329. package/dist/core/paths.js +31 -0
  330. package/dist/core/terminator-escalation.d.ts +0 -7
  331. package/dist/core/terminator-escalation.js +5 -8
  332. package/dist/core/url-policy.d.ts +2 -5
  333. package/dist/core/url-policy.js +8 -12
  334. package/dist/cursor/caret-memory.d.ts +34 -0
  335. package/dist/cursor/caret-memory.js +82 -0
  336. package/dist/cursor/coordinate-spaces.d.ts +26 -19
  337. package/dist/cursor/coordinate-spaces.js +32 -20
  338. package/dist/cursor/dom-walk.d.ts +7 -13
  339. package/dist/cursor/dom-walk.js +7 -13
  340. package/dist/cursor/edge-affinity.d.ts +7 -35
  341. package/dist/cursor/edge-affinity.js +13 -44
  342. package/dist/cursor/focused-caret.d.ts +6 -8
  343. package/dist/cursor/focused-caret.js +7 -16
  344. package/dist/cursor/height-model.d.ts +3 -3
  345. package/dist/cursor/height-model.js +5 -5
  346. package/dist/cursor/height-oracle.d.ts +10 -6
  347. package/dist/cursor/height-oracle.js +26 -74
  348. package/dist/cursor/observe-resize.d.ts +13 -0
  349. package/dist/cursor/observe-resize.js +43 -0
  350. package/dist/cursor/overlay-rects.d.ts +2 -5
  351. package/dist/cursor/overlay-rects.js +2 -5
  352. package/dist/cursor/overlay-remeasure.d.ts +6 -7
  353. package/dist/cursor/overlay-remeasure.js +10 -14
  354. package/dist/cursor/pending-marks.d.ts +7 -9
  355. package/dist/cursor/pending-marks.js +6 -27
  356. package/dist/cursor/point-offset.d.ts +16 -10
  357. package/dist/cursor/point-offset.js +60 -25
  358. package/dist/cursor/reveal-source.d.ts +7 -11
  359. package/dist/cursor/reveal-source.js +10 -18
  360. package/dist/cursor/scroll-ancestors.d.ts +9 -20
  361. package/dist/cursor/scroll-ancestors.js +14 -26
  362. package/dist/cursor/scroll-owner.d.ts +92 -0
  363. package/dist/cursor/scroll-owner.js +283 -0
  364. package/dist/cursor/scrollport.d.ts +14 -15
  365. package/dist/cursor/scrollport.js +7 -13
  366. package/dist/cursor/sticky-column.d.ts +6 -29
  367. package/dist/cursor/sticky-column.js +6 -49
  368. package/dist/cursor/sticky-measure.d.ts +14 -9
  369. package/dist/cursor/sticky-measure.js +99 -49
  370. package/dist/cursor/surface-backend.d.ts +41 -0
  371. package/dist/cursor/surface-backend.js +78 -0
  372. package/dist/cursor/typography-estimates.d.ts +3 -3
  373. package/dist/cursor/typography-estimates.js +3 -3
  374. package/dist/cursor/visual-lines.d.ts +25 -14
  375. package/dist/cursor/visual-lines.js +45 -46
  376. package/dist/cursor/widget-edge-snap.d.ts +10 -15
  377. package/dist/cursor/widget-edge-snap.js +13 -12
  378. package/dist/cursor/widget-offset.d.ts +110 -104
  379. package/dist/cursor/widget-offset.js +344 -179
  380. package/dist/debug/diagnostics-report.d.ts +6 -6
  381. package/dist/debug/diagnostics-report.js +5 -5
  382. package/dist/debug/dump-tree.js +2 -2
  383. package/dist/debug/editor-diagnostics.d.ts +15 -0
  384. package/dist/debug/editor-diagnostics.js +33 -0
  385. package/dist/debug/inspect.d.ts +2 -2
  386. package/dist/debug/inspect.js +4 -4
  387. package/dist/debug/interaction-trace.d.ts +14 -15
  388. package/dist/debug/interaction-trace.js +19 -20
  389. package/dist/debug/operations-log.d.ts +1 -1
  390. package/dist/debug/operations-log.js +1 -1
  391. package/dist/decorations/buckets.d.ts +6 -12
  392. package/dist/decorations/buckets.js +4 -11
  393. package/dist/decorations/decoration-state.svelte.d.ts +2 -3
  394. package/dist/decorations/decoration-state.svelte.js +24 -26
  395. package/dist/decorations/island-dom.d.ts +10 -18
  396. package/dist/decorations/island-dom.js +25 -50
  397. package/dist/decorations/reserved-attrs.d.ts +6 -8
  398. package/dist/decorations/reserved-attrs.js +30 -17
  399. package/dist/decorations/types.d.ts +6 -7
  400. package/dist/decorations/types.js +2 -1
  401. package/dist/decorations/use-block-decorations.svelte.d.ts +19 -0
  402. package/dist/decorations/use-block-decorations.svelte.js +71 -0
  403. package/dist/decorations/widget-dom.d.ts +3 -3
  404. package/dist/decorations/widget-dom.js +5 -5
  405. package/dist/dev-warn.d.ts +5 -2
  406. package/dist/dev-warn.js +16 -4
  407. package/dist/editor-actions/ancestry-folds.d.ts +9 -14
  408. package/dist/editor-actions/ancestry-folds.js +13 -18
  409. package/dist/editor-actions/block-edit-core.d.ts +49 -22
  410. package/dist/editor-actions/block-edit-core.js +279 -151
  411. package/dist/editor-actions/block-edit-scope.d.ts +63 -50
  412. package/dist/editor-actions/block-edit-scope.js +100 -48
  413. package/dist/editor-actions/block-edit.d.ts +2 -3
  414. package/dist/editor-actions/block-edit.js +14 -90
  415. package/dist/editor-actions/commit/history.d.ts +4 -4
  416. package/dist/editor-actions/commit/history.js +33 -34
  417. package/dist/editor-actions/commit/reading-write-gate.d.ts +18 -0
  418. package/dist/editor-actions/commit/reading-write-gate.js +56 -0
  419. package/dist/editor-actions/commit/text-batch.d.ts +7 -11
  420. package/dist/editor-actions/commit/text-batch.js +6 -7
  421. package/dist/editor-actions/commit/undo-controller.d.ts +6 -4
  422. package/dist/editor-actions/commit/undo-controller.js +346 -216
  423. package/dist/editor-actions/container-block-component.d.ts +32 -51
  424. package/dist/editor-actions/container-block-component.js +66 -92
  425. package/dist/editor-actions/container-edit.d.ts +4 -2
  426. package/dist/editor-actions/container-edit.js +14 -44
  427. package/dist/editor-actions/container-exit-overrides.d.ts +6 -4
  428. package/dist/editor-actions/container-exit-overrides.js +19 -21
  429. package/dist/editor-actions/deps.d.ts +25 -32
  430. package/dist/editor-actions/enter-completion.d.ts +12 -15
  431. package/dist/editor-actions/enter-completion.js +49 -51
  432. package/dist/editor-actions/focus/focus-dispatch.d.ts +24 -25
  433. package/dist/editor-actions/focus/focus-dispatch.js +28 -50
  434. package/dist/editor-actions/focus/focus-landing.d.ts +8 -11
  435. package/dist/editor-actions/focus/focus-landing.js +15 -14
  436. package/dist/editor-actions/focus/focus.d.ts +3 -2
  437. package/dist/editor-actions/focus/focus.js +42 -59
  438. package/dist/editor-actions/index.d.ts +4 -3
  439. package/dist/editor-actions/index.js +5 -4
  440. package/dist/editor-actions/inline-range-commit.d.ts +18 -16
  441. package/dist/editor-actions/inline-range-commit.js +45 -54
  442. package/dist/editor-actions/leaf-write.d.ts +11 -0
  443. package/dist/editor-actions/leaf-write.js +102 -0
  444. package/dist/editor-actions/list-context.d.ts +9 -10
  445. package/dist/editor-actions/list-context.js +115 -110
  446. package/dist/editor-actions/list-overrides.d.ts +13 -5
  447. package/dist/editor-actions/list-overrides.js +36 -12
  448. package/dist/editor-actions/merge-fallback.d.ts +10 -16
  449. package/dist/editor-actions/merge-fallback.js +11 -22
  450. package/dist/editor-actions/nested/container-actions.d.ts +37 -0
  451. package/dist/editor-actions/nested/container-actions.js +42 -0
  452. package/dist/editor-actions/nested/emptied-container.d.ts +5 -0
  453. package/dist/editor-actions/nested/emptied-container.js +5 -0
  454. package/dist/editor-actions/nested/nested-actions.d.ts +22 -25
  455. package/dist/editor-actions/nested/nested-actions.js +17 -15
  456. package/dist/editor-actions/nested/nested-block-edit.d.ts +3 -4
  457. package/dist/editor-actions/nested/nested-block-edit.js +40 -134
  458. package/dist/editor-actions/nested/nested-focus.d.ts +2 -3
  459. package/dist/editor-actions/nested/nested-focus.js +33 -21
  460. package/dist/editor-actions/paste-coordinator.d.ts +3 -2
  461. package/dist/editor-actions/paste-coordinator.js +17 -13
  462. package/dist/editor-actions/plugin/chrome-leaf.d.ts +11 -9
  463. package/dist/editor-actions/plugin/chrome-leaf.js +14 -13
  464. package/dist/editor-actions/plugin/container.d.ts +54 -82
  465. package/dist/editor-actions/plugin/container.js +105 -188
  466. package/dist/editor-actions/plugin/directive-container.d.ts +4 -4
  467. package/dist/editor-actions/plugin/directive-container.js +7 -8
  468. package/dist/editor-actions/reorder-action.d.ts +7 -12
  469. package/dist/editor-actions/reorder-action.js +32 -48
  470. package/dist/editor-actions/reorder-drag.d.ts +12 -8
  471. package/dist/editor-actions/reorder-drag.js +45 -41
  472. package/dist/editor-actions/replacement-focus.d.ts +13 -21
  473. package/dist/editor-actions/replacement-focus.js +32 -33
  474. package/dist/editor-actions/search-replace.js +71 -67
  475. package/dist/editor-actions/stored-caret.d.ts +7 -0
  476. package/dist/editor-actions/stored-caret.js +10 -0
  477. package/dist/editor-actions/table-context.d.ts +9 -11
  478. package/dist/editor-actions/table-context.js +70 -93
  479. package/dist/editor-actions/unwrap-strategies.d.ts +4 -5
  480. package/dist/editor-actions/unwrap-strategies.js +52 -63
  481. package/dist/editor-actions/whole-block-focus-surface.d.ts +19 -28
  482. package/dist/editor-actions/whole-block-focus-surface.js +43 -53
  483. package/dist/editor-events.d.ts +25 -29
  484. package/dist/editor-events.js +17 -28
  485. package/dist/editor-keys.d.ts +96 -104
  486. package/dist/editor-keys.js +11 -24
  487. package/dist/editor-props.d.ts +88 -71
  488. package/dist/editor-rects.d.ts +23 -34
  489. package/dist/editor-rects.js +9 -77
  490. package/dist/env.d.ts +6 -4
  491. package/dist/env.js +8 -4
  492. package/dist/index.d.ts +6 -2
  493. package/dist/index.js +5 -2
  494. package/dist/inline-menu/inline-menu-session.d.ts +37 -0
  495. package/dist/inline-menu/inline-menu-session.js +98 -0
  496. package/dist/inline-menu/inline-menu-state.svelte.d.ts +63 -0
  497. package/dist/inline-menu/inline-menu-state.svelte.js +409 -0
  498. package/dist/inline-menu/types.d.ts +77 -0
  499. package/dist/inline-menu/types.js +7 -0
  500. package/dist/invariants/child-id-parity.d.ts +23 -0
  501. package/dist/invariants/child-id-parity.js +39 -0
  502. package/dist/invariants/commit-paths.d.ts +4 -4
  503. package/dist/invariants/commit-paths.js +4 -4
  504. package/dist/invariants/commit-scope.d.ts +4 -4
  505. package/dist/invariants/commit-scope.js +9 -8
  506. package/dist/invariants/context-keys.d.ts +2 -5
  507. package/dist/invariants/context-keys.js +3 -6
  508. package/dist/invariants/descriptor.d.ts +2 -6
  509. package/dist/invariants/descriptor.js +2 -6
  510. package/dist/invariants/inline-transitions.d.ts +9 -16
  511. package/dist/invariants/inline-transitions.js +10 -18
  512. package/dist/invariants/install.d.ts +18 -27
  513. package/dist/invariants/install.js +29 -30
  514. package/dist/invariants/keeps-a-block.d.ts +7 -0
  515. package/dist/invariants/keeps-a-block.js +14 -0
  516. package/dist/invariants/landable-caret.d.ts +5 -7
  517. package/dist/invariants/landable-caret.js +8 -11
  518. package/dist/invariants/landing-focus-scroll.d.ts +7 -0
  519. package/dist/invariants/landing-focus-scroll.js +13 -0
  520. package/dist/invariants/landing-value.d.ts +13 -0
  521. package/dist/invariants/landing-value.js +27 -0
  522. package/dist/invariants/marker-css-parity.d.ts +5 -6
  523. package/dist/invariants/marker-css-parity.js +11 -12
  524. package/dist/invariants/measures-in-own-list.d.ts +6 -0
  525. package/dist/invariants/measures-in-own-list.js +13 -0
  526. package/dist/invariants/node-shape.d.ts +18 -37
  527. package/dist/invariants/node-shape.js +123 -79
  528. package/dist/invariants/open-tail.d.ts +7 -0
  529. package/dist/invariants/open-tail.js +45 -0
  530. package/dist/invariants/placement-ends-widget.d.ts +8 -0
  531. package/dist/invariants/placement-ends-widget.js +13 -0
  532. package/dist/invariants/registry.d.ts +27 -66
  533. package/dist/invariants/registry.js +61 -137
  534. package/dist/invariants/render-fidelity.d.ts +4 -4
  535. package/dist/invariants/render-fidelity.js +5 -5
  536. package/dist/invariants/selection-endpoints.d.ts +6 -7
  537. package/dist/invariants/selection-endpoints.js +18 -16
  538. package/dist/invariants/single-node-sink.d.ts +4 -5
  539. package/dist/invariants/single-node-sink.js +4 -5
  540. package/dist/invariants/snapshot-integrity.d.ts +5 -6
  541. package/dist/invariants/snapshot-integrity.js +1 -1
  542. package/dist/invariants/structural-descriptor.d.ts +6 -15
  543. package/dist/invariants/structural-descriptor.js +6 -15
  544. package/dist/menu-icons.d.ts +47 -0
  545. package/dist/menu-icons.js +113 -0
  546. package/dist/perf/instruments.d.ts +20 -5
  547. package/dist/perf/instruments.js +35 -15
  548. package/dist/perf/use-mount-gauge.svelte.js +4 -4
  549. package/dist/plugin.d.ts +37 -8
  550. package/dist/plugin.js +100 -77
  551. package/dist/plugins/admonitions/AdmonitionBlock.svelte +14 -11
  552. package/dist/plugins/admonitions/admonition-kind.js +12 -10
  553. package/dist/plugins/admonitions/convert-document.d.ts +5 -5
  554. package/dist/plugins/admonitions/convert-document.js +8 -7
  555. package/dist/plugins/admonitions/gh-alert.d.ts +8 -1
  556. package/dist/plugins/admonitions/gh-alert.js +21 -22
  557. package/dist/plugins/admonitions/github-alert-kind.d.ts +5 -7
  558. package/dist/plugins/admonitions/github-alert-kind.js +28 -37
  559. package/dist/plugins/admonitions/index.js +2 -2
  560. package/dist/plugins/admonitions/kinds.d.ts +1 -1
  561. package/dist/plugins/admonitions/register.js +11 -2
  562. package/dist/plugins/details/DetailsBlock.svelte +20 -20
  563. package/dist/plugins/details/details-disclosure.svelte.d.ts +5 -6
  564. package/dist/plugins/details/details-disclosure.svelte.js +6 -7
  565. package/dist/plugins/details/details-kind.d.ts +6 -7
  566. package/dist/plugins/details/details-kind.js +44 -59
  567. package/dist/plugins/emoji/emoji-recognizer.d.ts +4 -7
  568. package/dist/plugins/emoji/emoji-recognizer.js +7 -14
  569. package/dist/plugins/footnotes/FootnoteDefinition.svelte +33 -16
  570. package/dist/plugins/footnotes/FootnoteReference.svelte +39 -19
  571. package/dist/plugins/footnotes/constants.d.ts +2 -0
  572. package/dist/plugins/footnotes/constants.js +3 -0
  573. package/dist/plugins/footnotes/footnote-definition.d.ts +8 -6
  574. package/dist/plugins/footnotes/footnote-definition.js +69 -45
  575. package/dist/plugins/footnotes/footnote-lookup.d.ts +3 -3
  576. package/dist/plugins/footnotes/footnote-lookup.js +14 -26
  577. package/dist/plugins/footnotes/footnote-numbering.d.ts +11 -11
  578. package/dist/plugins/footnotes/footnote-numbering.js +22 -31
  579. package/dist/plugins/footnotes/footnote-reference.d.ts +4 -4
  580. package/dist/plugins/footnotes/footnote-reference.js +8 -13
  581. package/dist/plugins/footnotes/index.js +2 -3
  582. package/dist/plugins/highlight-occurrences/highlight-occurrences-plugin.d.ts +5 -8
  583. package/dist/plugins/highlight-occurrences/highlight-occurrences-plugin.js +8 -3
  584. package/dist/plugins/highlight-occurrences/occurrence-source.d.ts +10 -12
  585. package/dist/plugins/highlight-occurrences/occurrence-source.js +21 -18
  586. package/dist/plugins/highlight-occurrences/occurrences.d.ts +11 -11
  587. package/dist/plugins/highlight-occurrences/occurrences.js +14 -36
  588. package/dist/plugins/highlight-occurrences/word-char.d.ts +1 -0
  589. package/dist/plugins/highlight-occurrences/word-char.js +4 -0
  590. package/dist/plugins/latex/BlockMath.svelte +58 -75
  591. package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -4
  592. package/dist/plugins/latex/MathInline.svelte +10 -9
  593. package/dist/plugins/latex/flanking.d.ts +1 -0
  594. package/dist/plugins/latex/flanking.js +3 -0
  595. package/dist/plugins/latex/index.d.ts +1 -1
  596. package/dist/plugins/latex/latex-kind.d.ts +4 -4
  597. package/dist/plugins/latex/latex-kind.js +78 -67
  598. package/dist/plugins/latex/math-completion.d.ts +1 -1
  599. package/dist/plugins/latex/math-completion.js +5 -8
  600. package/dist/plugins/latex/math-layout.d.ts +2 -8
  601. package/dist/plugins/latex/math-layout.js +0 -9
  602. package/dist/plugins/latex/math-renderer.d.ts +17 -20
  603. package/dist/plugins/latex/math-renderer.js +21 -38
  604. package/dist/plugins/latex/math-source.d.ts +4 -8
  605. package/dist/plugins/latex/math-source.js +36 -81
  606. package/dist/plugins/latex/register.d.ts +10 -11
  607. package/dist/plugins/latex/register.js +24 -10
  608. package/dist/plugins/latex/renderer.d.ts +9 -10
  609. package/dist/plugins/latex/renderer.js +9 -24
  610. package/dist/plugins/mermaid/MermaidBlock.svelte +51 -45
  611. package/dist/plugins/mermaid/index.js +1 -1
  612. package/dist/plugins/mermaid/mermaid-kind.d.ts +4 -7
  613. package/dist/plugins/mermaid/mermaid-kind.js +30 -47
  614. package/dist/plugins/mermaid/mermaid-renderer.d.ts +9 -16
  615. package/dist/plugins/mermaid/mermaid-renderer.js +12 -31
  616. package/dist/plugins/mermaid/register.d.ts +3 -3
  617. package/dist/plugins/mermaid/register.js +5 -5
  618. package/dist/plugins/mermaid/renderer.d.ts +5 -5
  619. package/dist/plugins/mermaid/renderer.js +8 -11
  620. package/dist/plugins/parrot/ParrotBlock.svelte +15 -6
  621. package/dist/plugins/parrot/ParrotBlock.svelte.d.ts +2 -2
  622. package/dist/plugins/parrot/parrot-plugin.js +3 -3
  623. package/dist/plugins/slash-commands/filter.d.ts +23 -0
  624. package/dist/plugins/slash-commands/filter.js +31 -0
  625. package/dist/plugins/slash-commands/index.d.ts +2 -0
  626. package/dist/plugins/slash-commands/index.js +2 -0
  627. package/dist/plugins/slash-commands/slash-commands-plugin.d.ts +12 -0
  628. package/dist/plugins/slash-commands/slash-commands-plugin.js +40 -0
  629. package/dist/plugins/slash-commands/slash-source.d.ts +36 -0
  630. package/dist/plugins/slash-commands/slash-source.js +108 -0
  631. package/dist/plugins/toc/TocBlock.svelte +23 -22
  632. package/dist/plugins/toc/TocBlock.svelte.d.ts +2 -3
  633. package/dist/plugins/toc/heading-outline.d.ts +7 -12
  634. package/dist/plugins/toc/heading-outline.js +22 -36
  635. package/dist/plugins/toc/navigation-queue.d.ts +2 -4
  636. package/dist/plugins/toc/toc-plugin.d.ts +5 -5
  637. package/dist/plugins/toc/toc-plugin.js +20 -13
  638. package/dist/presentation-mode.d.ts +15 -23
  639. package/dist/presentation-mode.js +19 -25
  640. package/dist/reactivity/block-list-state.svelte.d.ts +9 -11
  641. package/dist/reactivity/block-list-state.svelte.js +8 -9
  642. package/dist/reactivity/block-window.svelte.d.ts +12 -6
  643. package/dist/reactivity/block-window.svelte.js +44 -17
  644. package/dist/reactivity/child-list.d.ts +34 -0
  645. package/dist/reactivity/child-list.js +48 -0
  646. package/dist/reactivity/content-version.svelte.d.ts +4 -7
  647. package/dist/reactivity/content-version.svelte.js +3 -5
  648. package/dist/reactivity/hold-across.d.ts +39 -0
  649. package/dist/reactivity/hold-across.js +57 -0
  650. package/dist/reactivity/layout-state.svelte.d.ts +27 -0
  651. package/dist/reactivity/layout-state.svelte.js +51 -0
  652. package/dist/reactivity/list-tree.d.ts +39 -0
  653. package/dist/reactivity/list-tree.js +132 -0
  654. package/dist/reactivity/list-windowing.svelte.d.ts +50 -71
  655. package/dist/reactivity/list-windowing.svelte.js +141 -310
  656. package/dist/reactivity/measure-batch.d.ts +6 -8
  657. package/dist/reactivity/measure-batch.js +5 -6
  658. package/dist/reactivity/publish-ref.svelte.d.ts +16 -41
  659. package/dist/reactivity/publish-ref.svelte.js +21 -85
  660. package/dist/reactivity/scope-geometry.d.ts +6 -13
  661. package/dist/reactivity/scope-geometry.js +7 -14
  662. package/dist/reactivity/state-registry.d.ts +5 -5
  663. package/dist/reactivity/state-registry.js +11 -15
  664. package/dist/reactivity/use-container-windowing.svelte.d.ts +13 -26
  665. package/dist/reactivity/use-container-windowing.svelte.js +64 -74
  666. package/dist/reactivity/use-measured-child.svelte.d.ts +10 -0
  667. package/dist/reactivity/use-measured-child.svelte.js +41 -0
  668. package/dist/reactivity/use-window-floor.svelte.d.ts +2 -0
  669. package/dist/reactivity/use-window-floor.svelte.js +27 -0
  670. package/dist/reactivity/window-slice.d.ts +4 -4
  671. package/dist/reactivity/window-slice.js +0 -2
  672. package/dist/renderer-slot.d.ts +48 -0
  673. package/dist/renderer-slot.js +85 -0
  674. package/dist/scan-index.d.ts +4 -4
  675. package/dist/scan-index.js +4 -4
  676. package/dist/schema/block-commands.d.ts +69 -54
  677. package/dist/schema/block-commands.js +91 -104
  678. package/dist/schema/block-completions.d.ts +16 -19
  679. package/dist/schema/block-completions.js +25 -32
  680. package/dist/schema/block-component-registry.d.ts +13 -14
  681. package/dist/schema/block-component-registry.js +19 -17
  682. package/dist/schema/block-kind-descriptor.d.ts +234 -168
  683. package/dist/schema/block-kind-descriptor.js +151 -66
  684. package/dist/schema/block-openers.d.ts +48 -39
  685. package/dist/schema/block-openers.js +73 -83
  686. package/dist/schema/built-in-descriptors.d.ts +3 -5
  687. package/dist/schema/built-in-descriptors.js +96 -80
  688. package/dist/schema/child-spans.d.ts +12 -10
  689. package/dist/schema/child-spans.js +68 -34
  690. package/dist/schema/closure.d.ts +18 -16
  691. package/dist/schema/closure.js +24 -14
  692. package/dist/schema/command-id.d.ts +6 -8
  693. package/dist/schema/command-id.js +20 -25
  694. package/dist/schema/commands.d.ts +78 -84
  695. package/dist/schema/commands.js +152 -117
  696. package/dist/schema/container-raw.d.ts +51 -11
  697. package/dist/schema/container-raw.js +143 -14
  698. package/dist/schema/container-rebuilders.d.ts +17 -15
  699. package/dist/schema/container-rebuilders.js +30 -22
  700. package/dist/schema/context-actions.d.ts +25 -13
  701. package/dist/schema/context-actions.js +32 -17
  702. package/dist/schema/define-plugin-block.d.ts +8 -6
  703. package/dist/schema/define-plugin-block.js +6 -4
  704. package/dist/schema/fenced-code-raw.d.ts +30 -17
  705. package/dist/schema/fenced-code-raw.js +155 -107
  706. package/dist/schema/global-commands.d.ts +6 -6
  707. package/dist/schema/global-commands.js +14 -22
  708. package/dist/schema/height-estimates.d.ts +31 -0
  709. package/dist/schema/height-estimates.js +64 -0
  710. package/dist/schema/inline-construct-policy.d.ts +42 -55
  711. package/dist/schema/inline-construct-policy.js +36 -35
  712. package/dist/schema/insert-catalogue.d.ts +30 -0
  713. package/dist/schema/insert-catalogue.js +95 -0
  714. package/dist/schema/keybinding-overrides.d.ts +9 -11
  715. package/dist/schema/keybinding-overrides.js +1 -1
  716. package/dist/schema/keybindings.d.ts +15 -7
  717. package/dist/schema/keybindings.js +21 -9
  718. package/dist/schema/merge-rules.d.ts +5 -7
  719. package/dist/schema/merge-rules.js +7 -9
  720. package/dist/schema/opener-priorities.d.ts +4 -4
  721. package/dist/schema/opener-priorities.js +4 -4
  722. package/dist/schema/operations.d.ts +8 -10
  723. package/dist/schema/operations.js +3 -3
  724. package/dist/schema/page-role.d.ts +14 -0
  725. package/dist/schema/page-role.js +19 -0
  726. package/dist/schema/plugin-activation.d.ts +4 -6
  727. package/dist/schema/plugin-activation.js +12 -10
  728. package/dist/schema/plugin-editor-context.d.ts +25 -10
  729. package/dist/schema/plugin-editor-context.js +40 -9
  730. package/dist/schema/plugin-install.d.ts +49 -17
  731. package/dist/schema/plugin-install.js +51 -25
  732. package/dist/schema/plugin-kind.d.ts +12 -8
  733. package/dist/schema/plugin-kind.js +35 -42
  734. package/dist/schema/plugin-name.js +3 -3
  735. package/dist/schema/plugin-registry.d.ts +54 -0
  736. package/dist/schema/plugin-registry.js +70 -0
  737. package/dist/schema/reading.d.ts +23 -0
  738. package/dist/schema/reading.js +7 -0
  739. package/dist/schema/register-once.d.ts +3 -5
  740. package/dist/schema/register-once.js +7 -16
  741. package/dist/schema/registration-checks.d.ts +12 -16
  742. package/dist/schema/registration-checks.js +35 -51
  743. package/dist/schema/registration-pairs.d.ts +54 -0
  744. package/dist/schema/registration-pairs.js +48 -0
  745. package/dist/schema/registration-pending.d.ts +8 -12
  746. package/dist/schema/registration-pending.js +9 -9
  747. package/dist/schema/registry-reset.d.ts +8 -4
  748. package/dist/schema/registry-reset.js +13 -26
  749. package/dist/schema/registry-view.d.ts +25 -13
  750. package/dist/schema/registry-view.js +26 -20
  751. package/dist/schema/reserved-chords.d.ts +9 -10
  752. package/dist/schema/reserved-chords.js +63 -80
  753. package/dist/schema/reserved-chrome.d.ts +12 -11
  754. package/dist/schema/reserved-chrome.js +15 -11
  755. package/dist/schema/setext-raw.d.ts +13 -0
  756. package/dist/schema/setext-raw.js +33 -0
  757. package/dist/schema/stored-as.d.ts +20 -0
  758. package/dist/schema/stored-as.js +6 -0
  759. package/dist/schema/table-cell-raw.d.ts +18 -7
  760. package/dist/schema/table-cell-raw.js +28 -7
  761. package/dist/schema/whole-block-unit.d.ts +5 -4
  762. package/dist/schema/whole-block-unit.js +5 -4
  763. package/dist/search/document-scan.d.ts +3 -3
  764. package/dist/search/document-scan.js +2 -2
  765. package/dist/search/matcher.d.ts +1 -1
  766. package/dist/search/regex-executor.d.ts +5 -6
  767. package/dist/search/regex-executor.js +6 -8
  768. package/dist/search/replace.d.ts +2 -5
  769. package/dist/search/replace.js +2 -5
  770. package/dist/search/search-state.svelte.d.ts +5 -5
  771. package/dist/search/search-state.svelte.js +12 -16
  772. package/dist/selection/autoscroll.d.ts +6 -9
  773. package/dist/selection/block-hit-test.d.ts +16 -24
  774. package/dist/selection/block-hit-test.js +29 -22
  775. package/dist/selection/caret-doors.d.ts +15 -14
  776. package/dist/selection/caret-doors.js +37 -26
  777. package/dist/selection/caret-landing.d.ts +62 -0
  778. package/dist/selection/caret-landing.js +157 -0
  779. package/dist/selection/caret-restore.d.ts +17 -10
  780. package/dist/selection/caret-restore.js +10 -22
  781. package/dist/selection/caret-target.d.ts +30 -0
  782. package/dist/selection/caret-target.js +84 -0
  783. package/dist/selection/char-endpoint-snap.d.ts +6 -10
  784. package/dist/selection/char-endpoint-snap.js +8 -14
  785. package/dist/selection/clipboard-text.d.ts +5 -12
  786. package/dist/selection/clipboard-text.js +69 -172
  787. package/dist/selection/covered-block.d.ts +6 -8
  788. package/dist/selection/covered-block.js +8 -19
  789. package/dist/selection/cross-block/clipboard.d.ts +3 -2
  790. package/dist/selection/cross-block/clipboard.js +13 -4
  791. package/dist/selection/cross-block/dispatch.d.ts +47 -43
  792. package/dist/selection/cross-block/dispatch.js +12 -23
  793. package/dist/selection/cross-block/format-range.d.ts +18 -21
  794. package/dist/selection/cross-block/format-range.js +104 -103
  795. package/dist/selection/cross-block/format-toggle.d.ts +10 -15
  796. package/dist/selection/cross-block/format-toggle.js +28 -33
  797. package/dist/selection/cross-block/keydown.d.ts +4 -4
  798. package/dist/selection/cross-block/keydown.js +92 -125
  799. package/dist/selection/cross-block/ops.d.ts +19 -34
  800. package/dist/selection/cross-block/ops.js +110 -95
  801. package/dist/selection/cross-block/paste.d.ts +2 -3
  802. package/dist/selection/cross-block/paste.js +59 -96
  803. package/dist/selection/cross-block/pointer.d.ts +17 -15
  804. package/dist/selection/cross-block/pointer.js +74 -25
  805. package/dist/selection/cross-block/type-replace.d.ts +4 -6
  806. package/dist/selection/cross-block/type-replace.js +51 -134
  807. package/dist/selection/dead-space-caret.d.ts +19 -30
  808. package/dist/selection/dead-space-caret.js +70 -88
  809. package/dist/selection/drag-pointer.d.ts +7 -9
  810. package/dist/selection/drag-pointer.js +23 -33
  811. package/dist/selection/gap-caret.d.ts +17 -28
  812. package/dist/selection/gap-caret.js +14 -26
  813. package/dist/selection/grid-selection.d.ts +12 -0
  814. package/dist/selection/grid-selection.js +22 -0
  815. package/dist/selection/keyboard-extend.d.ts +27 -40
  816. package/dist/selection/keyboard-extend.js +94 -117
  817. package/dist/selection/multi-click.d.ts +13 -18
  818. package/dist/selection/multi-click.js +32 -38
  819. package/dist/selection/native-bridge.d.ts +29 -39
  820. package/dist/selection/native-bridge.js +78 -127
  821. package/dist/selection/nearest-block.d.ts +17 -12
  822. package/dist/selection/nearest-block.js +37 -18
  823. package/dist/selection/path-lookup.d.ts +25 -16
  824. package/dist/selection/path-lookup.js +101 -18
  825. package/dist/selection/path-math.d.ts +9 -11
  826. package/dist/selection/path-math.js +5 -7
  827. package/dist/selection/pointer-gesture.d.ts +5 -5
  828. package/dist/selection/pointer-gesture.js +5 -5
  829. package/dist/selection/pointer-session.d.ts +10 -16
  830. package/dist/selection/pointer-session.js +6 -7
  831. package/dist/selection/primitives.d.ts +64 -40
  832. package/dist/selection/primitives.js +57 -67
  833. package/dist/selection/range-coverage.d.ts +94 -0
  834. package/dist/selection/range-coverage.js +365 -0
  835. package/dist/selection/range-delete-ceremony.d.ts +28 -74
  836. package/dist/selection/range-delete-ceremony.js +100 -141
  837. package/dist/selection/range-delete-chrome.d.ts +20 -33
  838. package/dist/selection/range-delete-chrome.js +53 -78
  839. package/dist/selection/range-delete-table-coverage.d.ts +11 -25
  840. package/dist/selection/range-delete-table-coverage.js +42 -112
  841. package/dist/selection/range-delete-table.d.ts +14 -12
  842. package/dist/selection/range-delete-table.js +106 -310
  843. package/dist/selection/range-delete.d.ts +18 -23
  844. package/dist/selection/range-delete.js +83 -123
  845. package/dist/selection/round-trip-restore.d.ts +18 -0
  846. package/dist/selection/round-trip-restore.js +19 -0
  847. package/dist/selection/selection-announcer.d.ts +17 -0
  848. package/dist/selection/selection-announcer.js +25 -0
  849. package/dist/selection/selection-description.d.ts +4 -3
  850. package/dist/selection/selection-description.js +5 -4
  851. package/dist/selection/selection-drop.d.ts +34 -16
  852. package/dist/selection/selection-drop.js +159 -95
  853. package/dist/selection/selection-restore.d.ts +20 -33
  854. package/dist/selection/selection-restore.js +21 -44
  855. package/dist/selection/selection-state.svelte.d.ts +50 -42
  856. package/dist/selection/selection-state.svelte.js +144 -98
  857. package/dist/selection/shared-keydown.d.ts +22 -27
  858. package/dist/selection/shared-keydown.js +25 -45
  859. package/dist/selection/table-endpoint-snap.d.ts +17 -41
  860. package/dist/selection/table-endpoint-snap.js +54 -80
  861. package/dist/selection/table-rect-extend.d.ts +3 -6
  862. package/dist/selection/table-rect-extend.js +19 -14
  863. package/dist/styles/editor-theme.css +60 -53
  864. package/dist/styles/editor.css +201 -143
  865. package/dist/testing/conformance-core.d.ts +50 -18
  866. package/dist/testing/conformance-core.js +88 -50
  867. package/dist/testing/container-conformance.d.ts +48 -84
  868. package/dist/testing/container-conformance.js +159 -285
  869. package/dist/testing/headless-actions.d.ts +34 -19
  870. package/dist/testing/headless-actions.js +116 -65
  871. package/dist/testing/headless-block-list.svelte.d.ts +16 -0
  872. package/dist/testing/headless-block-list.svelte.js +22 -0
  873. package/dist/testing/inline-conformance.d.ts +15 -22
  874. package/dist/testing/inline-conformance.js +131 -148
  875. package/dist/testing/kind-conformance.d.ts +19 -24
  876. package/dist/testing/kind-conformance.js +204 -117
  877. package/dist/testing/kit-reading.d.ts +7 -0
  878. package/dist/testing/kit-reading.js +16 -0
  879. package/dist/testing/mount-dom-stubs.d.ts +4 -5
  880. package/dist/testing/mount-dom-stubs.js +8 -5
  881. package/dist/testing/parse-convergence.d.ts +10 -10
  882. package/dist/testing/parse-convergence.js +14 -49
  883. package/dist/testing.d.ts +13 -9
  884. package/dist/testing.js +28 -38
  885. package/dist/tree-operations/blockquote.d.ts +8 -14
  886. package/dist/tree-operations/blockquote.js +25 -45
  887. package/dist/tree-operations/chain-rebuild.d.ts +34 -25
  888. package/dist/tree-operations/chain-rebuild.js +115 -52
  889. package/dist/tree-operations/children.d.ts +5 -9
  890. package/dist/tree-operations/children.js +7 -11
  891. package/dist/tree-operations/cleanup.d.ts +6 -7
  892. package/dist/tree-operations/cleanup.js +14 -11
  893. package/dist/tree-operations/clone.js +5 -4
  894. package/dist/tree-operations/container-lift.d.ts +10 -5
  895. package/dist/tree-operations/container-lift.js +20 -14
  896. package/dist/tree-operations/container-offsets.d.ts +22 -0
  897. package/dist/tree-operations/container-offsets.js +89 -0
  898. package/dist/tree-operations/content-write.d.ts +52 -29
  899. package/dist/tree-operations/content-write.js +155 -110
  900. package/dist/tree-operations/index.d.ts +4 -4
  901. package/dist/tree-operations/index.js +4 -4
  902. package/dist/tree-operations/keep-one-block.d.ts +10 -0
  903. package/dist/tree-operations/keep-one-block.js +19 -0
  904. package/dist/tree-operations/leaf-range.d.ts +33 -0
  905. package/dist/tree-operations/leaf-range.js +67 -0
  906. package/dist/tree-operations/list/empty-check.d.ts +2 -3
  907. package/dist/tree-operations/list/empty-check.js +4 -4
  908. package/dist/tree-operations/list/exit-replacement.d.ts +4 -6
  909. package/dist/tree-operations/list/exit-replacement.js +4 -9
  910. package/dist/tree-operations/list/item-partition.d.ts +2 -2
  911. package/dist/tree-operations/list/item-partition.js +2 -2
  912. package/dist/tree-operations/list/list-builders.d.ts +18 -16
  913. package/dist/tree-operations/list/list-builders.js +59 -30
  914. package/dist/tree-operations/list/ordered-markers.d.ts +7 -16
  915. package/dist/tree-operations/list/ordered-markers.js +7 -16
  916. package/dist/tree-operations/list/reconcile-task.d.ts +16 -7
  917. package/dist/tree-operations/list/reconcile-task.js +80 -23
  918. package/dist/tree-operations/list/sublist-separator.d.ts +7 -7
  919. package/dist/tree-operations/list/sublist-separator.js +20 -27
  920. package/dist/tree-operations/list/task-paragraph.d.ts +25 -0
  921. package/dist/tree-operations/list/task-paragraph.js +54 -0
  922. package/dist/tree-operations/list/unwrap-merge.d.ts +8 -16
  923. package/dist/tree-operations/list/unwrap-merge.js +44 -79
  924. package/dist/tree-operations/node-ops.d.ts +30 -52
  925. package/dist/tree-operations/node-ops.js +152 -218
  926. package/dist/tree-operations/node-primitives.d.ts +44 -31
  927. package/dist/tree-operations/node-primitives.js +84 -60
  928. package/dist/tree-operations/open-tail.d.ts +25 -0
  929. package/dist/tree-operations/open-tail.js +100 -0
  930. package/dist/tree-operations/parse-block.d.ts +15 -2
  931. package/dist/tree-operations/parse-block.js +19 -4
  932. package/dist/tree-operations/paste/apply.d.ts +7 -8
  933. package/dist/tree-operations/paste/apply.js +33 -63
  934. package/dist/tree-operations/paste/body-write.d.ts +8 -6
  935. package/dist/tree-operations/paste/body-write.js +13 -14
  936. package/dist/tree-operations/paste/container-match.d.ts +7 -10
  937. package/dist/tree-operations/paste/container-match.js +76 -58
  938. package/dist/tree-operations/paste/container-paste.d.ts +3 -3
  939. package/dist/tree-operations/paste/container-paste.js +3 -3
  940. package/dist/tree-operations/paste/dispatch.d.ts +26 -29
  941. package/dist/tree-operations/paste/dispatch.js +68 -53
  942. package/dist/tree-operations/paste/focus-target.d.ts +11 -14
  943. package/dist/tree-operations/paste/focus-target.js +13 -18
  944. package/dist/tree-operations/paste/hooks.d.ts +7 -12
  945. package/dist/tree-operations/paste/hooks.js +49 -46
  946. package/dist/tree-operations/paste/line-ending.d.ts +11 -0
  947. package/dist/tree-operations/paste/line-ending.js +32 -0
  948. package/dist/tree-operations/paste/list-absorb.d.ts +8 -11
  949. package/dist/tree-operations/paste/list-absorb.js +17 -19
  950. package/dist/tree-operations/paste/list-break-out.d.ts +12 -11
  951. package/dist/tree-operations/paste/list-break-out.js +24 -32
  952. package/dist/tree-operations/paste/parent-scope.d.ts +8 -12
  953. package/dist/tree-operations/paste/parent-scope.js +9 -16
  954. package/dist/tree-operations/paste/paste-deps.d.ts +14 -9
  955. package/dist/tree-operations/paste/paste-replacement.d.ts +20 -3
  956. package/dist/tree-operations/paste/paste-replacement.js +73 -42
  957. package/dist/tree-operations/paste/paste-transforms.d.ts +5 -7
  958. package/dist/tree-operations/paste/paste-transforms.js +18 -27
  959. package/dist/tree-operations/paste/replacement-parse.d.ts +9 -7
  960. package/dist/tree-operations/paste/replacement-parse.js +12 -10
  961. package/dist/tree-operations/paste/strategy.d.ts +1 -1
  962. package/dist/tree-operations/paste/strategy.js +1 -1
  963. package/dist/tree-operations/paste/table-slice.js +15 -10
  964. package/dist/tree-operations/paste-surfaces.d.ts +18 -29
  965. package/dist/tree-operations/paste-surfaces.js +14 -10
  966. package/dist/tree-operations/path-mutate.d.ts +7 -8
  967. package/dist/tree-operations/path-mutate.js +4 -4
  968. package/dist/tree-operations/reorder-unit.d.ts +4 -5
  969. package/dist/tree-operations/reorder-unit.js +5 -6
  970. package/dist/tree-operations/reorder.d.ts +7 -9
  971. package/dist/tree-operations/reorder.js +39 -40
  972. package/dist/tree-operations/settle.d.ts +42 -53
  973. package/dist/tree-operations/settle.js +241 -162
  974. package/dist/tree-operations/sharing.d.ts +5 -5
  975. package/dist/tree-operations/splice-many.d.ts +5 -5
  976. package/dist/tree-operations/splice-many.js +5 -5
  977. package/dist/tree-operations/stored-as.d.ts +13 -0
  978. package/dist/tree-operations/stored-as.js +48 -0
  979. package/dist/tree-operations/structural-change.d.ts +11 -15
  980. package/dist/tree-operations/structural-change.js +10 -14
  981. package/dist/tree-operations/structural-suffix.d.ts +22 -0
  982. package/dist/tree-operations/structural-suffix.js +61 -0
  983. package/dist/tree-operations/sub-table-copy.d.ts +7 -0
  984. package/dist/tree-operations/sub-table-copy.js +40 -36
  985. package/dist/tree-operations/table-grid-clipboard.d.ts +2 -7
  986. package/dist/tree-operations/table-grid-clipboard.js +11 -36
  987. package/dist/tree-operations/table-mutations.d.ts +7 -3
  988. package/dist/tree-operations/table-mutations.js +45 -9
  989. package/dist/tree-operations/unshare.d.ts +11 -15
  990. package/dist/tree-operations/unshare.js +15 -17
  991. package/dist/undo/manager.js +1 -1
  992. package/dist/undo/types.d.ts +2 -2
  993. package/docs/guide/consumer-guide.md +327 -205
  994. package/docs/guide/directives.md +7 -7
  995. package/docs/guide/plugin-api.md +253 -185
  996. package/docs/guide/plugin-guide.md +332 -235
  997. package/docs/guide/plugin-testing.md +96 -82
  998. package/package.json +12 -16
  999. package/dist/ambient/ambient-cursor.d.ts +0 -41
  1000. package/dist/ambient/ambient-cursor.js +0 -120
  1001. package/dist/components/blocks/code/code-paste.d.ts +0 -24
  1002. package/dist/components/blocks/code/code-paste.js +0 -21
  1003. package/dist/components/blocks/table/cell-clipboard.d.ts +0 -33
  1004. package/dist/components/blocks/table/cell-clipboard.js +0 -67
  1005. package/dist/components/blocks/text/hidden-suffix.d.ts +0 -10
  1006. package/dist/components/blocks/text/hidden-suffix.js +0 -15
  1007. package/dist/components/blocks/text/link-source-bytes.d.ts +0 -37
  1008. package/dist/components/image/image-source-bytes.d.ts +0 -15
  1009. package/dist/components/image/image-source-bytes.js +0 -80
  1010. package/dist/cursor/content-offsets.d.ts +0 -29
  1011. package/dist/cursor/content-offsets.js +0 -151
  1012. package/dist/cursor/reveal-anchor.d.ts +0 -30
  1013. package/dist/cursor/reveal-anchor.js +0 -31
  1014. package/dist/cursor/scroll-hold.d.ts +0 -10
  1015. package/dist/cursor/scroll-hold.js +0 -22
  1016. package/dist/invariants/split-landing.d.ts +0 -8
  1017. package/dist/invariants/split-landing.js +0 -15
  1018. package/dist/tree-operations/list/terminator.d.ts +0 -17
  1019. package/dist/tree-operations/list/terminator.js +0 -60
  1020. package/dist/tree-operations/paste/replace-block-at-parent.d.ts +0 -31
  1021. package/dist/tree-operations/paste/replace-block-at-parent.js +0 -78
@@ -18,10 +18,10 @@ This is gonna be a long one, so here are the sections:
18
18
  | [The public surface](#the-public-surface) | What the package exports, and what the version number promises about it |
19
19
  | [Props](#props) | Every prop, and which ones you can change after mount |
20
20
  | [The instance surface](#the-instance-surface) | The methods on a mounted editor: read the document, move the caret, run commands |
21
- | [Events](#events) | The five channels an editor reports on, and what each one carries |
21
+ | [Events](#events) | The seven channels an editor reports on, and what each one carries |
22
22
  | [Presentation modes](#presentation-modes) | One document shown five ways, from raw Markdown to fully rendered |
23
23
  | [Images and links](#images-and-links) | Rewriting URLs, importing pasted images, and which URLs the editor refuses to load |
24
- | [Plugins](#plugins) | Installing plugins, why the whole app should share one set, and the nine that ship in the box |
24
+ | [Plugins](#plugins) | Installing plugins, why the whole app should share one set, and the ten that ship in the box |
25
25
  | [Theming](#theming) | The CSS variables the editor reads, and three ways to restyle it |
26
26
  | [Keyboard shortcuts](#keyboard-shortcuts) | Every shortcut, how to rebind or disable one, and which keys the editor swallows |
27
27
  | [Embedding in a host layout](#embedding-in-a-host-layout) | Letting your page scroll the editor, and putting your own content above the document |
@@ -65,7 +65,7 @@ A few things in the above example snippet are decently important; you might want
65
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.
66
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.
67
67
 
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.
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, or every installed one when it has no `plugins` prop ([Plugins](#plugins)); and `editor.__test.*` is internal and will move, so don't build on it.
69
69
 
70
70
  ## What your build needs
71
71
 
@@ -94,73 +94,88 @@ Three things. Though, prob already true in sveltekit apps (if thats you, skip ah
94
94
 
95
95
  Everything supported is exported from `@voithos-labs/aragonite`. Before 1.0 the public surface is still unstable, and the changelog records any change to it. From 1.0 onwards, a breaking change to the surface rides a major version, while additive needs ship as minors. Note, the list below will be (or at least attempted to be) kept up to date; for the actual list of exports see `src/lib/index.ts`.
96
96
 
97
- | Group | What you get |
98
- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
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; `CodeRunRequest`, `RunCodeHook`, `CodeMenuItem` and `CodeMenuItemsHook` for the code-block hooks |
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
- | **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
- | **Commands** | `TOOLBAR_COMMANDS`, the command ids a formatting toolbar calls through `runCommand` |
104
- | **Search** | `SearchState`, `SearchOptions`, `Match`: the find/replace controller, its options, and one hit |
105
- | **Decorations** | `DecorationRegistry` and the decoration types: what `getDecorations()` returns |
106
- | **Rects** | `EditorRects` (what `getRects()` returns: on-screen geometry over the document) and `SELECTION_END`, the value its range calls accept as "through the end of the block" |
107
- | **CST utilities** | `parse` / `serialize` for round-tripping Markdown outside the component (CST: the concrete syntax tree, the parsed form of a document); `parseInline`, `getContentRange`, `isProseKind` for inspecting a block's inline content and editable range |
108
- | **Node types** | `CstNode`, `Document`, the block-kind and inline-node unions, and the per-kind metadata shapes: the vocabulary for reading a parsed document. `NodeView` / `DocumentView` are their read-only forms; every node the editor hands you to read is typed as a view, so mutating the live tree is a compile error, not a convention |
109
- | **Events** | `EditorEvents` and the payload types the subscription surface emits |
110
- | **Diagnostics** | `EditorDiagnostics` (what `getDiagnostics()` returns) and `InteractionTraceEntry` |
97
+ | Group | What you get |
98
+ | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
99
+ | **Component** | `Editor`, plus `EditorProps` and `EditorInstance` (the prop shape and the `bind:this` surface), `SyntaxOptions` (the `syntax` prop's shape), `InsertMarkdownOptions` (`insertMarkdown`'s second argument), `PresentationMode` (the `presentationMode` values), and `BlockComponent` (what a mounted block exposes to the editor) |
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
+ | **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
+ | **Selection + keymap** | `EditorSelection` (what `getSelection()` returns) and `SelectionPoint` (one of its two endpoints), `normalizeSelection`, which puts a selection's two endpoints in document order; `KeybindingOverride` and `CommandId` (what the `keybindings` prop takes) |
103
+ | **Commands** | `TOOLBAR_COMMANDS`, the command ids a formatting toolbar calls through `runCommand` |
104
+ | **Search** | `SearchState`, `SearchOptions`, `Match`: the find/replace controller, its options, and one hit |
105
+ | **Decorations** | `DecorationRegistry` and the decoration types: what `getDecorations()` returns |
106
+ | **Rects** | `EditorRects` (what `getRects()` returns: on-screen geometry over the document) and `SELECTION_END` (with its type `SelectionEnd`), the value its range calls accept as "through the end of the block" |
107
+ | **Inline menus** | `InlineMenuRegistry` (what `getInlineMenus()` returns) and the shapes it takes and hands back: `InlineMenuSource`, `InlineMenuSourceHandle`, `InlineMenuItem`, `InlineMenuQuery`, `InlineMenuOpenOptions`, `InlineMenuRowProps` |
108
+ | **Insert catalogue** | `InsertEntry` (one row of `getInsertCatalogue()`), `MenuIconName` (the glyph names its `icon` takes), and `registerInsertEntry` for adding a row |
109
+ | **CST utilities** | `parse` / `serialize` for round-tripping Markdown outside the component (CST: the concrete syntax tree, the parsed form of a document), with `ParseScope` for `parse`'s scope option; `parseInline`, `getContentRange` (and its `ContentRange`), `isProseKind` for inspecting a block's inline content and editable range |
110
+ | **Node types** | `CstNode`, `Document`, the block-kind and inline-node unions, and the per-kind metadata shapes: the vocabulary for reading a parsed document. `NodeView` / `DocumentView` are their read-only forms; every node the editor hands you to read is typed as a view, so mutating the live tree is a compile error, not a convention |
111
+ | **Events** | `EditorEvents` and the payload types the subscription surface emits |
112
+ | **Diagnostics** | `EditorDiagnostics` (what `getDiagnostics()` returns) and `InteractionTraceEntry` |
111
113
 
112
114
  ## Props
113
115
 
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
- | `selectionToolbar` | The built-in formatting popover over a selection: the marks, the link, a heading picker, inline code and copy (default on; reading mode never shows it; see [Recipe: a selection toolbar](#recipe-a-selection-toolbar)) |
134
-
135
- **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.
136
-
137
- **Read live:** `theme`, `searchBar`, `searchBarAnchor`, `selectionToolbar`, `presentationMode`, and `keybindings` may change after mount, and `header` re-renders like any other Svelte snippet.
116
+ | Prop | What it does |
117
+ | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
118
+ | `source` | Seeds the document at mount, and re-seeds it whenever the prop changes; never two-way bound |
119
+ | `theme` | Theme name, reflected to `data-editor-theme` on the editor root: `'dark'` (default), `'light'`, or a name of your own (see [Theming](#theming)) |
120
+ | `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)) |
121
+ | `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)) |
122
+ | `syntax` | Switch a GFM syntax off for this editor: `{ indentedCode: false, setextHeading: false }` (see [Switching a syntax off](#switching-a-syntax-off)) |
123
+ | `keybindings` | Rebind or disable the editor's shortcuts, per instance (see [Rebinding chords](#rebinding-chords)) |
124
+ | `resolveImageUrl` | Rewrite a raw image URL before it reaches `img.src` (to resolve a relative path, say) |
125
+ | `resolveLinkUrl` | Rewrite a raw link destination at render time |
126
+ | `imageLoadPolicy` | `'auto'` (load images) or `'placeholder'` (defer loading) |
127
+ | `onLinkActivate` | Handle an activated link (Ctrl/Cmd+click while editing, plain click in reading mode) instead of the default `window.open` |
128
+ | `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)) |
129
+ | `onRunCode` | Execution hook for code blocks: installing it is what puts a run button on the rail of every editable code block (the controls a marker-hiding mode shows; reading mode shows no run button), and the editor runs nothing itself (see [Running a code block](#running-a-code-block)) |
130
+ | `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)) |
131
+ | `header` | Your own UI above the first block, rendered inside the editor's scroll container (see [The header slot](#the-header-slot)) |
132
+ | `scrollMode` | `'self'` (default: the editor scrolls itself) or `'host'` (an ancestor of yours scrolls it; see [Host scroll mode](#host-scroll-mode)) |
133
+ | `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 in a list of two or more, dividers, cards), never prose. `false` removes them, except on a picture; keyboard reorder (Alt+Arrow) and the cell menu need no opt-in |
134
+ | `searchBar` | The built-in find/replace bar and its Mod+F / Mod+H shortcuts (default on) |
135
+ | `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)) |
136
+ | `selectionToolbar` | The built-in formatting popover over a selection: the marks, the link, a heading picker, inline code and copy (default on; reading mode never shows it; see [Recipe: a selection toolbar](#recipe-a-selection-toolbar)) |
137
+
138
+ **Set once at mount:** `resolveImageUrl`, `resolveLinkUrl`, `imageLoadPolicy`, `onLinkActivate`, `onPasteImage`, `onRunCode`, `codeMenuItems`, `scrollMode`, `plugins`, and `syntax`. Set them at mount and leave them; a swap later isn't guaranteed to reach blocks that are already built.
139
+
140
+ **Read live:** `theme`, `searchBar`, `searchBarAnchor`, `selectionToolbar`, `blockDragHandles`, `presentationMode`, and `keybindings` may change after mount, and `header` re-renders like any other Svelte snippet.
141
+
142
+ ### Switching a syntax off
143
+
144
+ Two GFM syntaxes catch people out in a rendered view, because the markers that would explain them are hidden: a line that starts with a tab (or four spaces) becomes a code block, and `---` right under a line of text turns that line into a heading. An editor can leave either one out of its grammar:
145
+
146
+ ```svelte
147
+ <Editor {source} syntax={{ indentedCode: false, setextHeading: false }} />
148
+ ```
149
+
150
+ With `indentedCode: false`, an indented line is a paragraph and its indent is just whitespace. With `setextHeading: false`, a `===` line under text stays part of the paragraph and a `---` line is a divider, which is how GFM reads them once setext headings are out. Only the reading changes. A file that already holds the shape loads as those paragraphs and dividers, keeps every byte, and saves exactly as it came, so GitHub or an editor without the switch still reads its code blocks and headings.
138
151
 
139
152
  ## The instance surface
140
153
 
141
154
  You set the editor up with props when it mounts. After that you talk to it through the `bind:this` handle. Here's what you can read:
142
155
 
143
- | Method | What it answers |
144
- | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
145
- | `getSource()` | The live document, serialized back to Markdown |
146
- | `getSelection()` | A frozen snapshot of the current selection, or `null` |
147
- | `getBlockKindAt(path)` | What kind of block sits at a path, or `null` |
148
- | `canRunCommand(id)` / `isCommandActive(id)` | Whether a toolbar button should be enabled, and whether it should paint pressed (see [Toolbar commands](#toolbar-commands)) |
149
- | `getEvents()` | The subscription surface (see [Events](#events)) |
150
- | `getSearch()` | The find/replace controller (see [Driving search yourself](#driving-search-yourself)) |
151
- | `getRects()` | Where things are on screen (see [Screen geometry](#screen-geometry)) |
152
- | `getDecorations()` | The registry for your own view-only annotations (see [Decorations](#decorations)) |
153
- | `getDiagnostics()` | The bug-report tooling (see [Diagnostics](#diagnostics)) |
154
- | `reservedChords()` / `claimsChord(event)` | Which shortcuts this editor consumes (see [Which shortcuts the editor consumes](#which-shortcuts-the-editor-consumes)) |
156
+ | Method | What it answers |
157
+ | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
158
+ | `getSource()` | The live document, serialized back to Markdown |
159
+ | `getSelection()` | A frozen snapshot of the current selection, or `null` |
160
+ | `getBlockKindAt(path)` | What kind of block sits at a path, or `null` |
161
+ | `canRunCommand(id)` / `isCommandActive(id)` | Whether a toolbar button should be enabled, and whether it should paint pressed (see [Toolbar commands](#toolbar-commands)) |
162
+ | `getEvents()` | The subscription surface (see [Events](#events)) |
163
+ | `getSearch()` | The find/replace controller (see [Driving search yourself](#driving-search-yourself)) |
164
+ | `getRects()` | Where things are on screen (see [Screen geometry](#screen-geometry)) |
165
+ | `getDecorations()` | The registry for your own view-only annotations (see [Decorations](#decorations)) |
166
+ | `getInlineMenus()` | The registry for lists opened by a typed trigger (see [Recipe: a typed-trigger menu](#recipe-a-typed-trigger-menu)) |
167
+ | `getInsertCatalogue()` | The blocks the insert menus offer, plugin blocks included (see [Inserting Markdown at the caret](#inserting-markdown-at-the-caret)) |
168
+ | `getDiagnostics()` | The bug-report tooling (see [Diagnostics](#diagnostics)) |
169
+ | `reservedChords()` / `claimsChord(event)` | Which shortcuts this editor consumes (see [Which shortcuts the editor consumes](#which-shortcuts-the-editor-consumes)) |
155
170
 
156
171
  And what you can write:
157
172
 
158
- | Method | What it does |
159
- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
160
- | `setSelection(snapshot)` | Puts a `getSelection()` snapshot back on the document (see [Restoring a selection](#restoring-a-selection)) |
161
- | `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)) |
162
- | `insertMarkdown(md)` | Inserts Markdown at the caret, exactly as pasting it would (see [Inserting Markdown at the caret](#inserting-markdown-at-the-caret)) |
163
- | `runCommand(id, arg?)` | Runs an editor command by name, no keystroke involved (see [Toolbar commands](#toolbar-commands)) |
173
+ | Method | What it does |
174
+ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
175
+ | `setSelection(snapshot)` | Puts a `getSelection()` snapshot back on the document (see [Restoring a selection](#restoring-a-selection)) |
176
+ | `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)) |
177
+ | `insertMarkdown(md, options?)` | Inserts Markdown at the caret, or in a new paragraph below its block, exactly as pasting it would (see [Inserting Markdown at the caret](#inserting-markdown-at-the-caret)) |
178
+ | `runCommand(id, arg?)` | Runs an editor command by name, no keystroke involved (see [Toolbar commands](#toolbar-commands)) |
164
179
 
165
180
  ### Reading the document and the selection
166
181
 
@@ -183,7 +198,7 @@ editor.getBlockKindAt([99]); // null
183
198
 
184
199
  `getSelection(): EditorSelection | null`
185
200
 
186
- Returns a snapshot of the current selection (a copy, so changing it changes nothing), or `null` when the editor isn't focused.
201
+ Returns a snapshot of the current selection (a copy, so changing it changes nothing), or `null` when the editor isn't focused. While an image is selected whole, it returns a collapsed caret at the image's edge (its end, after a click): the next `insertMarkdown` or keystroke still replaces the image, and passing that value to `setSelection` puts a caret back there without selecting the image again.
187
202
 
188
203
  ```ts
189
204
  editor.getSelection();
@@ -193,7 +208,7 @@ editor.getSelection();
193
208
  // }
194
209
  ```
195
210
 
196
- `anchor` is where the selection started and `focus` is where it ends, so a plain caret has the two equal. `offset` is a character index into the block's source, with one exception: inside a table it's a cell index (row by row), and the point carries `cellCoordinate: true` to say so. Well, mostly. A selection lying wholly inside one table uses cell indices without the flag, so check the block's kind (`getBlockKindAt(anchor.path) === 'table'`) before you trust `offset` as a character. [The selection toolbar recipe](#recipe-a-selection-toolbar) shows this in place.
211
+ `anchor` is where the selection started and `focus` is where it ends, so a plain caret has the two equal. `offset` is a character index into the block's source, with one exception: inside a table it's a cell index (row by row), and the point carries `cellCoordinate: true` to say so. Both corners of a rectangle of cells inside one table get it too. So the flag's all you need to check before you trust `offset` as a character.
197
212
 
198
213
  ### Restoring a selection
199
214
 
@@ -207,7 +222,7 @@ const saved = editor.getSelection();
207
222
  const ok = await editor.setSelection(saved); // true when placed and in view
208
223
  ```
209
224
 
210
- `true` means placed **and** in view, the same way `scrollTo` answers ([Screen geometry](#screen-geometry)). [The insert toolbar recipe](#recipe-an-insert-toolbar) uses this stash-and-restore to survive a focus-stealing button.
225
+ `true` means placed **and** in view, the same way `scrollTo` answers ([Screen geometry](#screen-geometry)). A block already on screen doesn't move; one off screen scrolls in just far enough to sit at the nearest edge, and nothing holds it there afterwards. [The insert toolbar recipe](#recipe-an-insert-toolbar) uses this stash-and-restore to survive a focus-stealing button.
211
226
 
212
227
  `false` never throws, and covers three shapes:
213
228
 
@@ -219,15 +234,29 @@ const ok = await editor.setSelection(saved); // true when placed and in view
219
234
 
220
235
  Two notes on the third shape:
221
236
 
222
- - Since 0.9.36 it has one more trigger: a later programmatic reveal (your own `scrollTo` or `navigateTo`, or the find bar navigating) issued before this restore settles takes the viewport, and the restore stops competing rather than fighting the newer target. An ordinary user gesture is not that case; typing, clicking, or scrolling while a restore settles changes nothing about the outcome.
237
+ - It has one more trigger: a later programmatic reveal (your own `scrollTo` or `navigateTo`, or the find bar navigating) issued before this restore settles takes the viewport, and the restore stops competing rather than fighting the newer target. An ordinary user gesture is not that case; typing, clicking, or scrolling while a restore settles changes nothing about the outcome.
223
238
  - Branch on it as "the viewport did not end up where I asked", never as "nothing happened". Re-placing a fallback selection there would discard a caret that landed correctly.
224
239
 
225
240
  Out-of-range offsets clamp, each in its own coordinate space: a character offset clamps to the block's source length, and an endpoint addressing a table clamps to the last cell, so a huge offset there becomes the bottom-right cell rather than a character position.
226
241
 
242
+ A caret doesn't have to name the block it ends up in. Point it at offset 0 of a block that holds other blocks (a list at `[2]`, say) and it lands at the start of the list's first item, so the next key types there. A caret inside a closed `<details>` lands at the end of its title row, and the block stays closed.
243
+
244
+ You can leave the flag off when you build a selection by hand. An offset on a table's path always counts cells, so plain numbers there paint the rectangle with those two cells at its corners, and `getSelection()` hands it back flagged:
245
+
246
+ ```ts
247
+ // a two-column table at [3]; cells count row by row from the header, so 5 is the third row's right cell
248
+ await editor.setSelection({ anchor: { path: [3], offset: 0 }, focus: { path: [3], offset: 5 } });
249
+ editor.getSelection();
250
+ // {
251
+ // anchor: { path: [3], offset: 0, cellCoordinate: true },
252
+ // focus: { path: [3], offset: 5, cellCoordinate: true }
253
+ // }
254
+ ```
255
+
227
256
  What `selectionChange` reports while a restore runs:
228
257
 
229
258
  - **On success, every emission carries the restored selection.** The editor holds the channel until the state write and the caret landing have both happened, so a handler that treats the first event as authoritative (a persist-on-change host, say) saves the right one. Reading back with `getSelection()` after the await is still correct, just no longer necessary.
230
- - **The browser's own `selectionchange` may still deliver a trailing duplicate** of the same value, so make the handler idempotent rather than counting events.
259
+ - **One emission, not two.** The editor announces the restored selection itself, so the browser's own `selectionchange` that follows has nothing new to report and is dropped. Make the handler idempotent anyway: the count is not part of the contract.
231
260
  - **The failed-placement `false` is the exception.** A collapsed or within-block restore into a resolvable-but-unmounted block clears the old selection and then finds no element, so its one emission reports what was there before. Treat a `false` restore as "read the selection back", not as an authoritative event.
232
261
 
233
262
  ### Placing the caret at a point
@@ -250,14 +279,14 @@ Two things about where a point lands:
250
279
 
251
280
  ### Inserting Markdown at the caret
252
281
 
253
- `insertMarkdown(md: string): boolean`
282
+ `insertMarkdown(md: string, options?: { placement?: 'caret' | 'below' }): Promise<boolean>`
254
283
 
255
284
  Inserts Markdown at the caret the way a paste would. `md` is any Markdown string, `**hi**` or a whole table. The usual caller is a toolbar button inserting a canned snippet; [the insert toolbar recipe](#recipe-an-insert-toolbar) is built on this call.
256
285
 
257
286
  ```ts
258
- editor.insertMarkdown('**hi**'); // true
259
- editor.insertMarkdown('| a | b |\n| --- | --- |\n| | |\n'); // true, and a table lands as a block
260
- editor.insertMarkdown('**hi**'); // false with no caret (reading mode, or focus outside the editor)
287
+ await editor.insertMarkdown('**hi**'); // true
288
+ await editor.insertMarkdown('| a | b |\n| --- | --- |\n| | |\n'); // true, and a table lands as a block
289
+ await editor.insertMarkdown('**hi**'); // false with no caret (reading mode, or focus outside the editor)
261
290
  ```
262
291
 
263
292
  One call runs the whole paste route:
@@ -266,7 +295,11 @@ One call runs the whole paste route:
266
295
  2. A live selection is deleted, then the text is spliced in the way a paste would pick: a table as a block, a one-liner inline at the caret, list items absorbed into a matching list.
267
296
  3. Focus lands at the end of the insertion, and the whole thing is one undo entry.
268
297
 
269
- `false` means nothing changed: no caret in this editor, reading mode, or a caret parked between two blocks. `true` means the pipeline took the text, not that the edit has landed yet, so read the result off the `edit` channel rather than calling `getSource()` on the next line.
298
+ The caret is read when you call, and the promise resolves once the edit has landed and the caret is placed, so `getSource()` after an `await` sees it. `false` means nothing changed: no caret in this editor, reading mode, or a caret parked between two blocks.
299
+
300
+ `placement: 'below'` is what the right-click "Insert block" rows do: an empty paragraph goes in after the top-level block holding the caret, and the text is pasted into it, so a sentence is never split around a new block. The paragraph and the insert are one undo entry.
301
+
302
+ `getInsertCatalogue()` lists the blocks those rows offer, in their order: an `id`, a `label`, an `icon` name, `keywords`, the `markdown` to hand this call, and on some an optional `withArgument(word)` that answers the Markdown for a typed argument (`/table 3x4`). A plugin's block is in the list while this editor lists its plugin, so a `+` button or a menu of your own shows it without naming it.
270
303
 
271
304
  ### Toolbar commands
272
305
 
@@ -284,7 +317,7 @@ editor.runCommand('nope'); // false, unknown id, nothing changed
284
317
 
285
318
  The ids you can pass:
286
319
 
287
- - **`TOOLBAR_COMMANDS`** (exported from the package) has what a selection toolbar needs: `toggleStrong`, `toggleEmphasis`, `toggleStrikethrough`, `toggleCode`, `editLink`, and `setHeading`. The rest of the built-in commands stay internal for now.
320
+ - **`TOOLBAR_COMMANDS`** (exported from the package) has what a selection toolbar needs: `toggleStrong`, `toggleEmphasis`, `toggleStrikethrough`, `toggleCode`, `editLink`, and `setHeading`. Any other id in the exported `CommandId` union runs too (`history.undo`, `block.moveUp`, and so on); those just don't get a named constant.
288
321
  - **`setHeading`, with a level.** The arm behind `Mod+0` to `Mod+6`: `runCommand(TOOLBAR_COMMANDS.setHeading, 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. A heading level belongs to one block, so over a selection spanning blocks it declines rather than guessing which block you meant.
289
322
  - **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.
290
323
 
@@ -293,7 +326,7 @@ The ids you can pass:
293
326
  What the boolean means:
294
327
 
295
328
  - **`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()`.
296
- - **`false` means nothing changed**: an unknown id, reading mode, a command that needs a focused block when none is, or the link editor or a heading level over a selection spanning blocks (a link lives inside one block, a heading level is one block's, and a range across blocks gives them none).
329
+ - **`false` means nothing changed**: an unknown id, reading mode, a command that needs a focused block when none is, a text command on a block that can't run one, a plugin command whose plugin this editor didn't list, or the link editor or a heading level over a selection spanning blocks (a link lives inside one block, a heading level is one block's, and a range across blocks gives them none).
297
330
 
298
331
  Two more things before you wire buttons:
299
332
 
@@ -302,7 +335,16 @@ Two more things before you wire buttons:
302
335
 
303
336
  `canRunCommand(commandId: string): boolean`
304
337
 
305
- Tells you whether `runCommand(id)` would reach the command right now, which is what greys a toolbar button out instead of hiding it. It answers `false` exactly where `runCommand` declines before dispatch: an unknown id, reading mode, a block-scoped id with nothing focused, and the link editor or a heading level while the selection spans blocks. `true` means reachable, not that it'll write (across blocks it may find no block that can hold the mark), so keep reading `runCommand`'s boolean too.
338
+ Tells you whether `runCommand(id)` would reach the command right now, which is what greys a toolbar button out instead of hiding it. It answers `false` exactly where `runCommand` declines before dispatch:
339
+
340
+ - an unknown id
341
+ - reading mode
342
+ - a block-scoped id with nothing focused
343
+ - a text command on a block that can't run one (a divider, a diagram, a formula)
344
+ - a plugin command whose plugin this editor didn't list
345
+ - the link editor or a heading level while the selection spans blocks
346
+
347
+ `true` means reachable, not that it'll write (across blocks it may find no block that can hold the mark), so keep reading `runCommand`'s boolean too.
306
348
 
307
349
  ```ts
308
350
  // with a selection spanning two paragraphs
@@ -341,16 +383,17 @@ const off = events.on('edit', (e) => console.log(e.op, e.path));
341
383
  off();
342
384
  ```
343
385
 
344
- Six channels:
386
+ Seven channels:
345
387
 
346
- | Channel | Fires |
347
- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
348
- | `edit` | After every applied edit: a structural operation, a batch of typing (consecutive keystrokes flush as one), an undo or redo |
349
- | `selectionChange` | Whenever the selection changes; the payload is the snapshot, or `null` |
350
- | `error` | On a failure the editor contained rather than threw |
351
- | `presentationModeChange` | After a `presentationMode` prop change; the payload is the effective mode (never at mount) |
352
- | `themeChange` | After a `theme` prop change; the payload is the theme name (never at mount) |
353
- | `menuChange` | `true` when an editor-owned menu (right-click, insert `+`) opens and `false` when it closes; hide selection chrome meanwhile |
388
+ | Channel | Fires |
389
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
390
+ | `edit` | After every applied edit: a structural operation, a batch of typing (consecutive keystrokes flush as one), an undo or redo |
391
+ | `selectionChange` | Whenever the selection changes; the payload is the snapshot, or `null` |
392
+ | `error` | On a failure the editor contained rather than threw |
393
+ | `presentationModeChange` | After a `presentationMode` prop change; the payload is the effective mode (never at mount) |
394
+ | `themeChange` | After a `theme` prop change; the payload is the theme name (never at mount) |
395
+ | `sourceSwap` | After a `source` prop write replaces the whole document; the payload is `{ generation }` (never at mount, and never on `edit`) |
396
+ | `menuChange` | `true` when an editor-owned menu or popover opens and `false` when the last one closes (not the selection toolbar's own flyout, nor a view a plugin draws); hide selection chrome meanwhile |
354
397
 
355
398
  Events fire synchronously from wherever they happen, and **a handler must not edit the document**: reentrant edits aren't supported.
356
399
 
@@ -365,9 +408,11 @@ events.on('edit', (e) => e);
365
408
  // { op: 'delete', path: [1], detail: { crossBlock: true }, timestamp: 1788390004120 }
366
409
  ```
367
410
 
368
- `path` is document-absolute for every operation, nested ones and the typing flush included: it walks from the document root to the block that was operated on. One event names one path even when the write spanned several blocks; a `delete` or `updateContent` that did carries `detail.crossBlock: true`, and a host that reconciles incrementally should re-read the whole affected range on those rather than just `path`.
411
+ A typing flush's `byteLength` counts every keystroke in the burst, at any depth. That includes a last key that changed its block's kind (the space that makes `#` a heading, say), which also fires its own `updateContent` right after the flush.
412
+
413
+ `path` is document-absolute for every operation, nested ones and the typing flush included: it walks from the document root to the block that was operated on. One event names one path even when the write spanned several blocks; a `delete`, `updateContent`, `tableDeleteRow` or `tableDeleteColumn` that did carries `detail.crossBlock: true`, and a host that reconciles incrementally should re-read the whole affected range on those rather than just `path`.
369
414
 
370
- **`selectionChange`** carries the `EditorSelection` snapshot, or `null` when nothing is focused.
415
+ **`selectionChange`** carries the `EditorSelection` snapshot, or `null` when nothing is focused. While an image is selected whole, the snapshot is a collapsed caret at the image's edge (its end, after a click): the next `insertMarkdown` or keystroke still replaces the image, and `setSelection` of that snapshot puts a caret back without selecting the image again.
371
416
 
372
417
  ```ts
373
418
  events.on('selectionChange', (sel) => sel);
@@ -377,28 +422,36 @@ events.on('selectionChange', (sel) => sel);
377
422
 
378
423
  Read the value the channel settles on rather than counting emissions. Most changes emit once, but a caret landing between two blocks emits a short burst, and its last value is `null`, since a between-blocks caret sits outside the public selection shape. Focus leaving the editor reads `null` too, even where the browser's own range survives unfocused, so a button greyed off this channel can't go stale when the user clicks out.
379
424
 
425
+ When it fires is worth knowing if your handler cares which block the caret just arrived in:
426
+
427
+ - **A caret the editor puts down itself** (the paragraph an Enter creates, the block a merge leaves you in, the block an arrow takes you to) is announced right at the placement, before anything can be typed there. So a handler never sees that block's first bytes ahead of the arrival.
428
+ - **A click** is announced at the click too, so a byte typed straight after it can't arrive first.
429
+ - **Anything else the browser moves** (a move inside one block, mostly) is reported a task later, from the browser's own `selectionchange`. What arrives late there is the new offset, not a new block.
430
+
380
431
  **`error`** carries an `EditorError`, `{ origin, error, context? }`.
381
432
 
382
433
  ```ts
383
434
  events.on('error', (err) => err);
384
435
  // { origin: 'link', error: Error('aragonite: blocked link with disallowed scheme: file:///notes.md'), context: { url: 'file:///notes.md' } }
385
- // { origin: 'command', error: TypeError(...), context: { kind: 'paragraph', command: 'my.command', plugin: 'my-plugin' } }
436
+ // { origin: 'command', error: TypeError(...), context: { kind: 'admonition', command: 'admonition.cycleKind', plugin: 'admonitions' } }
386
437
  ```
387
438
 
388
439
  `origin` is one of `subscriber`, `render`, `commit`, `command`, `decoration`, `clipboard`, or `link`, and `context` carries what's known for it:
389
440
 
390
- | Origin | `context` |
391
- | ------------ | -------------------------------------------------------------- |
392
- | `render` | `path` of the block |
393
- | `commit` | `op` and `path` |
394
- | `command` | `kind`, `command`, and `plugin` when a plugin owns the command |
395
- | `decoration` | `source`, the decoration source's name |
396
- | `clipboard` | `path` the paste was aimed at, when it was aimed at a range |
397
- | `link` | `url` the editor refused |
398
- | `subscriber` | nothing; one of your own handlers threw |
441
+ | Origin | `context` |
442
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------- |
443
+ | `render` | `path` of the block |
444
+ | `commit` | `op` and `path` |
445
+ | `command` | `kind`, `command`, and `plugin` when a plugin owns the command |
446
+ | `decoration` | `source`, the decoration source's name; `path` instead when a mark's `interactive.onClick` threw |
447
+ | `clipboard` | `path` the paste was aimed at, when it was aimed at a range |
448
+ | `link` | `url` the editor refused |
449
+ | `subscriber` | nothing when one of your own handlers threw; `plugin` when a plugin's callback threw; `source` when an inline menu source threw |
399
450
 
400
451
  **`presentationModeChange`** and **`themeChange`** carry bare values (`'reading'`, `'light'`), not envelopes, and never fire at mount. Only plugin content that paints its own colors needs `themeChange`; anything styled through the tokens rethemes itself through the CSS cascade.
401
452
 
453
+ **`sourceSwap`** carries `{ generation }`, how many times a `source` write has replaced the document since mount (the first swap is 1). It fires once the new document, its cleared selection and its link references are all in place, so a handler reading `getSource()` sees the new document. A swap is not an edit: it fires nothing on `edit`, so a host that marks a document dirty on `edit` never hears its own `source` write echoed back.
454
+
402
455
  ## Presentation modes
403
456
 
404
457
  `presentationMode` dials one document from the raw side to the rendered side, and you can switch it at runtime like `theme`. Every mode is CSS over the same render path: the bytes and the offsets are the source document's in all of them.
@@ -413,7 +466,7 @@ events.on('error', (err) => err);
413
466
 
414
467
  **`'source'`** is what you get by default: every Markdown marker renders, dimmed, and everything is editable.
415
468
 
416
- **`'reading'`** is a rendered reading view, and it writes no bytes. Markers are hidden by CSS (the document and its offsets are untouched), inline widgets (an image, a rendered emoji) draw, and list bullets and numbers show as rendered chrome (chrome: what the editor paints around the text, not bytes in the document). Blocks aren't `contenteditable` here, so there's no caret inside a block and you move around by mouse, the same deal as other reading views (Obsidian's reading mode has no caret either).
469
+ **`'reading'`** is a rendered reading view, and it writes no bytes. Every edit is refused, whatever asked for it (your own `source` write still replaces the document, though). Switching into it counts as leaving the block the caret was in, same as clicking elsewhere: an edit still open there gets committed first, and an empty heading the caret sat in turns back into a blank line. So yes, the switch itself can emit `edit`. Markers are hidden by CSS (the document and its offsets are untouched), inline widgets (an image, a rendered emoji) draw, and list bullets and numbers show as rendered chrome (chrome: what the editor paints around the text, not bytes in the document). Blocks aren't `contenteditable` here, so there's no caret inside a block and you move around by mouse, the same deal as other reading views (Obsidian's reading mode has no caret either).
417
470
 
418
471
  - Inert: typing, paste, cut, Enter and Backspace, undo and redo, block commands, checkbox toggles, drag handles, table structure edits.
419
472
  - Still live: text selection, copy (the rendered text, markers excluded), scrolling, find (not replace), and links, which open on plain click since there's no caret to place.
@@ -432,24 +485,33 @@ events.on('error', (err) => err);
432
485
  - Where two constructs meet at one boundary (`**a***b*`) both reveal and a keystroke inserts between them. Walking left into a construct's opening markers reaches them, and `Home` lands at the first visible position, just inside.
433
486
  - A focused list item keeps its bullet or number as rendered chrome here (`preview-block` shows it as source), and escapes (`\`) and hard line breaks reveal whenever their block is focused, not by caret proximity.
434
487
 
435
- **`'live'`** is the rendered end that's still fully editable. Where `preview-inline` reveals the construct under the caret, live reveals nothing: `**bold**` renders as bold whether the caret is inside it or not, a heading with a word behind it shows no `## `, and a link shows its text with the destination out of sight. What does stay on screen is chrome with nothing behind it: a construct with no content (a bare `# `, an empty fence) keeps its markers dimmed so the block stays visible and editable, and the first character of content folds them away. Everything a source-mode caret can do still works: typing, selection, `Enter`, `Backspace`, undo, search and replace, tables, drag handles, plugins.
488
+ **`'live'`** is the rendered end that's still fully editable. Where `preview-inline` reveals the construct under the caret, live reveals nothing: `**bold**` renders as bold whether the caret is inside it or not, a heading with a word behind it shows no `## `, and a link shows its text with the destination out of sight. Everything a source-mode caret can do still works: typing, selection, `Enter`, `Backspace`, undo, search and replace, tables, drag handles, plugins. A few things do stay on screen, or show up for a moment:
436
489
 
437
- 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:
438
-
439
- - **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.
440
- - **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.
441
- - **`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.
442
- - **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.
443
- - **The format toggles work at a collapsed caret.** `Mod+B`, `Mod+I`, `Mod+Shift+X`, and `Mod+E` over a selection wrap or unwrap it as always; at a caret they arm the format for the next thing you type, which is what a mode with no visible delimiters needs. A selection ending on a space wraps the word and leaves the space beside it (a run closing against whitespace is no run at all), and a press whose wrap the screen wouldn't survive writes nothing rather than printing delimiters you can't see to delete.
490
+ - A construct with no content (a bare `# `, an empty fence) keeps its markers dimmed so the block stays visible and editable, and the first character of content folds them away.
491
+ - A keystroke that turns its block into another kind (a tab that makes a code block, `# ` that makes a heading) names the new kind at the block's corner for a moment, and a screen reader hears it once. The preview modes do the same.
492
+ - A hard line break whose backslash or trailing spaces don't show draws a dimmed `↵` where they are.
444
493
 
445
- Three more live-mode facts:
494
+ Three things behave differently from what the screen might suggest:
446
495
 
447
496
  - **Reading a link's destination.** The link card is the only place a URL shows in this mode. `Mod+K` with the caret inside a link opens it with focus in the URL field, a click on a link opens the same card beside a caret that stays the document's, and editing the URL commits as one undoable step.
448
497
  - **Copy yields the source bytes** (`**bold**`, not `bold`), because the caret's offsets are the source's. Reading mode is the one mode that copies the rendered text, since it has no caret and nothing to paste back into.
449
498
  - **Search matches the source bytes too**, so a query spanning a construct boundary misses what the screen appears to show: `beta gamma` finds nothing in `**beta** gamma`, where the bytes between the words are `** `. Matches inside a construct's own text work normally.
450
499
 
500
+ <details>
501
+ <summary>How live mode edits behind markers you can't see</summary>
502
+
503
+ 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:
504
+
505
+ - **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.
506
+ - **A caret placed at an extreme lands outside.** `Home`, `End`, and collapsing a selection put the caret past the delimiters, not between them. Placing a caret isn't a step, so the direction of the key that placed it doesn't decide the side.
507
+ - **`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.
508
+ - **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.
509
+ - **The format toggles work at a collapsed caret.** `Mod+B`, `Mod+I`, `Mod+Shift+X`, and `Mod+E` over a selection wrap or unwrap it as always; at a caret they arm the format for the next thing you type, which is what a mode with no visible delimiters needs. A selection ending on a space wraps the word and leaves the space beside it (a run closing against whitespace is no run at all), and a press whose wrap the screen wouldn't survive writes nothing rather than printing delimiters you can't see to delete. Deleting back through everything a chord just wrapped arms the format again, so the next character is still bold and a second press still turns it off.
510
+
451
511
  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.
452
512
 
513
+ </details>
514
+
453
515
  **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.
454
516
 
455
517
  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.
@@ -484,15 +546,16 @@ For the curious, where the Markdown lands when the user moves the caret during a
484
546
 
485
547
  ### Running a code block
486
548
 
487
- 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.
549
+ The editor runs nothing. `onRunCode` is the hook that says your app can: installing it puts a run button on the rail of every editable code block (the top-right controls a marker-hiding mode shows on hover or with the caret inside, so source mode has none, and reading mode shows no run button), and pressing it hands you the block, then everything after is yours: the engine, the result, and where the output goes.
488
550
 
489
551
  ```svelte
490
552
  <Editor
491
553
  {source}
492
- onRunCode={({ code, info, path }) => {
493
- // code: the fence body alone, never the fence lines; info: the whole info string
494
- // ("py {1-3}"); path: child indices from the document root to the block.
495
- runInMyKernel(code, info.split(/\s+/)[0]).then((out) => showOutputBeside(path, out));
554
+ onRunCode={({ code, language, path }) => {
555
+ // code: the fence body alone, never the fence lines; language: the info string's first
556
+ // word ("py" for "py {1-3}"), the same one the rail shows, or "" for none; the whole info
557
+ // string rides along as `info`. path: child indices from the document root to the block.
558
+ runInMyKernel(code, language).then((out) => showOutputBeside(path, out));
496
559
  }}
497
560
  codeMenuItems={(request) => [
498
561
  { id: 'clear', label: 'Clear output', run: () => clearOutput(request.path) },
@@ -507,10 +570,10 @@ The editor runs nothing. `onRunCode` is the hook that says your app can: install
507
570
 
508
571
  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.
509
572
 
510
- | Where | Admitted schemes |
511
- | --------- | -------------------------------- |
512
- | `img` src | `http`, `https`, `data`, `asset` |
513
- | link href | `http`, `https`, `mailto`, `tel` |
573
+ | Where | Admitted schemes |
574
+ | --------- | ---------------------------------------- |
575
+ | `img` src | `http`, `https`, `data`, `asset` |
576
+ | link href | `http`, `https`, `mailto`, `tel`, `xmpp` |
514
577
 
515
578
  A URL with no scheme at all (relative, fragment) is admitted at both. The two sets differ on purpose: `asset:` hands bytes to an `<img>`, and nothing has asked to navigate to one, so the same URL that renders as an image is refused as a link destination.
516
579
 
@@ -531,16 +594,16 @@ Plugins teach the editor new block and inline kinds. Writing one is the [plugin
531
594
  <Editor {source} {plugins} />
532
595
  ```
533
596
 
534
- Plugins install once at mount, in array order, before the first parse. Build the array once, in a shared module, and pass that same array to every `<Editor>` in your app (why is under "one plugin set per app" below). An inline array in the markup re-creates the plugins on every render, which is harmless (you get a dev-build warning) but noise you don't need.
597
+ Plugins install once at mount, in array order, before the first parse. Build the array once, in a shared module, and pass that same array to every `<Editor>` in your app (why is under "one plugin set per app" below). An inline array in the markup builds fresh plugin units for every editor that mounts, and each one after the first is a different unit under a name that's already installed: harmless, but a dev-build warning every time.
535
598
 
536
599
  **Installation is process-global.** The grammar (the kinds, their components, their parsing rules, their commands) is one shared set per JavaScript context, the way `customElements` is, so registering the same kind twice is a conflict rather than a per-instance override. Runtime state is per instance: selection, undo history, and every cache are one editor's own, and nothing one instance does reaches another. Mounting several editors on one page is fine; they share one grammar and never any state. Three consequences:
537
600
 
538
- - **Passing the same plugin to two editors registers it once.** Per-instance configuration still works: an entry may be `{ plugin, options }` instead of a bare plugin, and each editor gets its own `options` even though the registration is shared (the split-pane case). Reach for this over the plugin's own factory argument for anything two editors would vary, because a factory argument only takes effect on the first install.
539
- - **The prop is the enablement set.** Registration is shared; activation is per editor. An editor runs the hooks, resolves the kinds, answers the commands and their chords, and applies the paste transforms of exactly the plugins it lists, so leaving one out of an editor's array switches it off for that editor. Its blocks still parse (the seed parse reads the whole grammar) and then render as plain editable source, which is the same fallback an unknown kind gets. Two things aren't scoped yet: a plugin's inline syntax and directive names still reach every editor, and the chords a plugin's own block types define are still listed by `reservedChords()` in an editor that left the plugin out. That is an over-report, not a swallowed key, since those blocks never render there. An editor mounted with no `plugins` prop is the exception: it activates everything installed in the process.
601
+ - **Passing the same plugin to two editors registers it once.** Per-instance configuration still works: an entry may be `{ plugin, options }` instead of a bare plugin, and each editor gets its own `options` even though the registration is shared (the split-pane case). Reach for this over the plugin's own factory argument for anything two editors would vary, because a factory argument only takes effect on the first install. An entry doesn't have to spell out everything, either: each field it names replaces the plugin's default for that field (a list included), and the rest keep their defaults.
602
+ - **The prop is the enablement set.** Registration is shared; activation is per editor. An editor runs the hooks, resolves the kinds, answers the commands and their chords, and applies the paste hooks of exactly the plugins it lists, so leaving one out of an editor's array switches it off for that editor. Its syntax isn't in that editor's grammar either, so its blocks read as the plain Markdown they are, from the first parse on and after every edit. The same goes for the rest of it: its inline syntax stays text, its inline widgets show their source, its directive names open the generic directive block, and a paste into one of its blocks takes the default paste. An editor mounted with no `plugins` prop (or an empty array) is the exception: it activates everything installed in the process.
540
603
  - **A later editor may mount carrying a plugin an earlier one never had.** The late install is legal and serves the new editor's own parse; an editor that already parsed doesn't re-parse against the newer grammar, and a dev-build warning names the late registration.
541
604
  - **For a `parse()` pipeline with no `<Editor>` mounted**, call `installPlugins(plugins)` from the package to make the grammar live.
542
605
 
543
- **One plugin set per app, not per route.** Installation is first-wins: the first set to install decides the grammar for the whole process, and a later route's different set is ignored with a dev-build warning. Under SSR, first-wins turns per-route sets into a hydration hazard:
606
+ **One plugin set per app, not per route.** Installation is first-wins per plugin name: once a plugin is installed, a later set's own definition of it (its factory options included) is ignored with a dev-build warning (a plugin new to the process still installs late, as above). Under SSR, that turns per-route sets into a hydration hazard:
544
607
 
545
608
  1. The server process outlives a request, so whichever route it happened to render first decided the server's grammar.
546
609
  2. Each browser load starts fresh, so the client decides its grammar from the route it actually loaded.
@@ -552,7 +615,7 @@ The same rule covers [directive](directives.md) names. A plugin that claims an a
552
615
 
553
616
  ### Bundled plugins
554
617
 
555
- Nine first-party plugins ship in the package as subpath exports. Install them like any other plugin:
618
+ Ten first-party plugins ship in the package as subpath exports. Install them like any other plugin:
556
619
 
557
620
  ```ts
558
621
  import { admonitionsPlugin } from '@voithos-labs/aragonite/plugins/admonitions';
@@ -564,19 +627,21 @@ import { highlightOccurrencesPlugin } from '@voithos-labs/aragonite/plugins/high
564
627
  import { latexPlugin } from '@voithos-labs/aragonite/plugins/latex';
565
628
  import { mermaidPlugin } from '@voithos-labs/aragonite/plugins/mermaid';
566
629
  import { parrotPlugin } from '@voithos-labs/aragonite/plugins/parrot';
630
+ import { slashCommandsPlugin } from '@voithos-labs/aragonite/plugins/slash-commands';
567
631
  ```
568
632
 
569
- | Plugin | What it teaches the editor |
570
- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
571
- | `admonitionsPlugin()` | `:::name` directive callouts and native GitHub alerts (`> [!NOTE]` blockquotes) render as styled boxes, GitHub bytes untouched |
572
- | `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 |
573
- | `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 |
574
- | `footnotesPlugin()` | GFM footnotes: `[^label]: content` definitions render as an editable block, and `[^label]` references render as superscript numbers in first-reference order |
575
- | `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 |
576
- | `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 |
577
- | `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 |
578
- | `mermaidPlugin({ renderer? })` | A ` ```mermaid ` fence renders as a diagram through an injected engine; without one, the fence renders statically (the source, styled) |
579
- | `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 |
633
+ | Plugin | What it teaches the editor |
634
+ | --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
635
+ | `admonitionsPlugin()` | `:::name` directive callouts and native GitHub alerts (`> [!NOTE]` blockquotes) render as styled boxes, GitHub bytes untouched |
636
+ | `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 |
637
+ | `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 |
638
+ | `footnotesPlugin()` | GFM footnotes: `[^label]: content` definitions render as an editable block, and `[^label]` references render as superscript numbers in first-reference order |
639
+ | `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 |
640
+ | `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 |
641
+ | `latexPlugin({ renderer?, blockLayout? })` | All three GitHub math forms through one injected engine: inline `$…$`, block `$$…$$`, and the fenced ` ```math ` form; without an engine each formula shows its source, and 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 |
642
+ | `mermaidPlugin({ renderer? })` | A ` ```mermaid ` fence renders as a diagram through an injected engine; without one, the fence renders statically (the source, styled) |
643
+ | `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 |
644
+ | `slashCommandsPlugin({ entries?, exclude? })` | `/` at the start of a line or after a space opens a list of blocks to insert and headings to turn the line into; nothing is added to the document's syntax, so uninstalled, `/` is just a character |
580
645
 
581
646
  A few of them take options or need a word more.
582
647
 
@@ -592,7 +657,16 @@ const plugins = [{ plugin: tocPlugin(), options: { maxDepth: 3 } satisfies TocOp
592
657
 
593
658
  Two editors in one process can list different depths this way; the factory form, `tocPlugin({ maxDepth: 3 })`, is the default for an instance that declares none. The `satisfies TocOptions` is there because `options` is `unknown` to the editor, and with it a typo or an out-of-range level stays a compile error. At runtime anything that isn't a level from 1 to 6 falls back to the factory value.
594
659
 
595
- **Footnotes.** A reference jumps to its definition, on plain click in reading mode and on Ctrl/Cmd+click elsewhere (the same gesture links take); a plain click in an editing mode still opens the reference's source to edit. The definition's own `[^label]` marker is the way back, on the same gesture, and it lands the caret right after the first citation. Backspace at the start of a note's body unwraps it: the first block lifts out and the marker stays on whatever's left. One clipboard consequence: copying part of a single-paragraph definition's body carries its `[^label]: ` marker along (the marker is that block's own source, and a slice without it would re-parse as a bare paragraph), so pasting that slice elsewhere lands a second definition under the same label.
660
+ **Footnotes.** A reference jumps to its definition, on plain click in reading mode and on Ctrl/Cmd+click elsewhere (the same gesture links take); a plain click in an editing mode still opens the reference's source to edit. The definition's own `[^label]` marker is the way back, on the same gesture, and it lands the caret right after the first citation. In reading mode both are links a keyboard reaches with Tab and follows with Enter; the editing modes give them no tab stop. Backspace at the start of a note's body unwraps it: the first block lifts out and the marker stays on whatever's left. One clipboard consequence: copying part of a single-paragraph definition's body carries its `[^label]: ` marker along (the marker is that block's own source, and a slice without it would re-parse as a bare paragraph), so pasting that slice elsewhere lands a second definition under the same label.
661
+
662
+ **Slash commands.** Type `/` at the start of a line or after a space and a list opens under the caret: every block the right-click "Insert block" menu offers (your plugins' blocks included), then Heading 1 to 3. Typing narrows it by label or keyword (`/td` finds the to-do list), Enter picks, and Escape closes it and leaves what you typed. On an empty line the block replaces the line. On a line with text, the `/query` goes and the block lands below it, except a heading row, which turns the line itself into a heading. It never opens in a table cell. Two built-in rows take a word after a space: `/code js` opens a fence tagged `js`, and `/table 3x4` makes three columns by four rows. A plugin's block can take one too, if its insert entry declares [`withArgument`](plugin-api.md#insert-catalogue). `Mod+/` types the `/` for you, and `runCommand('slashCommands.open', 'table')` opens the list already narrowed, which is how a toolbar button or a touch UI gets there.
663
+
664
+ The two options:
665
+
666
+ - `entries` adds rows of your own, listed after the built-in ones. A row either inserts Markdown (`insert`) or runs a function (`run`, handed the plugin's editor context and, if the row sets `takesArgument: true`, the word typed after a space). A row whose id matches a built-in one hides that built-in, and yours shows up with your other rows.
667
+ - `exclude` hides built-in rows by id: `bullet`, `numbered`, `todo`, `quote`, `divider`, `code`, `table`, `h1`, `h2`, `h3`, plus whatever ids your installed plugins add to `getInsertCatalogue()` (`math`, `note`).
668
+
669
+ Options passed to the factory are every editor's defaults, and an editor's `{ plugin, options }` entry swaps out only the fields it names. So an entry of `{ exclude: ['table'] }` still lists the factory's `entries`, and `{ entries: [] }` is how one editor drops them. A row with both `insert` and `run`, or neither, is refused: the factory throws, and in an editor's entry the editor reports it on the `error` event and keeps the factory's options.
596
670
 
597
671
  **Math and diagrams.** latex and mermaid render through injected engines that never ride the main bundle: each has a `/renderer` subpath adapter, and its engine (`katex` / `mermaid`) is an optional peer dependency you install only if you use it.
598
672
 
@@ -604,7 +678,7 @@ latexPlugin({ renderer: katexRenderer });
604
678
  mermaidPlugin({ renderer: mermaidRenderer });
605
679
  ```
606
680
 
607
- The two differ on whether the renderer is required, on purpose. Math without a renderer has no honest fallback (a formula would render as nothing), so `latexPlugin` requires one at the type level. A mermaid block without an engine still has a useful static form (the fenced source, styled), so `mermaidPlugin()` is legal and renders statically; supply the renderer when you want live diagrams. The latex adapter imports `katex/dist/katex.min.css` on your behalf (it's the one bundled-plugin module with a side effect); no other setup is needed.
681
+ Both renderers are optional. Without one, math shows each formula's source in the code font, and says there's no renderer when you hover it, while a mermaid block shows its fenced source, styled, with a note. Supply the renderer when you want the real thing. The latex adapter imports `katex/dist/katex.min.css` on your behalf (it's the one bundled-plugin module with a side effect); no other setup is needed.
608
682
 
609
683
  ## Theming
610
684
 
@@ -619,7 +693,7 @@ No font ships either. The `/` showcase and the harness load Inter and JetBrains
619
693
 
620
694
  ### Scope
621
695
 
622
- 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:
696
+ Nothing is declared on `:root`. The tokens come in two tiers, and the tier decides where you override:
623
697
 
624
698
  - **Host-chrome tokens** are your vocabulary: the editor only reads them, and their defaults live behind the opt-in `aragonite-editor-theme` class alone. A host with a theme system of its own declares the same names anywhere in its cascade (`:root` included), skips the class, and the editor blends in with no bridge stylesheet. Standalone, add the class to a wrapper for the built-in palette; non-editor UI inside the wrapper (a surrounding toolbar, say) inherits it too.
625
699
  - **Editor-owned tokens** (the syntax and code palettes, the overlays, and the surfaces in the second table under [Theme tokens](#theme-tokens)) keep their defaults on `.editor` itself, so they render correctly with or without the class.
@@ -640,7 +714,7 @@ Three paths, by how much you want to change:
640
714
 
641
715
  ### Theme tokens
642
716
 
643
- 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.
717
+ The role table below is the stable **host-chrome contract** (the first tier under [Scope](#scope)): the tokens the editor and its plugins read to blend into your app, named the way a host theme system names them.
644
718
 
645
719
  | Role | Token(s) |
646
720
  | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -679,7 +753,7 @@ A live change is supported, and virtual rendering re-estimates the document at t
679
753
 
680
754
  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.
681
755
 
682
- **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.
756
+ **Plugin fallbacks.** A plugin reading a token keeps an inline fallback (`var(--color-text-muted, #aaaaaa)`) so it renders with no host. The rule for a fallback is the token's dark base value in `editor-theme.css`, never the light one (a few bundled plugins don't follow it yet). The one exception is the primary text color, `--color-text-primary`: 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.
683
757
 
684
758
  ## Keyboard shortcuts
685
759
 
@@ -687,52 +761,65 @@ Two terms before the table. A **chord** is one key plus its modifiers, written a
687
761
 
688
762
  Shifted symbols aren't modeled: `Shift+1` reaches the editor as whatever symbol the keyboard layout produces, so bind digits and letters (`Mod+7`), never the shifted symbol.
689
763
 
690
- 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.
691
-
692
- 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.
693
-
694
- | Action | Chord |
695
- | ----------------------------------- | ----------------------------------------------------------------------------- |
696
- | **Editing** | |
697
- | Bold (toggle strong) | `Mod+B` |
698
- | Italic (toggle emphasis) | `Mod+I` |
699
- | Strikethrough | `Mod+Shift+X` |
700
- | Inline code | `Mod+E` |
701
- | Edit a link's URL (live mode) | `Mod+K` (caret inside a link; opens the link card) |
702
- | Cycle heading level | `Mod+0`–`Mod+6` (0 clears, 1–6 set `#`–`######`) |
703
- | Split a block | `Enter` (in a code block, inserts a newline) |
704
- | Leave a code block | `Enter` on its empty last line (typing the closing fence there does the same) |
705
- | Hard line break | `Shift+Enter` |
706
- | Merge into the block before / after | `Backspace` / `Delete` (at the block's start / end) |
707
- | Indent / outdent a list item | `Tab` / `Shift+Tab` |
708
- | Indent / dedent a code line | `Tab` / `Shift+Tab` |
709
- | Insert a tab in prose | `Tab` |
710
- | Undo | `Mod+Z` |
711
- | Redo | `Mod+Y` or `Mod+Shift+Z` |
712
- | **Block reorder** | |
713
- | Move block up / down | `Alt+↑` / `Alt+↓` |
714
- | **Find / replace** | |
715
- | Open find | `Mod+F` |
716
- | Open find + replace | `Mod+H` |
717
- | Next / previous match | `Enter` / `Shift+Enter` (in the find field) |
718
- | Close search | `Esc` |
719
- | **Tables** | |
720
- | Move between cells | `Tab` / `Shift+Tab`, arrow keys |
721
- | Next row (or add one) | `Enter` (from the last cell, appends a row) |
722
- | Insert row below / above | `Mod+Enter` / `Mod+Shift+Enter` |
723
- | Insert column right / left | `Alt+Shift+→` / `Alt+Shift+←` |
724
- | Delete row | `Mod+Shift+Backspace` |
725
- | Delete column | `Alt+Shift+Backspace` |
726
- | Move row up / down | `Alt+↑` / `Alt+↓` |
727
- | Move column left / right | `Alt+←` / `Alt+→` |
728
- | Move the whole table up / down | `Mod+Alt+↑` / `Mod+Alt+↓` |
729
- | Cycle column alignment | `Mod+Shift+A` |
730
- | Create a table | type a header row (`\| a \| b \|`), then `Enter` |
731
- | **Clipboard** | |
732
- | Copy / cut a focused block | `Mod+C` / `Mod+X` |
733
- | Copy / cut a selected image | `Mod+C` / `Mod+X` |
734
-
735
- **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.
764
+ This table is for a reader. Bundled plugins list their chords here under their own family; a third-party plugin documents its own. 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 to extend a selection, `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.
765
+
766
+ 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.
767
+
768
+ | Action | Chord |
769
+ | ----------------------------------- | ------------------------------------------------------------------------------- |
770
+ | **Editing** | |
771
+ | Bold (toggle strong) | `Mod+B` |
772
+ | Italic (toggle emphasis) | `Mod+I` |
773
+ | Strikethrough | `Mod+Shift+X` |
774
+ | Inline code | `Mod+E` |
775
+ | Edit a link's URL (live mode) | `Mod+K` (caret inside a link; opens the link card) |
776
+ | Cycle heading level | `Mod+0`–`Mod+6` (0 clears, 1–6 set `#`–`######`) |
777
+ | Split a block | `Enter` (in a code block, inserts a newline) |
778
+ | Leave a code block | `Enter` on its empty last line (typing the closing fence there does the same) |
779
+ | Hard line break | `Shift+Enter` |
780
+ | Merge into the block before / after | `Backspace` / `Delete` (at the block's start / end) |
781
+ | Indent / outdent a list item | `Tab` / `Shift+Tab` |
782
+ | Check / uncheck a task item | `Mod+Enter` |
783
+ | Indent / dedent a code line | `Tab` / `Shift+Tab` |
784
+ | Insert a tab in prose | `Tab` |
785
+ | Undo | `Mod+Z` |
786
+ | Redo | `Mod+Y` or `Mod+Shift+Z` |
787
+ | **Block reorder** | |
788
+ | Move block up / down | `Alt+↑` / `Alt+↓` |
789
+ | **Find / replace** | |
790
+ | Open find | `Mod+F` |
791
+ | Open find + replace | `Mod+H` |
792
+ | Next / previous match | `Enter` / `Shift+Enter` (in the find field) |
793
+ | Close search | `Esc` |
794
+ | **Tables** | |
795
+ | Move between cells | `Tab` / `Shift+Tab`, arrow keys |
796
+ | Next row (or add one) | `Enter` (from the last cell, appends a row) |
797
+ | Insert row below / above | `Mod+Enter` / `Mod+Shift+Enter` |
798
+ | Insert column right / left | `Alt+Shift+→` / `Alt+Shift+←` |
799
+ | Delete row | `Mod+Shift+Backspace` |
800
+ | Delete column | `Alt+Shift+Backspace` |
801
+ | Move row up / down | `Alt+↑` / `Alt+↓` |
802
+ | Move column left / right | `Alt+←` / `Alt+→` |
803
+ | Move the whole table up / down | `Mod+Alt+↑` / `Mod+Alt+↓` |
804
+ | Cycle column alignment | `Mod+Shift+A` |
805
+ | Create a table | type a header row (`\| a \| b \|`), then `Enter` |
806
+ | **Clipboard** | |
807
+ | Copy / cut a focused block | `Mod+C` / `Mod+X` |
808
+ | Copy / cut a selected image | `Mod+C` / `Mod+X` |
809
+ | **Images** | |
810
+ | Resize a selected image | `Shift+←` / `Shift+→` |
811
+ | **Admonitions** | |
812
+ | Cycle the admonition kind | `Mod+7` (in an admonition; needs `admonitionsPlugin`) |
813
+ | Move from the title into the body | `Enter` (in the title; needs `admonitionsPlugin`) |
814
+ | **Details** | |
815
+ | Move from the summary into the body | `Enter` (in the summary; needs `detailsPlugin`) |
816
+ | **Mermaid diagrams** | |
817
+ | Finish editing a diagram | `Mod+Enter` (in the diagram's source box; `Esc` cancels; needs `mermaidPlugin`) |
818
+ | Open the diagram's focus view | `Mod+M` (the diagram focused; `Esc` closes it; needs `mermaidPlugin`) |
819
+ | **Slash commands** | |
820
+ | Open the list at the caret | `Mod+/` (types the `/` for you; needs `slashCommandsPlugin`) |
821
+
822
+ **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 of two or more cells (`| 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.
736
823
 
737
824
  **A merge that wouldn't read back as one block is refused.** `Backspace` / `Delete` at a boundary joins the two blocks only where the joined bytes re-parse as a single block; otherwise the press moves the caret across the boundary and the document is untouched.
738
825
 
@@ -740,7 +827,7 @@ Right-clicking any cell opens the table's action menu: cut/copy/paste, Row and C
740
827
 
741
828
  **Whole-block clipboard.** A block focused as a whole (a thematic break, a plugin diagram) has no text selection, so `Mod+C` / `Mod+X` copy or cut the block's own Markdown (cut removes the block), and the same chords on a selected inline image act on the image's source. In reading mode copy works and cut degrades to copy.
742
829
 
743
- **Menu clipboard caveats.** The right-click menu's Cut/Copy write the cell's rendered text, which differs from keyboard `Mod+X`'s raw-source slice for a cell holding an inline widget (a literal `<br>`, say). Menu Paste reads through `navigator.clipboard.readText()`, the one clipboard path not yet proven on the Tauri/wry webview. Keyboard `Mod+V` is unaffected.
830
+ **Menu clipboard rows.** A table cell's menu Cut and Copy go through the same copy code as the keyboard, so they write the same bytes. The menus' Paste rows (and the block menu's Copy) go through the async `navigator.clipboard` API instead of a clipboard event, which is the one kind of clipboard route not yet proven on the Tauri/wry webview; [Clipboard in a webview](#clipboard-in-a-webview) lists them. Keyboard `Mod+V` is unaffected.
744
831
 
745
832
  ### Rebinding chords
746
833
 
@@ -756,15 +843,15 @@ The `keybindings` prop rebinds (or disables, with `command: null`) chords that g
756
843
  />
757
844
  ```
758
845
 
759
- An override's `kind` scope takes a plugin kind too; name it through the plugin's exported kind constant, which is a branded string, so a raw literal won't typecheck. A bind reaches every surface the editor owns, including the ones with no focused block for a kind scope to apply to: the caret between two blocks, a block focused as a whole (a thematic break), and the document with nothing focused inside it. A disable unbinds the command but the press is still consumed, as [Which shortcuts the editor consumes](#which-shortcuts-the-editor-consumes) explains.
846
+ An override's `kind` scope takes a plugin kind too; name it through the plugin's exported kind constant, which is a branded string, so a raw literal won't typecheck. A bind reaches every surface the editor owns, including the ones with no focused block for a kind scope to apply to: the caret between two blocks, a block focused as a whole (a thematic break, a plugin diagram), and the document with nothing focused inside it. A disable unbinds the command but the press is still consumed, as [Which shortcuts the editor consumes](#which-shortcuts-the-editor-consumes) explains.
760
847
 
761
848
  Scoping by kind is what makes the shared structural chords reachable, since a chord like `Tab` is bound separately on every kind that wants it. The first entry above frees `Tab` inside list items (for focus traversal in a form-embedded editor, say) and leaves `Tab` alone in code blocks and prose.
762
849
 
763
850
  **Scope table chords to `tableCell`, not `table`.** Inside a table the cell holds the caret, so the cell's kind is what resolves a chord: `{ kind: 'tableCell', chord: 'Mod+Enter', command: null }` frees the insert-row chord, while the same entry scoped to `table` resolves against a block that never gets a keystroke and silently does nothing.
764
851
 
765
- Two cell gestures sit outside the keymap entirely, because both depend on where the caret sits inside the cell rather than on the chord: arrow navigation between cells, and the three-stage `Mod+A` (cell text, then the table, then the document). They aren't commands, so the two override directions are asymmetric:
852
+ Two cell gestures sit outside the keymap entirely, because both depend on where the caret sits inside the cell rather than on the chord: arrow navigation between cells, and the two-press `Mod+A` (the cell's text, then the document). They aren't commands, so the two override directions are asymmetric:
766
853
 
767
- - A **disable** can't reach them. `{ kind: 'tableCell', chord: 'Mod+A', command: null }` unbinds nothing (there was no binding) and the three-stage gesture keeps running.
854
+ - A **disable** can't reach them. `{ kind: 'tableCell', chord: 'Mod+A', command: null }` unbinds nothing (there was no binding) and the two presses keep working.
768
855
  - A **bind** shadows them completely. The second entry above, `ArrowUp` bound to `table.deleteRow`, resolves first and the cell never navigates. That's the intended precedence (an explicit binding wins), but it means claiming an arrow or `Mod+A` for your own command takes the built-in gesture with it.
769
856
 
770
857
  Disabling `Tab` or `Enter` for `tableCell` likewise leaves the cell with no way to reach the next cell or append a row, so scope those deliberately.
@@ -800,7 +887,7 @@ window.addEventListener(
800
887
  );
801
888
  ```
802
889
 
803
- The set is composed on each call, not baked at build time, so it already reflects the block kinds your plugins registered, the global chords claimed by the plugins this editor listed, and the `keybindings` overrides you passed: a chord you disabled globally drops out (a per-kind disable can't, since other kinds still claim it), one you bound appears, and turning `searchBar` off drops `Mod+F` and `Mod+H` with it.
890
+ The set is composed on each call, not baked at build time, so it already reflects the block kinds and the global chords of the plugins this editor listed, and the `keybindings` overrides you passed: a chord you disabled globally drops out (a per-kind disable can't, since other kinds still claim it, and neither can a disable of a chord the editor handles outside the keymaps, like `Shift+Tab` or `Shift+ArrowUp`), one you bound appears, and turning `searchBar` off drops `Mod+F` and `Mod+H` with it.
804
891
 
805
892
  **Modifier chords only, by design.** Bare keys (`Enter`, `Tab`, `Escape`, the arrows, `Backspace`) never appear: a focused document owns them whatever the set says, so an app shortcut bound to one is lost while the caret is in a block regardless. That makes the set the right input for an accelerator table and the wrong input for a "what can I press here" help sheet; for that, use the [shortcut table](#keyboard-shortcuts).
806
893
 
@@ -818,7 +905,7 @@ Two props decide how the editor sits in your page: who owns the scroll, and what
818
905
 
819
906
  By default the editor root is the scrollport (the box that scrolls): it owns its scroll position, and virtual rendering keeps the mounted block count proportional to the viewport rather than the document, which is what lets it hold a large file at all. `scrollMode='host'` is the embedded alternative: the root stops scrolling and grows to its content, and an ancestor of yours scrolls it. A shell that stacks several documents in one scroller (a journal, a comment thread) wants this; a whole-file editor doesn't.
820
907
 
821
- **Virtual rendering follows the scroll.** The editor windows against whatever actually scrolls it, so a large document inside a page-scrolled shell stays bounded to the viewport just like a standalone one. Windowing only turns on past a size budget (a few viewports' worth of estimated height), so a small embedded entry never windows in either mode and pays nothing.
908
+ **Virtual rendering follows the scroll.** The editor windows against whatever actually scrolls it, so a large document inside a page-scrolled shell stays bounded to the viewport just like a standalone one. Windowing only turns on past a fixed budget of estimated height (a few screens' worth on a typical display), so a small embedded entry never windows in either mode and pays nothing.
822
909
 
823
910
  **The one trade is scroll anchoring.** The browser's native anchoring and windowing's own correction can't both hold one scroll position (they'd double-correct), so exactly one runs. While an embedded editor is windowing it corrects by hand and withdraws its subtree from your scroller's anchor candidates; below the budget it corrects nothing and stays a candidate. Two consequences: your scroller is otherwise untouched, and late-sizing content in your own chrome above a windowing editor isn't compensated while the viewport holds only editor content. Size your chrome up front (or reserve its height) if that matters to you.
824
911
 
@@ -864,7 +951,7 @@ By default the bar pins to the editor root's top edge. In self-scroll mode that
864
951
 
865
952
  - **The prop reads live.** `null` or `undefined` puts the bar back in the editor root, so an anchor that mounts with a panel and unmounts with it is fine. It has no effect while `searchBar` is `false`; that switch turns the whole feature off, chords included.
866
953
  - **Placement inside the anchor is yours.** The editor treats the element as the box and exports no positioning knobs. The bar positions itself absolutely, so give the anchor `position: relative` (or another positioned ancestor) and a size; otherwise the bar resolves against whatever the page's layout offers next.
867
- - **The bar carries the editor's theme scope with it.** Custom properties resolve by DOM ancestry, so an anchor outside the editor resolves whatever the page's cascade offers there. When the editor itself sits under `aragonite-editor-theme`, the relocated node carries that class and the effective `data-editor-theme` (both tracking a `theme` change live), so the bar keeps the built-in palette. In a themed host with no class, it deliberately carries neither: the anchor inherits your own tokens, which is the palette the bar should wear there.
954
+ - **The bar carries the editor's theme scope with it.** Custom properties resolve by DOM ancestry, so an anchor outside the editor resolves whatever the page's cascade offers there. When the editor itself sits under `aragonite-editor-theme`, the relocated node carries that class and the effective `data-editor-theme` (both tracking a `theme` change live), so the bar keeps the built-in palette. In a themed host with no class, it carries the `data-editor-theme` attribute but not the class, so the anchor inherits your own tokens, which is the palette the bar should wear there.
868
955
 
869
956
  ## Embedding in a webview shell
870
957
 
@@ -882,11 +969,11 @@ Which chords reach the page, and whether the shell or the document gets first re
882
969
 
883
970
  ### Clipboard in a webview
884
971
 
885
- **Plain text is the whole model.** Every copy and cut writes `text/plain`, every paste reads it, and there's no HTML flavor to negotiate. What crosses is Markdown source.
972
+ **Plain text is the model.** Every copy and cut writes `text/plain`, every paste reads it, and what crosses is Markdown source. The one extra is a rectangle of table cells: its copy and cut also write `text/html` holding a plain `<table>` of the same cells, which is what spreadsheets paste. A paste never reads HTML.
886
973
 
887
974
  - **A clipboard event may target `document.body` rather than the editor.** Where the selection's focus end hosts no caret (an image-only paragraph, a thematic break), Chromium dispatches `copy` / `cut` / `paste` at the body instead of the focused block. The editor handles that with a root-level handler, so cross-block copy works. What it means for you: an editor clipboard event doesn't reliably originate inside the editor's DOM, so a host listener that claims clipboard events by "the target is outside the editor" will claim the editor's.
888
- - **Multi-line writes normalize to the OS line ending.** The whole-block copy chord (`Mod+C` / `Mod+X` on a block focused as a whole) writes through `navigator.clipboard.writeText`, and Chromium rewrites a multi-line payload to the platform's line ending, CRLF on Windows. Pasting back into the editor re-normalizes to LF, so documents are unaffected; a host that reads the system clipboard itself normalizes on its own side.
889
- - **That async write is the path to prove in your shell.** wry has refused `writeText` in some contexts, which is why every other clipboard route writes synchronously through the event object. A refused write is contained rather than thrown: nothing reaches the clipboard, a dev build warns, and a cut degrades to leaving the block alone.
975
+ - **Multi-line writes normalize to the OS line ending.** The whole-block copy chord (`Mod+C` / `Mod+X` on a block focused as a whole) writes through `navigator.clipboard.writeText`, and Chromium rewrites a multi-line payload to the platform's line ending, CRLF on Windows. Pasting back into the editor reads either ending and writes the document's own, so documents are unaffected; a host that reads the system clipboard itself normalizes on its own side.
976
+ - **The async routes are the ones to prove in your shell.** wry has refused `writeText` in some contexts, which is why keyboard copy and cut (inside a block and across blocks) write synchronously through the event object. What goes through the async `navigator.clipboard` API instead: the whole-block chord above, the code block's copy button, and the right-click menus' clipboard rows (the block menu's Copy and "Replace with clipboard", the prose and table menus' Paste, and the prose menu's "Paste as plain text"). A refused whole-block write is contained rather than thrown: nothing reaches the clipboard, a dev build warns, and a cut degrades to leaving the block alone.
890
977
 
891
978
  ### Verify in the shell
892
979
 
@@ -894,7 +981,7 @@ Run these by hand in the built application, once per platform you ship. Yes, by
894
981
 
895
982
  1. Every chord the editor and your app rely on, including whatever the shell reserves for zoom, devtools, and reload.
896
983
  2. Select-all across blocks containing an image or a thematic break, copy, then paste into an external application.
897
- 3. The two routes that reach the async `navigator.clipboard` API instead of a clipboard event: whole-block `Mod+C` / `Mod+X` on a thematic break or a plugin diagram, and the table right-click menu's Paste (see "Menu clipboard caveats" under [Keyboard shortcuts](#keyboard-shortcuts)).
984
+ 3. The routes that reach the async `navigator.clipboard` API instead of a clipboard event (listed under [Clipboard in a webview](#clipboard-in-a-webview)): whole-block `Mod+C` / `Mod+X` on a thematic break or a plugin diagram, the code block's copy button, the block menu's Copy and "Replace with clipboard" rows, and the prose and table menus' Paste rows (and the prose menu's "Paste as plain text").
898
985
  4. Multi-line text copied from a native application and pasted into a block.
899
986
  5. An image pasted from the system clipboard, if `onPasteImage` is installed (see [Image paste](#image-paste)).
900
987
  6. A local-file image, on each platform, since the asset protocol takes a different form on Windows (see [Which URLs render](#which-urls-render)).
@@ -915,7 +1002,7 @@ diag.enableTrace(); // once, behind a "report a bug" affordance, say
915
1002
  const report = diag.serializeDiagnostics();
916
1003
 
917
1004
  diag.isTraceEnabled(); // true
918
- diag.traceSnapshot(); // [{ t: 48211.3, site: 'reveal', kind: 'open', detail: { tier: 'inline', construct: 'strong:4-12' } }, ...]
1005
+ diag.traceSnapshot(); // [{ t: 48211.3, site: 'reveal', kind: 'open', detail: { tier: 'construct', construct: 'strong:4-12' } }, ...]
919
1006
  diag.disableTrace();
920
1007
  ```
921
1008
 
@@ -925,8 +1012,8 @@ diag.disableTrace();
925
1012
  ## Interaction trace
926
1013
 
927
1014
  ```
928
- [812ms ago] reveal/open tier=inline construct=strong:4-12
929
- [640ms ago] text-render/cursor-capture walk=3
1015
+ [812ms ago] reveal/open tier=construct construct=strong:4-12
1016
+ [640ms ago] text-render/cursor-capture raw=3
930
1017
  [12ms ago] pending-cursor/consume offset=9 applied=true
931
1018
  ```
932
1019
 
@@ -1000,7 +1087,7 @@ await rects.navigateTo([840], 12); // the same, with the caret after the block's
1000
1087
  | `caretRect()` | The live native caret, or `null` (including whenever a cross-block selection is active) |
1001
1088
  | `reveal(path)` | Mounts a block virtual rendering has unmounted, resolving `true` once its element exists |
1002
1089
  | `scrollTo(path, opts?)` | Mounts the block, then scrolls the viewport to it (`opts.block`: `'nearest'` default, or `'center'`; `opts.hold`: keep holding it, default true) |
1003
- | `navigateTo(path, offset?)` | The same, plus lands the caret in the block (at its start, or at the offset you pass), which is what a navigation affordance owes the user |
1090
+ | `navigateTo(path, offset?)` | The same, plus lands the caret in the block (at its start, or at the offset you pass), which is what a navigation button should do for the user |
1004
1091
 
1005
1092
  Offsets are raw offsets into the block (dimmed markers included) on text blocks, and cell indices on tables. `rangeRects` accepts the exported `SELECTION_END` as `end`, meaning "through the block's last measurable position".
1006
1093
 
@@ -1027,9 +1114,9 @@ search.close();
1027
1114
  `getRects().navigateTo(path)` is the navigation call: jump to a heading, an outline entry, a cross-reference target. `scrollTo(path, opts)` is the same reveal-and-scroll without landing the caret, for moving the viewport without moving the selection (the built-in search does exactly that). Four things to know:
1028
1115
 
1029
1116
  - **It mounts first.** A block virtual rendering has unmounted has no element to scroll to, so the call mounts it and then scrolls. `reveal(path)` is that same mount without the scroll, for measuring something offscreen.
1030
- - **The boolean is honest.** It resolves only after the position settles, so `true` means the block is genuinely in view, not merely that the call ran. A target that can't mount (one inside a collapsed `<details>` or admonition, say) resolves `false` and leaves nothing pinned.
1031
- - **`'nearest'` holds, `'center'` places.** The default `'nearest'` keeps the target visible through the reflow a mount triggers (images decoding above it collapse the document height). `'center'` places the block precisely once the scroll settles, and stops holding it after. Pass `hold: false` to hand the viewport straight back, which is what a restore that writes its own remembered scroll position afterwards wants.
1032
- - **Land the caret if a user asked to go there.** A navigation affordance that only scrolls leaves focus on whatever the user clicked, where the editor's chords don't reach: an undo typed right after the jump does nothing. `navigateTo` places the caret at the target through the same restore machinery `setSelection` and undo use, which is why it's a distinct call rather than a flag.
1117
+ - **The boolean is honest.** It resolves only after the position settles, so `true` means the block is genuinely in view, not merely that the call ran. A closed `<details>` on the way is opened first, as one undoable edit. In reading mode a section that shows closed can't be opened, so the target can't mount, and the call resolves `false` and leaves nothing pinned.
1118
+ - **`'nearest'` holds, `'center'` places.** The default `'nearest'` scrolls only as far as it has to (not at all for a block already on screen), then keeps the block right where it landed through the reflow a mount triggers (images decoding above it collapse the document height). `'center'` places the block precisely once the scroll settles, and stops holding it after. Pass `hold: false` to hand the viewport straight back, which is what a restore that writes its own remembered scroll position afterwards wants.
1119
+ - **Land the caret if a user asked to go there.** A navigation affordance that only scrolls leaves focus on whatever the user clicked, where the editor's chords don't reach: an undo typed right after the jump does nothing. `navigateTo` places the caret at the target the way an edit's caret lands (through the block's own caret entry, and into the first line of a block that holds others), which is why it's a distinct call rather than a flag. Like `reveal` and `scrollTo`, and unlike an edit's landing, it opens a closed `<details>` on the way.
1033
1120
 
1034
1121
  Finding the path in the first place: `parse(getSource())` gives you the document tree (every node has a `kind` and containers have `children`), so collect the headings, recursing into containers so a heading inside a blockquote or list is reachable too:
1035
1122
 
@@ -1049,15 +1136,15 @@ The bundled toc plugin does exactly that walk over its live document, and clicki
1049
1136
 
1050
1137
  ### Recipe: a selection toolbar
1051
1138
 
1052
- The editor ships one: a popover that opens beside a prose selection with the marks, the link, a heading picker (inside one block only), inline code and copy, on by default and off with `selectionToolbar={false}`. It is built on the doors below and nothing else, so this recipe is also how to replace it with your own. Nine steps, and the anchoring ones have a snippet after the list:
1139
+ The editor ships one: a popover that opens beside a prose selection with the marks, the link, a heading picker (inside one block only), inline code and copy, on by default and off with `selectionToolbar={false}`. It's built on the public calls below and nothing else, so this recipe is also how to replace it with your own. Nine steps, and the anchoring ones have a snippet after the list:
1053
1140
 
1054
1141
  1. **Subscribe to `selectionChange`.** A `null` payload or a collapsed selection (anchor equals focus) hides the bar.
1055
1142
  2. **Put the endpoints in document order first.** `normalizeSelection(snapshot)` answers `{ start, end }` (by path, then by offset when the paths match), so a backward drag anchors exactly like a forward one. Anchor to `start`; a hand-rolled comparison gets the container-and-its-child pair wrong, where the shorter path is the earlier one.
1056
1143
  3. **Cross-block selections** (start and end in different blocks): anchor to `rangeRects(start.path, start.offset, SELECTION_END)`, the start block's rects from the selection to its end. Rect `[0]` is the first visual line; place the bar above its top-left.
1057
- 4. **Single-block selections**: `getSelection()` reports the range's real endpoints, so anchor with `rangeRects(start.path, start.offset, end.offset)`, the same call with a real end offset in place of `SELECTION_END`. (Reading the native `window.getSelection()` range works too, since within one block the editor delegates selection to the browser.) A selection **inside a table** shares the table's path on both endpoints and carries cell indices in `offset`, which the `cellCoordinate` flag need not mark, so exclude it with `getBlockKindAt(start.path) === 'table'`, never by the flag alone.
1144
+ 4. **Single-block selections**: `getSelection()` reports the range's real endpoints, so anchor with `rangeRects(start.path, start.offset, end.offset)`, the same call with a real end offset in place of `SELECTION_END`. (Reading the native `window.getSelection()` range works too, since within one block the editor delegates selection to the browser.) A selection **inside a table** shares the table's path on both endpoints and carries cell indices in `offset`, with `cellCoordinate: true` on both, so exclude it by that flag, as the snippet below does.
1058
1145
  5. **Re-anchor on the next `selectionChange`, not on scroll.** Rects are viewport-space snapshots; a `position: fixed` bar drifts under scroll until the selection next changes. Wire a scroll listener only if your UX demands live tracking.
1059
1146
  6. **Fire the buttons through `runCommand`, not synthetic keystrokes.** `runCommand(TOOLBAR_COMMANDS.toggleStrong)` says what the button means; a synthesized `Ctrl+B` says which key the button impersonates, and a user's rebind then silently rewires it.
1060
- 7. **Grey the declining buttons out with `canRunCommand`, on the same `selectionChange`.** Ask it per button and disable the ones that answer `false`, so a selection spanning blocks shows the link button dimmed rather than dead while the format toggles stay live (the editor's own bar goes one further and drops a labelled row the door declines, which is why its heading picker vanishes there). Still read `runCommand`'s boolean, per [Toolbar commands](#toolbar-commands).
1147
+ 7. **Grey the declining buttons out with `canRunCommand`, on the same `selectionChange`.** Ask it per button and disable the ones that answer `false`, so a selection spanning blocks shows the link button dimmed rather than dead while the format toggles stay live (the editor's own bar goes one further and drops a labelled row `canRunCommand` declines, which is why its heading picker vanishes there). Still read `runCommand`'s boolean, per [Toolbar commands](#toolbar-commands).
1061
1148
  8. **Paint the pressed states with `isCommandActive`, on that same `selectionChange`.** A selection already inside a bold run shows the bold button pressed (`aria-pressed` is the accessible spelling), and pressing it then unwraps: the pressed paint and the press read the same bytes, so they agree by construction. In live mode a selection sitting inside a link shows the link button pressed the same way, off the link the card would edit, and clicking it opens that link's card with the selection left alone; a selection that runs out of the link isn't inside it, so the button unpresses and the click falls back to creating a new link over the range.
1062
1149
  9. **Keep focus in the document**, for the same reason the insert toolbar does: cancel the button's mousedown default, or restore a `getSelection()` snapshot before calling.
1063
1150
 
@@ -1067,7 +1154,7 @@ import { normalizeSelection, SELECTION_END } from '@voithos-labs/aragonite';
1067
1154
  editor.getEvents().on('selectionChange', (sel) => {
1068
1155
  if (!sel) return hide();
1069
1156
  const { start, end } = normalizeSelection(sel);
1070
- if (editor.getBlockKindAt(start.path) === 'table') return hide();
1157
+ if (start.cellCoordinate) return hide();
1071
1158
  const sameBlock = start.path.join('.') === end.path.join('.');
1072
1159
  if (sameBlock && start.offset === end.offset) return hide();
1073
1160
  const rects = editor.getRects().rangeRects(start.path, start.offset, sameBlock ? end.offset : SELECTION_END);
@@ -1075,7 +1162,7 @@ editor.getEvents().on('selectionChange', (sel) => {
1075
1162
  });
1076
1163
  ```
1077
1164
 
1078
- The editor's own bar (`src/lib/components/menu/SelectionToolbar.svelte`) is this recipe end to end: both anchoring branches, the table exclusion, the `TOOLBAR_COMMANDS` buttons greyed by `canRunCommand` and pressed by `isCommandActive`, and the mousedown cancel that keeps the caret in the document.
1165
+ The editor's own bar (`src/lib/components/menu/SelectionToolbar.svelte`) is built on the same calls: `normalizeSelection`, `rangeRects`, the `TOOLBAR_COMMANDS` buttons greyed by `canRunCommand` and pressed by `isCommandActive`, and the mousedown cancel that keeps the caret in the document. It places itself differently, though: below-right of the selection's end rather than above its start, it leaves tables out by asking `getBlockKindAt` rather than reading the flag, and it re-anchors on scroll and resize.
1079
1166
 
1080
1167
  ### Recipe: an insert toolbar
1081
1168
 
@@ -1090,13 +1177,48 @@ The editor's own bar (`src/lib/components/menu/SelectionToolbar.svelte`) is this
1090
1177
  </button>
1091
1178
  ```
1092
1179
 
1093
- 1. **Don't let the button take focus.** The call inserts at the caret, and a button that focuses on press has already destroyed it, so the call returns `false`. Cancel the press default, as above, so focus never leaves the document, or stash a `getSelection()` snapshot and `setSelection` it back before inserting.
1180
+ 1. **Don't let the button take focus.** The call inserts at the caret, and a button that focuses on press has already destroyed it, so the call resolves `false`. Cancel the press default, as above, so focus never leaves the document, or stash a `getSelection()` snapshot and `setSelection` it back before inserting.
1094
1181
  2. **Hand it canonical bytes.** A table button inserts `'| Column | Column |\n| --- | --- |\n| | |\n'`; a fence button `'```lang\n\n```\n'`. There's no per-construct API, so a new kind needs no new call. (A table is also typeable: a lone header row completed with `Enter` creates the same thing, per [Keyboard shortcuts](#keyboard-shortcuts).)
1095
1182
  3. **Position with `getRects()`.** `caretRect()` anchors a bar to the insertion point, `blockRect(path)` to the block. Both are viewport-space snapshots; re-read on the next `selectionChange`.
1096
- 4. **Read the result on the `edit` channel**, not on the line after the call: the commit lands on the editor's own flush.
1183
+ 4. **Await the call before reading the result**, or read it on the `edit` channel: the commit lands on the editor's own flush, not on the line after the call.
1097
1184
 
1098
1185
  The repository's `InsertToolbar` component, the fixed strip the showcase mounts under its header in live mode, is this recipe's reference: canonical snippet buttons, the mousedown cancel, and a no-caret greying read off `selectionChange`, the same decline `insertMarkdown` would answer, surfaced before the click.
1099
1186
 
1187
+ ### Recipe: a typed-trigger menu
1188
+
1189
+ Tag autocomplete on `#`, a document picker on `[[`, a mention on `@`: a list that follows the caret while the author keeps typing, an inline menu. This is the one piece of chrome not to build yourself from `getRects()` and a key listener, because the editor has to be the one to notice the trigger, hold the keys, and write the pick. You hand it a trigger and a list; `getInlineMenus()` does the rest.
1190
+
1191
+ ```ts
1192
+ const handle = editor.getInlineMenus().addSource({
1193
+ name: 'doc-links',
1194
+ trigger: '[[',
1195
+ // End the session once the author closes the link by hand.
1196
+ accepts: (query) => !/[\]\n]/.test(query),
1197
+ items: async ({ query, signal }) => {
1198
+ const hits = await searchTitles(query, { signal });
1199
+ return hits.map((doc) => ({
1200
+ id: doc.id,
1201
+ label: doc.title,
1202
+ detail: doc.folder,
1203
+ insert: `[[${doc.linkText}]]`
1204
+ }));
1205
+ }
1206
+ });
1207
+ // A shortcut or a toolbar button: types `[[` at the caret and opens the same list.
1208
+ editor.getInlineMenus().open('doc-links');
1209
+ ```
1210
+
1211
+ 1. **`items` is the whole data contract.** Return an array, or a promise of one for a list read off an index. A slow answer a later keystroke superseded is dropped, and its `signal` aborts so you can cancel the read. A rejection is reported on the `error` event and reads as an empty list.
1212
+ 2. **`insert` is bytes.** The pick replaces the trigger and the query, the caret lands after it, and the whole replacement is one undo entry. There is no construct-specific call: a tag inserts `#work`, a link `[[Roadmap]]`. Add a trailing space there if your construct wants one. One line only: a line break is refused and reported on the `error` event, because those bytes belong to one block. An empty `insert` is fine and just removes the trigger and the query, which is the shape for a `/` command: the pick clears what was typed, and `onCommit` inserts the block through `insertMarkdown`, which puts it in as a paste would. Make `onCommit` async and await the insert there: every write that lands while its promise is pending, up to the author's next input in this editor, is part of the pick's undo entry, so the block and the cleared query come back in one press. The bundled slash-commands plugin is that shape.
1213
+ 3. **An empty list holds no key.** While rows are showing, the editor takes ArrowUp, ArrowDown, Enter, Tab and Escape before the focused block sees them. With nothing to show, the list is gone and Enter is the author's own Enter again, while the session stays alive for the next keystroke.
1214
+ 4. **`opensAt` and `accepts` are your grammar.** A tag declines a mid-word `#` (so `C#` stays text) and ends on a space; a link accepts spaces and ends on `]`. `open(name)` skips `opensAt`: the gesture is the author's say-so. `open(name, { query })` types a query after the trigger too, so the list opens narrowed.
1215
+ 5. **Some places never open a list, whatever your grammar says.** A trigger you type only opens where the bytes are prose: inside an inline code span, a link's destination or title, an image, an autolink or raw HTML it opens nothing, so a `#` in a URL fragment stays a fragment, and a destination still being typed (its closing `)` hasn't arrived yet) counts as a destination too. A link's own text is prose, and a trigger there opens. And no list opens at all in a table cell, in reading mode, or over a selection.
1216
+ 6. **Escape dismisses for good.** What was typed stays, and typing on does not reopen the list; only a new trigger does.
1217
+ 7. **Style it as you would the editor's other menus.** The list is the shared `.md-menu` surface and reads the same tokens. For rows richer than a label and a detail (a snippet, a highlighted match), pass a `row` component; it receives the `item`, whether it is `active`, and the `query`.
1218
+ 8. **From a plugin, the same registry is `editor.inlineMenus`** on your `onEditor` context; return the handle's `dispose` from the callback.
1219
+
1220
+ The tag source in `src/routes/demo-tags/tag-marks-plugin.ts` is this recipe over a synchronous list, `src/routes/test/plugins/inline-menu/doc-link-menu-plugin.ts` over a late one, and `src/lib/plugins/slash-commands/slash-source.ts` over picks that insert blocks.
1221
+
1100
1222
  ## Rewriting a document
1101
1223
 
1102
1224
  You never assemble an edit by hand. Edits happen through the component, every applied edit shows up on the `edit` channel, and how an edit is applied inside isn't part of the consumer contract.
@@ -1118,7 +1240,7 @@ For rewriting a whole document (converting legacy syntax, migrating content, app
1118
1240
  <Editor bind:this={editor} {source} />
1119
1241
  ```
1120
1242
 
1121
- The replacement is one document swap, so undo history and the caret don't survive it. That's the honest shape for an import-or-convert affordance; pretending otherwise would only hide the swap.
1243
+ The replacement is one document swap, so undo history and the caret don't survive it, and it's announced on `sourceSwap` rather than `edit`.
1122
1244
 
1123
1245
  A transformer working over `parse`'s output can lean on how the document is put back together: `serialize` is exactly `prefix + Σ(child.leadingTrivia + child.raw) + suffix` over the document's children, so a rewrite can replace individual blocks' bytes and reassemble without touching the rest.
1124
1246