@voithos-labs/aragonite 0.10.2 → 0.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/README.md +6 -21
  2. package/THIRD-PARTY-NOTICES.md +25 -0
  3. package/dist/a11y-strings.d.ts +19 -0
  4. package/dist/a11y-strings.js +19 -0
  5. package/dist/action-contracts.d.ts +7 -1
  6. package/dist/ambient/ambient-dom.js +5 -1
  7. package/dist/block-component.d.ts +14 -0
  8. package/dist/components/BlockDragHandle.svelte +37 -24
  9. package/dist/components/BlockHost.svelte +18 -11
  10. package/dist/components/Editor.svelte +434 -198
  11. package/dist/components/Editor.svelte.d.ts +1 -1
  12. package/dist/components/SelectionOverlay.svelte +25 -17
  13. package/dist/components/SelectionOverlay.svelte.d.ts +1 -1
  14. package/dist/components/TailInsert.svelte +107 -0
  15. package/dist/components/TailInsert.svelte.d.ts +17 -0
  16. package/dist/components/block-content-selector.d.ts +6 -2
  17. package/dist/components/block-content-selector.js +6 -2
  18. package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
  19. package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
  20. package/dist/components/blocks/code/CodeBlock.svelte +211 -30
  21. package/dist/components/blocks/code/CodeBlockRail.svelte +704 -0
  22. package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
  23. package/dist/components/blocks/code/code-bootstrap.js +16 -0
  24. package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
  25. package/dist/components/blocks/code/code-context-actions.js +24 -0
  26. package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
  27. package/dist/components/blocks/code/code-fence-exit.js +26 -0
  28. package/dist/components/blocks/code/code-languages.d.ts +6 -0
  29. package/dist/components/blocks/code/code-languages.js +22 -3
  30. package/dist/components/blocks/code/code-renderer.js +11 -0
  31. package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
  32. package/dist/components/blocks/editable-leaf.d.ts +37 -6
  33. package/dist/components/blocks/editable-leaf.js +248 -30
  34. package/dist/components/blocks/editable-surface.d.ts +9 -0
  35. package/dist/components/blocks/editable-surface.js +22 -3
  36. package/dist/components/blocks/list/ListBlock.svelte +1 -0
  37. package/dist/components/blocks/list/ListItemBlock.svelte +14 -5
  38. package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +2 -0
  39. package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
  40. package/dist/components/blocks/list/task-checkbox.js +11 -2
  41. package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
  42. package/dist/components/blocks/surface-wiring.svelte.js +8 -1
  43. package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
  44. package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
  45. package/dist/components/blocks/table/TableBlock.svelte +205 -186
  46. package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
  47. package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
  48. package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
  49. package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
  50. package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
  51. package/dist/components/blocks/table/cell-clipboard.js +34 -1
  52. package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
  53. package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
  54. package/dist/components/blocks/table/table-cell-paste.js +2 -1
  55. package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
  56. package/dist/components/blocks/table/table-menu-model.js +36 -17
  57. package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
  58. package/dist/components/blocks/text/click-snap-guard.d.ts +3 -0
  59. package/dist/components/blocks/text/click-snap-guard.js +8 -0
  60. package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
  61. package/dist/components/blocks/text/delimiter-autopair.js +216 -0
  62. package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
  63. package/dist/components/blocks/text/edge-policy-dispatch.js +89 -20
  64. package/dist/components/blocks/text/live-selection-edit.d.ts +5 -5
  65. package/dist/components/blocks/text/live-selection-edit.js +37 -7
  66. package/dist/components/blocks/text/text-clipboard.js +2 -2
  67. package/dist/components/blocks/text/text-keydown.d.ts +7 -1
  68. package/dist/components/blocks/text/text-keydown.js +11 -1
  69. package/dist/components/blocks/text/text-render.d.ts +1 -1
  70. package/dist/components/blocks/text/text-render.js +3 -1
  71. package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
  72. package/dist/components/blocks/text/widget-interaction.js +181 -37
  73. package/dist/components/drag-handle.d.ts +41 -0
  74. package/dist/components/drag-handle.js +134 -0
  75. package/dist/components/editor-root-focus.d.ts +19 -0
  76. package/dist/components/editor-root-focus.js +67 -0
  77. package/dist/components/editor-root-geometry.d.ts +39 -0
  78. package/dist/components/editor-root-geometry.js +91 -0
  79. package/dist/components/editor-root-keydown.d.ts +1 -1
  80. package/dist/components/editor-root-keydown.js +12 -3
  81. package/dist/components/editor-root-listeners.d.ts +3 -6
  82. package/dist/components/editor-root-listeners.js +3 -24
  83. package/dist/components/editor-root-mode-flip.d.ts +36 -0
  84. package/dist/components/editor-root-mode-flip.js +92 -0
  85. package/dist/components/image/ImageOverlayHost.svelte +14 -6
  86. package/dist/components/image/ImageProperties.svelte +512 -70
  87. package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
  88. package/dist/components/image/ImageResizeHandles.svelte +67 -34
  89. package/dist/components/image/image-crop.d.ts +39 -0
  90. package/dist/components/image/image-crop.js +74 -0
  91. package/dist/components/image/image-edit-commit.d.ts +1 -0
  92. package/dist/components/image/image-edit-commit.js +21 -5
  93. package/dist/components/image/image-source-bytes.js +10 -3
  94. package/dist/components/image/image-widget-editing.js +1 -0
  95. package/dist/components/image/widget-dom.js +5 -1
  96. package/dist/components/link-card/link-card-commit.js +1 -1
  97. package/dist/components/lrd-map-gate.js +1 -1
  98. package/dist/components/menu/BlockMenu.svelte +315 -0
  99. package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
  100. package/dist/components/menu/MenuIcon.svelte +153 -0
  101. package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
  102. package/dist/components/menu/SelectionToolbar.svelte +385 -0
  103. package/dist/components/menu/SelectionToolbar.svelte.d.ts +16 -0
  104. package/dist/components/menu/clipboard-actions.d.ts +12 -0
  105. package/dist/components/menu/clipboard-actions.js +42 -0
  106. package/dist/components/menu/default-context-actions.d.ts +15 -0
  107. package/dist/components/menu/default-context-actions.js +76 -0
  108. package/dist/components/menu/flyout-placement.d.ts +6 -0
  109. package/dist/components/menu/flyout-placement.js +25 -0
  110. package/dist/core/inline/format-toggle.d.ts +12 -4
  111. package/dist/core/inline/format-toggle.js +94 -40
  112. package/dist/core/inline/image-dimensions.d.ts +3 -0
  113. package/dist/core/inline/image-dimensions.js +46 -10
  114. package/dist/core/inline/inline-widgets.d.ts +19 -0
  115. package/dist/core/inline/inline-widgets.js +5 -0
  116. package/dist/core/inline/scan/brackets.js +1 -0
  117. package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
  118. package/dist/core/inline/scan/plugin-syntax.js +15 -1
  119. package/dist/core/inline/transparency.js +3 -3
  120. package/dist/core/inline-render.d.ts +6 -0
  121. package/dist/core/inline-render.js +32 -0
  122. package/dist/core/nodes.d.ts +14 -0
  123. package/dist/cursor/edge-affinity.js +2 -1
  124. package/dist/cursor/height-oracle.d.ts +2 -3
  125. package/dist/cursor/height-oracle.js +0 -1
  126. package/dist/cursor/overlay-remeasure.js +8 -0
  127. package/dist/cursor/reveal-source.js +7 -2
  128. package/dist/cursor/scroll-hold.d.ts +10 -0
  129. package/dist/cursor/scroll-hold.js +22 -0
  130. package/dist/cursor/scrollport.d.ts +9 -0
  131. package/dist/cursor/scrollport.js +30 -1
  132. package/dist/cursor/visual-lines.d.ts +5 -4
  133. package/dist/cursor/visual-lines.js +56 -11
  134. package/dist/cursor/widget-edge-snap.d.ts +28 -0
  135. package/dist/cursor/widget-edge-snap.js +38 -0
  136. package/dist/cursor/widget-offset.d.ts +9 -0
  137. package/dist/cursor/widget-offset.js +71 -4
  138. package/dist/debug/interaction-trace.d.ts +4 -0
  139. package/dist/debug/interaction-trace.js +15 -0
  140. package/dist/decorations/decoration-state.svelte.js +1 -1
  141. package/dist/decorations/reserved-attrs.js +1 -0
  142. package/dist/editor-actions/ancestry-folds.d.ts +2 -2
  143. package/dist/editor-actions/ancestry-folds.js +1 -1
  144. package/dist/editor-actions/block-edit-scope.js +1 -1
  145. package/dist/editor-actions/commit/text-batch.d.ts +3 -2
  146. package/dist/editor-actions/commit/text-batch.js +1 -1
  147. package/dist/editor-actions/commit/undo-controller.js +4 -2
  148. package/dist/editor-actions/container-edit.js +2 -1
  149. package/dist/editor-actions/enter-completion.d.ts +2 -0
  150. package/dist/editor-actions/enter-completion.js +23 -2
  151. package/dist/editor-actions/focus/focus-dispatch.js +7 -4
  152. package/dist/editor-actions/focus/focus-landing.d.ts +10 -1
  153. package/dist/editor-actions/focus/focus-landing.js +20 -6
  154. package/dist/editor-actions/inline-range-commit.js +1 -1
  155. package/dist/editor-actions/plugin/container.d.ts +7 -0
  156. package/dist/editor-actions/plugin/container.js +3 -1
  157. package/dist/editor-actions/reorder-action.js +19 -10
  158. package/dist/editor-actions/reorder-drag.js +29 -1
  159. package/dist/editor-actions/replacement-focus.d.ts +1 -1
  160. package/dist/editor-actions/replacement-focus.js +1 -1
  161. package/dist/editor-actions/search-replace.js +1 -1
  162. package/dist/editor-actions/table-context.d.ts +4 -1
  163. package/dist/editor-actions/table-context.js +57 -1
  164. package/dist/editor-events.d.ts +3 -0
  165. package/dist/editor-keys.d.ts +33 -0
  166. package/dist/editor-props.d.ts +24 -12
  167. package/dist/index.d.ts +1 -1
  168. package/dist/plugin.d.ts +7 -0
  169. package/dist/plugin.js +16 -0
  170. package/dist/plugins/latex/BlockMath.svelte +264 -29
  171. package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
  172. package/dist/plugins/latex/index.d.ts +2 -1
  173. package/dist/plugins/latex/latex-kind.js +60 -10
  174. package/dist/plugins/latex/math-completion.js +4 -1
  175. package/dist/plugins/latex/math-layout.d.ts +15 -0
  176. package/dist/plugins/latex/math-layout.js +13 -0
  177. package/dist/plugins/latex/math-source.d.ts +19 -0
  178. package/dist/plugins/latex/math-source.js +103 -0
  179. package/dist/plugins/latex/register.d.ts +11 -2
  180. package/dist/plugins/latex/register.js +3 -1
  181. package/dist/plugins/latex/renderer.d.ts +3 -3
  182. package/dist/plugins/latex/renderer.js +13 -6
  183. package/dist/plugins/mermaid/MermaidBlock.svelte +19 -9
  184. package/dist/plugins/parrot/ParrotBlock.svelte +3 -1
  185. package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
  186. package/dist/reactivity/list-windowing.svelte.js +75 -30
  187. package/dist/schema/block-completions.d.ts +8 -0
  188. package/dist/schema/block-completions.js +11 -0
  189. package/dist/schema/commands.d.ts +9 -5
  190. package/dist/schema/commands.js +12 -7
  191. package/dist/schema/context-actions.d.ts +31 -0
  192. package/dist/schema/context-actions.js +23 -0
  193. package/dist/schema/fenced-code-raw.js +31 -1
  194. package/dist/schema/operations.d.ts +8 -1
  195. package/dist/schema/reserved-chords.js +37 -4
  196. package/dist/schema/table-cell-raw.d.ts +1 -1
  197. package/dist/schema/table-cell-raw.js +1 -1
  198. package/dist/selection/block-hit-test.js +3 -2
  199. package/dist/selection/char-endpoint-snap.js +1 -1
  200. package/dist/selection/clipboard-text.js +6 -1
  201. package/dist/selection/covered-block.d.ts +10 -0
  202. package/dist/selection/covered-block.js +24 -0
  203. package/dist/selection/cross-block/dispatch.d.ts +3 -0
  204. package/dist/selection/cross-block/dispatch.js +13 -1
  205. package/dist/selection/cross-block/format-range.d.ts +1 -1
  206. package/dist/selection/cross-block/format-range.js +4 -15
  207. package/dist/selection/cross-block/format-toggle.js +1 -1
  208. package/dist/selection/cross-block/keydown.js +3 -29
  209. package/dist/selection/cross-block/ops.js +1 -1
  210. package/dist/selection/cross-block/paste.js +31 -35
  211. package/dist/selection/cross-block/type-replace.d.ts +3 -2
  212. package/dist/selection/cross-block/type-replace.js +52 -13
  213. package/dist/selection/dead-space-caret.d.ts +13 -0
  214. package/dist/selection/dead-space-caret.js +57 -1
  215. package/dist/selection/drag-pointer.d.ts +26 -2
  216. package/dist/selection/drag-pointer.js +103 -2
  217. package/dist/selection/gap-caret.js +1 -1
  218. package/dist/selection/keyboard-extend.d.ts +3 -2
  219. package/dist/selection/keyboard-extend.js +4 -3
  220. package/dist/selection/multi-click.d.ts +42 -0
  221. package/dist/selection/multi-click.js +203 -0
  222. package/dist/selection/native-bridge.d.ts +3 -0
  223. package/dist/selection/native-bridge.js +31 -2
  224. package/dist/selection/path-lookup.d.ts +10 -2
  225. package/dist/selection/path-lookup.js +11 -4
  226. package/dist/selection/pointer-gesture.d.ts +9 -0
  227. package/dist/selection/pointer-gesture.js +11 -0
  228. package/dist/selection/primitives.d.ts +7 -0
  229. package/dist/selection/primitives.js +19 -1
  230. package/dist/selection/range-delete-ceremony.js +4 -2
  231. package/dist/selection/range-delete-chrome.js +2 -1
  232. package/dist/selection/range-delete-table-coverage.js +2 -1
  233. package/dist/selection/range-delete-table.js +3 -2
  234. package/dist/selection/range-delete.d.ts +4 -0
  235. package/dist/selection/range-delete.js +31 -3
  236. package/dist/selection/selection-drop.d.ts +34 -0
  237. package/dist/selection/selection-drop.js +253 -0
  238. package/dist/selection/selection-restore.js +1 -1
  239. package/dist/selection/selection-state.svelte.d.ts +6 -0
  240. package/dist/selection/selection-state.svelte.js +36 -1
  241. package/dist/selection/table-endpoint-snap.js +1 -1
  242. package/dist/selection/table-rect-extend.js +1 -1
  243. package/dist/styles/editor-theme.css +75 -31
  244. package/dist/styles/editor.css +268 -18
  245. package/dist/testing/container-conformance.js +2 -2
  246. package/dist/testing/inline-conformance.js +2 -1
  247. package/dist/tree-operations/blockquote.js +1 -1
  248. package/dist/tree-operations/chain-rebuild.d.ts +63 -0
  249. package/dist/tree-operations/chain-rebuild.js +142 -0
  250. package/dist/tree-operations/children.d.ts +1 -1
  251. package/dist/tree-operations/children.js +1 -1
  252. package/dist/tree-operations/cleanup.js +1 -1
  253. package/dist/tree-operations/content-write.d.ts +50 -0
  254. package/dist/tree-operations/content-write.js +263 -0
  255. package/dist/tree-operations/index.d.ts +8 -3
  256. package/dist/tree-operations/index.js +6 -2
  257. package/dist/tree-operations/list/exit-replacement.js +1 -1
  258. package/dist/tree-operations/list/unwrap-merge.js +3 -3
  259. package/dist/tree-operations/node-ops.d.ts +17 -234
  260. package/dist/tree-operations/node-ops.js +47 -1113
  261. package/dist/tree-operations/node-primitives.d.ts +75 -0
  262. package/dist/tree-operations/node-primitives.js +117 -0
  263. package/dist/tree-operations/paste/apply.js +1 -1
  264. package/dist/tree-operations/paste/body-write.d.ts +1 -1
  265. package/dist/tree-operations/paste/body-write.js +2 -2
  266. package/dist/tree-operations/paste/container-match.js +4 -2
  267. package/dist/tree-operations/paste/dispatch.js +2 -1
  268. package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
  269. package/dist/tree-operations/paste/focus-target.d.ts +1 -1
  270. package/dist/tree-operations/paste/list-absorb.js +1 -1
  271. package/dist/tree-operations/paste/list-break-out.js +1 -1
  272. package/dist/tree-operations/paste/parent-scope.js +1 -1
  273. package/dist/tree-operations/paste/paste-replacement.js +1 -1
  274. package/dist/tree-operations/paste/replace-block-at-parent.d.ts +3 -1
  275. package/dist/tree-operations/paste/replace-block-at-parent.js +5 -2
  276. package/dist/tree-operations/paste/replacement-parse.d.ts +17 -0
  277. package/dist/tree-operations/paste/replacement-parse.js +22 -0
  278. package/dist/tree-operations/path-mutate.d.ts +1 -1
  279. package/dist/tree-operations/path-mutate.js +2 -1
  280. package/dist/tree-operations/reorder-unit.js +1 -1
  281. package/dist/tree-operations/reorder.d.ts +5 -2
  282. package/dist/tree-operations/reorder.js +55 -2
  283. package/dist/tree-operations/settle.d.ts +105 -0
  284. package/dist/tree-operations/settle.js +660 -0
  285. package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
  286. package/dist/tree-operations/table-grid-clipboard.js +90 -0
  287. package/dist/tree-operations/unshare.d.ts +15 -77
  288. package/dist/tree-operations/unshare.js +15 -166
  289. package/docs/guide/consumer-guide.md +136 -104
  290. package/docs/guide/plugin-api.md +73 -29
  291. package/docs/guide/plugin-guide.md +55 -23
  292. package/package.json +9 -7
  293. package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
  294. package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
  295. package/dist/components/blocks/table/TableGrip.svelte +0 -91
  296. package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
  297. package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
  298. package/dist/components/blocks/table/table-drop-target.js +0 -16
  299. package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
  300. package/dist/components/blocks/table/table-reorder-drag.js +0 -97
@@ -3,7 +3,7 @@
3
3
  * No Svelte context or reactivity; inputs are passed in as parameters.
4
4
  */
5
5
  import { CURSOR_END, CURSOR_START } from '../../block-component';
6
- import { consumeStickyLanding } from './focus-landing';
6
+ import { consumeStickyLanding, verticalArrival } from './focus-landing';
7
7
  /** Move focus within a container, or delegate upward when out of range. */
8
8
  export async function dispatchMoveFocus(refs, innerIndex, position, stickyColumn, parent, scope = {}) {
9
9
  const { childCount, options, gapStop } = scope;
@@ -89,13 +89,16 @@ export function dispatchFocusAtColumn(refs, x, from) {
89
89
  if (refs.length === 0)
90
90
  return;
91
91
  const indices = from === 'above' ? refs.map((_, i) => i) : refs.map((_, i) => refs.length - 1 - i);
92
- // Skip vertically-transparent refs so an entry from above/below lands on the
93
- // first/last text-bearing child, not an image-only paragraph.
92
+ // Pass over transparent refs so an entry from above/below lands on the first/last
93
+ // text-bearing child.
94
94
  for (const i of indices) {
95
95
  const ref = refs[i];
96
96
  if (!ref?.focusable)
97
97
  continue;
98
- if (ref.isVerticallyTransparent?.())
98
+ const arrival = verticalArrival(ref, from);
99
+ if (arrival === 'entered')
100
+ return;
101
+ if (arrival === 'transparent')
99
102
  continue;
100
103
  if (ref.focusAtColumn)
101
104
  ref.focusAtColumn(x, from);
@@ -2,6 +2,15 @@
2
2
  * Shared cross-block landing tail for the focus dispatchers. Sticky-X null handling
3
3
  * lives here, so `focusAtColumn` receivers always get a finite x.
4
4
  */
5
- import { type BlockComponent, type FocusPosition } from '../../block-component';
5
+ import { type BlockComponent, type FocusPosition, type StickyColumnDirection } from '../../block-component';
6
6
  import type { StickyColumnState } from '../../cursor/sticky-column';
7
7
  export declare function consumeStickyLanding(block: BlockComponent, index: number, position: FocusPosition, stickyColumn: StickyColumnState, retryAt: (index: number) => Promise<void> | void): Promise<void>;
8
+ /** What a vertical arrival does at a block: enter its widget, pass over it, or seat a caret. */
9
+ export type VerticalArrival = 'entered' | 'transparent' | 'seat';
10
+ /**
11
+ * The vertical stop rule, asked by both vertical doors: the per-block landing and a container's
12
+ * column entry. A widget-only block carries no column, so it is passed over unless its edge widget
13
+ * takes the arrival, which is a stop of its own and reads alike from either side. `'entered'` means
14
+ * the widget ALREADY took it, so the caller stops rather than repeating the entry.
15
+ */
16
+ export declare function verticalArrival(block: BlockComponent, from: StickyColumnDirection): VerticalArrival;
@@ -5,12 +5,15 @@
5
5
  import { CURSOR_END, CURSOR_START } from '../../block-component';
6
6
  export async function consumeStickyLanding(block, index, position, stickyColumn, retryAt) {
7
7
  const isStickyMove = typeof position === 'object' && 'stickyColumnFrom' in position;
8
- // Widget-only blocks contribute no column landing, so ArrowUp/Down passes through.
9
- // Horizontal moves still stop at the widget edge.
10
- if (isStickyMove && block.isVerticallyTransparent?.()) {
11
- const direction = position.stickyColumnFrom === 'below' ? -1 : 1;
12
- await retryAt(index + direction);
13
- return;
8
+ if (isStickyMove) {
9
+ const from = position.stickyColumnFrom;
10
+ const arrival = verticalArrival(block, from);
11
+ if (arrival === 'entered')
12
+ return;
13
+ if (arrival === 'transparent') {
14
+ await retryAt(index + (from === 'below' ? -1 : 1));
15
+ return;
16
+ }
14
17
  }
15
18
  // Enter an edge widget rather than dropping a no-op caret at its boundary, so the
16
19
  // arrow key produces one visible step.
@@ -37,3 +40,14 @@ export async function consumeStickyLanding(block, index, position, stickyColumn,
37
40
  else
38
41
  block.focus(CURSOR_END);
39
42
  }
43
+ /**
44
+ * The vertical stop rule, asked by both vertical doors: the per-block landing and a container's
45
+ * column entry. A widget-only block carries no column, so it is passed over unless its edge widget
46
+ * takes the arrival, which is a stop of its own and reads alike from either side. `'entered'` means
47
+ * the widget ALREADY took it, so the caller stops rather than repeating the entry.
48
+ */
49
+ export function verticalArrival(block, from) {
50
+ if (!block.isVerticallyTransparent?.())
51
+ return 'seat';
52
+ return block.enterEdgeWidget?.(from === 'above' ? 'start' : 'end') ? 'entered' : 'transparent';
53
+ }
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { docPathFrom } from '../cursor/coordinate-spaces';
7
7
  import { expectStateForNode } from '../reactivity/state-registry';
8
- import { isBlockNode, nodeAt, normalizeOwnRaw, writeOwnRaw } from '../tree-operations/node-ops';
8
+ import { isBlockNode, nodeAt, normalizeOwnRaw, writeOwnRaw } from '../tree-operations/node-primitives';
9
9
  import { ensureUnsharedChild, ensureUnsharedPath } from '../tree-operations/unshare';
10
10
  export function createInlineRangeCommit(deps) {
11
11
  async function commitInlineRange(path, start, end, bytes, caretAfter) {
@@ -87,6 +87,13 @@ export interface ContainerBlock {
87
87
  * entry and windowing reveal. False for a modified or non-arrow key: leave it native.
88
88
  */
89
89
  moveFocusOut(e: KeyboardEvent): boolean;
90
+ /**
91
+ * The reader's scroll position, captured now. Call before a state flip that swaps this
92
+ * block's view for one of a different height, then await the returned restore: the
93
+ * scrollport is back where it was once the swap has flushed. A reveal in flight outranks
94
+ * it and the restore stands down.
95
+ */
96
+ captureScrollPosition(): () => Promise<void>;
90
97
  }
91
98
  /**
92
99
  * Collapse-ness has ONE definition: the descriptor's `reservedChrome.isCollapsed`
@@ -13,6 +13,7 @@ import { eventToChord } from '../../schema/keybindings';
13
13
  import { isReadingMode } from '../../presentation-mode';
14
14
  import { devWarn } from '../../dev-warn';
15
15
  import { BLOCK_EDIT_KEY, CONTAINER_EDIT_KEY, EDITOR_DOC_KEY, EDITOR_POLICIES_KEY, EDITOR_SERVICES_KEY, FOCUS_KEY, HISTORY_KEY } from '../../editor-keys';
16
+ import { captureScrollPosition } from '../../cursor/scroll-hold';
16
17
  import { emitCommandError } from '../../editor-events';
17
18
  import { owningPluginEditor } from '../../schema/plugin-install';
18
19
  import { createBlockListState } from '../../reactivity/block-list-state.svelte';
@@ -130,7 +131,7 @@ export function createContainerBlock(deps) {
130
131
  const parentFocus = getContext(FOCUS_KEY);
131
132
  const parentContainerEdit = getContext(CONTAINER_EDIT_KEY);
132
133
  const history = getContext(HISTORY_KEY);
133
- const { stickyColumn, edgeAffinity, selection, reorder, events: editorEvents, registryView, activePlugins } = getContext(EDITOR_SERVICES_KEY);
134
+ const { stickyColumn, edgeAffinity, selection, reorder, revealAnchor, events: editorEvents, registryView, activePlugins } = getContext(EDITOR_SERVICES_KEY);
134
135
  const { keybindingOverrides, presentationMode: getPresentationMode, theme: getTheme } = getContext(EDITOR_POLICIES_KEY);
135
136
  const editorDoc = getContext(EDITOR_DOC_KEY);
136
137
  const pluginEditor = editorDoc?.pluginEditor;
@@ -344,6 +345,7 @@ export function createContainerBlock(deps) {
344
345
  updateOwnMetadata,
345
346
  handleKeydown,
346
347
  moveFocusOut,
348
+ captureScrollPosition: () => captureScrollPosition(deps.getBoxEl(), () => revealAnchor.get() !== null),
347
349
  getPresentationMode,
348
350
  getTheme,
349
351
  getOptions
@@ -9,7 +9,7 @@
9
9
  import { CURSOR_START } from '../block-component';
10
10
  import { reorderChildrenWithTrivia } from '../tree-operations/reorder';
11
11
  import { resolveReorderUnit } from '../tree-operations/reorder-unit';
12
- import { blockNodeAt, nodeAt } from '../tree-operations/node-ops';
12
+ import { blockNodeAt, nodeAt } from '../tree-operations/node-primitives';
13
13
  import { renumberOrderedList } from '../tree-operations/list/ordered-markers';
14
14
  import { expectStateForNode } from '../reactivity/state-registry';
15
15
  import { readCurrentSelection } from '../selection/native-bridge';
@@ -25,7 +25,7 @@ export function createReorderAction(deps, controller, onReorder) {
25
25
  function caretOffset() {
26
26
  return readCurrentSelection(deps.selectionState, deps.blockRefs)?.focus.offset ?? 0;
27
27
  }
28
- async function commitReorder(unit, to, offset) {
28
+ async function commitReorder(unit, to, offset, focusAfter) {
29
29
  let landing = to;
30
30
  if (unit.scope === 'document') {
31
31
  await controller.commitStructural({
@@ -36,11 +36,14 @@ export function createReorderAction(deps, controller, onReorder) {
36
36
  eventPath: docPathFrom([unit.index])
37
37
  },
38
38
  mutate: (children) => {
39
- const settled = reorderChildrenWithTrivia(children, unit.index, to, deps.sharing);
39
+ const settled = reorderChildrenWithTrivia(children, unit.index, to, deps.sharing, true);
40
40
  landing = settled.landing;
41
41
  return settled.change;
42
42
  },
43
- afterTick: () => deps.blockRefs[landing]?.focus(CURSOR_START)
43
+ afterTick: () => {
44
+ if (focusAfter)
45
+ deps.blockRefs[landing]?.focus(CURSOR_START);
46
+ }
44
47
  });
45
48
  return { landing, total: deps.doc.children.length };
46
49
  }
@@ -69,7 +72,10 @@ export function createReorderAction(deps, controller, onReorder) {
69
72
  }
70
73
  return settled.change;
71
74
  },
72
- afterTick: () => state.innerBlockRefs[landing]?.focus(CURSOR_START)
75
+ afterTick: () => {
76
+ if (focusAfter)
77
+ state.innerBlockRefs[landing]?.focus(CURSOR_START);
78
+ }
73
79
  });
74
80
  // Re-resolved, not `parent`: the ceremony's copy-on-write replaced that node, so the one
75
81
  // this action resolved still holds the pre-move children.
@@ -86,19 +92,22 @@ export function createReorderAction(deps, controller, onReorder) {
86
92
  return null;
87
93
  return { unit, to };
88
94
  }
89
- async function run(fromPath, computeTo) {
95
+ async function run(fromPath, computeTo, focusAfter) {
90
96
  const target = resolveAndClamp(fromPath, computeTo);
91
97
  if (!target)
92
98
  return;
93
99
  // Drop any cross-block selection so the overlay doesn't fight the move; the
94
- // commit's afterTick re-places the caret.
100
+ // commit's afterTick re-places the caret when the caller wants it.
95
101
  deps.selectionState.collapse();
96
- const outcome = await commitReorder(target.unit, target.to, caretOffset());
102
+ const outcome = await commitReorder(target.unit, target.to, caretOffset(), focusAfter);
97
103
  if (outcome)
98
104
  onReorder?.(outcome.landing, outcome.total);
99
105
  }
100
106
  return {
101
- moveReorderUnit: (fromPath, toIndex) => run(fromPath, () => toIndex),
102
- nudgeReorderUnit: (fromPath, dir) => run(fromPath, (index) => index + dir)
107
+ // A dropped block is not a block you asked to edit: focusing it opens whatever a caret
108
+ // opens there (an equation reveals its source), which is not what a drag asked for.
109
+ // The keyboard nudge is the opposite — the caret must ride the block it is moving.
110
+ moveReorderUnit: (fromPath, toIndex) => run(fromPath, () => toIndex, false),
111
+ nudgeReorderUnit: (fromPath, dir) => run(fromPath, (index) => index + dir, true)
103
112
  };
104
113
  }
@@ -144,9 +144,37 @@ function indexOf(host) {
144
144
  return readBlockPath(host)?.at(-1) ?? null;
145
145
  return readBlockPath(host.querySelector('[data-block-path]'))?.at(-2) ?? null;
146
146
  }
147
+ /**
148
+ * What the block IS, for the kinds whose text does not read as a label: a table's cells run
149
+ * together into `IngredientAmountWater35 L`, an equation reads as its own source. Prose keeps
150
+ * its first words, which is the best label it could have.
151
+ */
152
+ const KIND_LABELS = {
153
+ table: 'Table',
154
+ mathBlock: 'Equation',
155
+ fencedCode: 'Code',
156
+ mermaid: 'Diagram',
157
+ thematicBreak: 'Divider',
158
+ details: 'Details',
159
+ image: 'Image'
160
+ };
147
161
  function ghostLabel(host) {
162
+ const kind = host.dataset.blockKind ?? '';
163
+ if (kind === 'table')
164
+ return tableLabel(host);
165
+ const named = KIND_LABELS[kind];
166
+ if (named)
167
+ return named;
148
168
  const text = (host.textContent ?? '').trim().replace(/\s+/g, ' ');
149
169
  if (!text)
150
- return 'block';
170
+ return host.querySelector('[data-image-widget]') ? 'Image' : 'Block';
151
171
  return text.length > 40 ? text.slice(0, 40) + '…' : text;
152
172
  }
173
+ /** Body rows × columns: the shape is what tells one table from another at a glance. */
174
+ function tableLabel(host) {
175
+ const rows = host.querySelectorAll('.table-row').length;
176
+ const cols = host.querySelector('.table-row')?.childElementCount ?? 0;
177
+ if (rows === 0 || cols === 0)
178
+ return 'Table';
179
+ return `Table · ${rows} × ${cols}`;
180
+ }
@@ -3,7 +3,7 @@
3
3
  * probe that picks structural-vs-typing, and the post-commit caret restore.
4
4
  */
5
5
  import { updateNodeContent } from '../tree-operations';
6
- import { type SettledContent } from '../tree-operations/node-ops';
6
+ import { type SettledContent } from '../tree-operations/content-write';
7
7
  import { type AnyBlockKind } from '../core/nodes';
8
8
  import type { NodeView } from '../core/node-views';
9
9
  import type { StructuralChange } from '../tree-operations/structural-change';
@@ -3,7 +3,7 @@
3
3
  * probe that picks structural-vs-typing, and the post-commit caret restore.
4
4
  */
5
5
  import { updateNodeContent } from '../tree-operations';
6
- import { settledCaretTarget } from '../tree-operations/node-ops';
6
+ import { settledCaretTarget } from '../tree-operations/content-write';
7
7
  import { makeBlockNode } from '../core/nodes';
8
8
  import { readBlockPath } from '../selection/path-lookup';
9
9
  // ── Reparse probe ────────────────────────────────────────────────────────────
@@ -2,7 +2,7 @@ import { parse } from '../core/parser';
2
2
  import { cloneNode } from '../tree-operations/clone';
3
3
  import { spliceMany } from '../tree-operations/splice-many';
4
4
  import { getBlockKindDescriptor } from '../schema/block-kind-descriptor';
5
- import { normalizeBodyWrite, normalizeReplacementTrivia, writeOwnRaw } from '../tree-operations/node-ops';
5
+ import { normalizeBodyWrite, normalizeReplacementTrivia, writeOwnRaw } from '../tree-operations/node-primitives';
6
6
  import { rebuildAncestryRaw } from '../schema/container-raw';
7
7
  import { replacePreservingFirst, stampStructuralChange } from '../tree-operations/structural-change';
8
8
  import { applyRangesToText } from '../search/replace';
@@ -4,6 +4,7 @@
4
4
  * live here.
5
5
  */
6
6
  import type { CellPosition, ContainerEditActions, TableContext } from '../action-contracts';
7
+ import type { GrammarView } from '../schema/block-openers';
7
8
  import type { NodeView } from '../core/node-views';
8
9
  import type { UndoController } from './deps';
9
10
  import type { BlockListState } from '../reactivity/block-list-state.svelte';
@@ -29,6 +30,8 @@ export interface TableMutationsContextDeps {
29
30
  controller: UndoController;
30
31
  focusCell: (rowIdx: number, colIdx: number, position: CellPosition) => void;
31
32
  announceReorder: (message: string) => void;
33
+ /** The instance grammar the cell byte-write door threads (absent: the global one). */
34
+ grammar?: GrammarView;
32
35
  }
33
- export type TableMutationsContext = Pick<TableContext, 'insertRowAbove' | 'insertRowBelow' | 'insertColumnLeft' | 'insertColumnRight' | 'deleteRow' | 'deleteColumn' | 'reorderRowTo' | 'moveRowUp' | 'moveRowDown' | 'reorderColumnTo' | 'moveColumnLeft' | 'moveColumnRight' | 'cycleAlignment' | 'setColumnAlignment'>;
36
+ export type TableMutationsContext = Pick<TableContext, 'insertRowAbove' | 'insertRowBelow' | 'insertColumnLeft' | 'insertColumnRight' | 'deleteRow' | 'deleteColumn' | 'reorderRowTo' | 'moveRowUp' | 'moveRowDown' | 'reorderColumnTo' | 'moveColumnLeft' | 'moveColumnRight' | 'cycleAlignment' | 'setColumnAlignment' | 'pasteGrid'>;
34
37
  export declare function createTableMutationsContext(deps: TableMutationsContextDeps): TableMutationsContext;
@@ -8,8 +8,9 @@ import { extendDocPath, docPathFrom } from '../cursor/coordinate-spaces';
8
8
  import { getStateForNode } from '../reactivity/state-registry';
9
9
  import { assertInvariant } from '../assert';
10
10
  import { columnAligned, COLUMN_ALIGNMENT_CLEARED, DELETED_COLUMN, DELETED_ROW, INSERTED_COLUMN, INSERTED_ROW, movedColumnToPosition, movedRowToPosition } from '../a11y-strings';
11
- import { ensureUnsharedChildren } from '../tree-operations/unshare';
11
+ import { ensureUnsharedChildren, ensureUnsharedSubtree } from '../tree-operations/unshare';
12
12
  import { rebuildTableRowRaw, rebuildTableRaw } from '../schema/container-rebuilders';
13
+ import { writeOwnRaw } from '../tree-operations/node-primitives';
13
14
  import { reorderChildren } from '../tree-operations/reorder';
14
15
  import { insertEmptyRow, insertEmptyColumn, deleteRow as mutDeleteRow, deleteColumn as mutDeleteColumn, moveColumn as mutMoveColumn, cycleAlignment as mutCycleAlignment, setAlignment as mutSetAlignment, canDeleteRow, canDeleteColumn } from '../tree-operations/table-mutations';
15
16
  /**
@@ -105,6 +106,60 @@ export function createTableMutationsContext(deps) {
105
106
  afterTick: opts.afterTick
106
107
  });
107
108
  }
109
+ // Grown at the bottom and the right only, so each scope's change is one contiguous insert:
110
+ // rows for the table, cells for every mounted row. Cell texts go through the cell kind's own
111
+ // raw rule in place, and the table raw is rebuilt once, delimiter line included.
112
+ async function pasteGrid(origin, grid) {
113
+ const { myPath, controller, focusCell } = deps;
114
+ const rows = grid.length;
115
+ let cols = 0;
116
+ for (const line of grid)
117
+ cols = Math.max(cols, line.length);
118
+ if (rows === 0 || cols === 0)
119
+ return;
120
+ const { scopes, rowIndices } = mountedColumnScopes();
121
+ await controller.commitMultiScope({
122
+ scopes,
123
+ snapshot: { path: docPathFrom(myPath), offset: 0 },
124
+ mutate: ([tableScope, ...rowScopes]) => {
125
+ // Subtree, not children: the cell writes below land at depth two, and a row's
126
+ // cells stay shared with the undo snapshot when only the rows are unshared —
127
+ // the write would go through it and undo would restore the pasted bytes.
128
+ ensureUnsharedSubtree(tableScope.node, tableScope.sharing);
129
+ assertInvariant('column-scope-alignment', () => rowScopes.every((s, i) => s.node === tableScope.node.children?.[rowIndices[i]])
130
+ ? null
131
+ : {
132
+ code: 'column-scope-alignment',
133
+ message: 'pasteGrid: row scopes misaligned with owned table children'
134
+ });
135
+ const table = tableScope.node;
136
+ const oldRows = table.children?.length ?? 0;
137
+ const oldCols = metadataOf(table, 'table').columnCount;
138
+ const needRows = origin.rowIdx + rows;
139
+ const needCols = origin.colIdx + cols;
140
+ for (let r = oldRows; r < needRows; r++)
141
+ insertEmptyRow(table, r - 1, 'below');
142
+ for (let c = oldCols; c < needCols; c++)
143
+ insertEmptyColumn(table, c - 1, 'right');
144
+ grid.forEach((line, r) => {
145
+ const cells = table.children[origin.rowIdx + r].children;
146
+ line.forEach((text, c) => writeOwnRaw(cells[origin.colIdx + c], text, deps.grammar));
147
+ });
148
+ rebuildTableRaw(table);
149
+ const addedRows = needRows - oldRows;
150
+ const addedCols = needCols - oldCols;
151
+ const tableChange = addedRows > 0 ? { op: 'insert', at: oldRows, count: addedRows } : { op: 'noop' };
152
+ const rowChange = addedCols > 0 ? { op: 'insert', at: oldCols, count: addedCols } : { op: 'noop' };
153
+ return [tableChange, ...rowScopes.map(() => rowChange)];
154
+ },
155
+ op: {
156
+ kind: 'tablePasteGrid',
157
+ detail: { rowIdx: origin.rowIdx, colIdx: origin.colIdx, rows, cols },
158
+ eventPath: docPathFrom(myPath)
159
+ },
160
+ afterTick: () => focusCell(origin.rowIdx + rows - 1, origin.colIdx + cols - 1, 'end')
161
+ });
162
+ }
108
163
  async function insertColumn(colIdx, side) {
109
164
  const { focusCell, focusedCell } = deps;
110
165
  const insertAt = side === 'left' ? colIdx : colIdx + 1;
@@ -187,6 +242,7 @@ export function createTableMutationsContext(deps) {
187
242
  return {
188
243
  insertRowAbove: (rowIdx) => insertRow(rowIdx, 'above'),
189
244
  insertRowBelow: (rowIdx) => insertRow(rowIdx, 'below'),
245
+ pasteGrid,
190
246
  insertColumnLeft: (colIdx) => insertColumn(colIdx, 'left'),
191
247
  insertColumnRight: (colIdx) => insertColumn(colIdx, 'right'),
192
248
  reorderRowTo,
@@ -54,6 +54,9 @@ export interface EditorEventMap {
54
54
  /** The theme name after a `theme` prop change (never fired at mount), for a plugin
55
55
  * that paints its own colors and so cannot pick the change up from CSS. */
56
56
  themeChange: string;
57
+ /** `true` when an editor-owned menu (right-click, insert `+`) opens, `false` when it closes,
58
+ * so host chrome over the selection can step aside rather than stack on it. */
59
+ menuChange: boolean;
57
60
  }
58
61
  export interface EditorEvents {
59
62
  on<K extends keyof EditorEventMap>(event: K, handler: (payload: EditorEventMap[K]) => void): () => void;
@@ -45,6 +45,35 @@ export interface PastedImage {
45
45
  /** Host import hook for pasted images: resolves to the markdown to insert, or null
46
46
  * to skip that image. Called once per image file, in clipboard order. */
47
47
  export type PasteImageHook = (image: PastedImage) => Promise<string | null>;
48
+ /** What a code block hands its host when the run affordance is pressed. */
49
+ export interface CodeRunRequest {
50
+ /** The fence body alone — opener and closer lines excluded. */
51
+ code: string;
52
+ /** The opener's full info string, untrimmed of trailing attributes (`py {1-3}`). */
53
+ info: string;
54
+ /** Child indices from the document root to this block. */
55
+ path: number[];
56
+ }
57
+ /**
58
+ * Host hook for executing a code block. The editor runs nothing itself: installing this is
59
+ * what puts the run affordance on the block's rail, and the host owns everything after —
60
+ * the engine, the result, and where output goes. Absent, no run affordance renders.
61
+ */
62
+ export type RunCodeHook = (request: CodeRunRequest) => void;
63
+ /** One entry in a code block's overflow menu. `run` is called with the menu already closed. */
64
+ export interface CodeMenuItem {
65
+ id: string;
66
+ label: string;
67
+ run: () => void;
68
+ /** Renders dimmed and refuses activation. */
69
+ disabled?: boolean;
70
+ }
71
+ /**
72
+ * Host hook for the code block's overflow menu, consulted each time the menu opens so the
73
+ * items can read live state. Absent — or returning nothing — renders no overflow affordance:
74
+ * the editor has no app-level actions of its own to put there.
75
+ */
76
+ export type CodeMenuItemsHook = (request: CodeRunRequest) => readonly CodeMenuItem[];
48
77
  export type PresentationModeGetter = () => PresentationMode;
49
78
  /** The editor's theme name, as reflected to `data-editor-theme`. An open string:
50
79
  * built-ins are `'dark'`/`'light'`, and a consumer may name its own. */
@@ -150,6 +179,10 @@ export interface EditorPolicies {
150
179
  /** Set-once host import hook for image-bearing pastes. Required-nullable: a mount must
151
180
  * answer, and `undefined` deliberately leaves the paste on the text/plain path. */
152
181
  onPasteImage: PasteImageHook | undefined;
182
+ /** Set-once host execution hook; its presence is what renders the run affordance. */
183
+ onRunCode: RunCodeHook | undefined;
184
+ /** Set-once host menu hook; its presence is what renders the overflow affordance. */
185
+ codeMenuItems: CodeMenuItemsHook | undefined;
153
186
  /** Resolved image URLs that failed to load this session. One Set per instance, so a
154
187
  * failed load never suppresses another editor's broken-state recompute
155
188
  * (`components/image/widget-dom.ts`). */
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { Snippet } from 'svelte';
6
6
  import type { AnyBlockKind } from './core/nodes';
7
- import type { PasteImageHook, ResolveImageUrl, ResolveLinkUrl } from './editor-keys';
7
+ import type { CodeMenuItemsHook, PasteImageHook, ResolveImageUrl, ResolveLinkUrl, RunCodeHook } from './editor-keys';
8
8
  import type { ImageLoadPolicy } from './core/inline-render';
9
9
  import type { PresentationMode } from './presentation-mode';
10
10
  import type { KeybindingOverride } from './schema/keybinding-overrides';
@@ -27,16 +27,29 @@ export interface EditorProps {
27
27
  * in order and the markdown returned is inserted at the caret; `null` skips it.
28
28
  * Installing it takes the WHOLE paste — the clipboard's `text/plain` is not pasted. */
29
29
  onPasteImage?: PasteImageHook;
30
+ /** Execution hook for code blocks, set once at mount. The editor runs nothing itself:
31
+ * installing this is what puts the run affordance on a code block's rail, and the host
32
+ * owns the engine, the result, and where output goes. Absent, no run affordance renders. */
33
+ onRunCode?: RunCodeHook;
34
+ /** Overflow-menu hook for code blocks, set once at mount and consulted each time a menu
35
+ * opens so items can read live state. Absent, or returning nothing, renders no overflow
36
+ * affordance — the editor has no app-level actions of its own to offer there. */
37
+ codeMenuItems?: CodeMenuItemsHook;
30
38
  /** Host chrome rendered INSIDE the editor's scroll container, above the first block
31
39
  * (a title, properties panel, tag row). It scrolls away with the document rather than
32
40
  * pinning, which is what lets the editor keep its own scrollport and windowing. */
33
41
  header?: Snippet;
34
- /** Opt into the pointer affordances: the block drag handle and the table's row and column
35
- * grips (default off, so the surface stays gutter-free). A hover reveals them; touch, which
36
- * has none, shows them outright. Keyboard reorder (Alt+Arrow) and the cell menu are always
37
- * available and need no opt-in. */
42
+ /** The block drag handle (default on; reading mode never shows it). A hover reveals it;
43
+ * touch, which has none, shows it outright. Only the blocks a reader picks up whole carry
44
+ * one — code, tables, equations, diagrams, pictures, list items, dividers, cards — never
45
+ * prose (paragraph, heading, quote, note). `false` removes them, except on a picture,
46
+ * whose grip is the only pointer road to move it. Keyboard reorder (Alt+Arrow) is always
47
+ * available, as is the table's right-click cell menu. */
38
48
  blockDragHandles?: boolean;
39
49
  searchBar?: boolean;
50
+ /** The editor's own formatting popover beside a prose selection (default on; reading mode
51
+ * never shows it). A host with its own bar over `runCommand` passes false. */
52
+ selectionToolbar?: boolean;
40
53
  /** Where the editor's own find/replace bar renders. Default (absent) keeps it pinned inside
41
54
  * the editor root; an element relocates the SAME bar into it, theme scope included, so
42
55
  * host-scroll embeds can put it in a pane's chrome instead of mid-page. Read live, and
@@ -102,14 +115,13 @@ export interface EditorInstance {
102
115
  */
103
116
  insertMarkdown(md: string): boolean;
104
117
  /**
105
- * Run a command by id at the focused surface, no chord in the path, so a consumer's
106
- * `keybindings` rebind cannot rewire a toolbar button. `TOOLBAR_COMMANDS` names the built-in
107
- * ids; a plugin's global name resolves ahead of the focused block, its per-block one stays
108
- * chord-only. Semantics match the chord: one undo entry, same caret — over a cross-block range
109
- * a format toggle marks every block it touches, a table by its cells. False, and nothing mutates,
110
- * on an unknown id, in reading mode, with nothing focused, and on the link editor over a range.
118
+ * Run a command by id at the focused surface, or across a painted range where the id has a
119
+ * cross-block arm (a format toggle marks every block it touches, a table by its cells). False,
120
+ * and nothing mutates, on an unknown id, in reading mode, with nothing focused, and on the link
121
+ * editor over a range. `arg` reaches the arm as a keybinding's argument would (`heading.cycle`
122
+ * takes the level, 0 for plain text); an arm that takes none ignores it.
111
123
  */
112
- runCommand(commandId: string): boolean;
124
+ runCommand(commandId: string, arg?: unknown): boolean;
113
125
  /**
114
126
  * Whether `runCommand(id)` would reach that command's arm right now, asked at the seam that
115
127
  * would run it, so a host can grey a toolbar button out instead of hiding the affordance.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { default as Editor } from './components/Editor.svelte';
2
2
  export type { EditorProps, EditorInstance } from './editor-props';
3
3
  export type { EditorDiagnostics, InteractionTraceEntry } from './editor-props';
4
4
  export type { BlockComponent } from './block-component';
5
- export type { ResolveImageUrl, ResolveLinkUrl, PastedImage, PasteImageHook } from './editor-keys';
5
+ export type { ResolveImageUrl, ResolveLinkUrl, PastedImage, PasteImageHook, CodeRunRequest, RunCodeHook, CodeMenuItem, CodeMenuItemsHook } from './editor-keys';
6
6
  export type { ImageLoadPolicy } from './core/inline-render';
7
7
  export type { PresentationMode } from './presentation-mode';
8
8
  export type { SearchState, SearchOptions } from './search/search-state.svelte';
package/dist/plugin.d.ts CHANGED
@@ -29,9 +29,15 @@ export type { OuterBlockScan } from './schema/block-openers';
29
29
  export { OPENER_PRIORITIES } from './schema/opener-priorities';
30
30
  export { registerBlockCompleter } from './schema/block-completions';
31
31
  export type { BlockCompleter, CompletionResult } from './schema/block-completions';
32
+ export { registerLanguage, listLanguages, getLanguageAliases } from './components/blocks/code/code-languages';
33
+ export type { LanguageGrammar } from './components/blocks/code/code-languages';
34
+ export { tokenizeBody as highlightCode } from './components/blocks/code/code-renderer';
35
+ export type { LanguageFn } from 'highlight.js';
32
36
  export type { CommandId } from './schema/commands';
33
37
  export type { KeyBinding } from './schema/keybindings';
34
38
  export { registerBlockCommand } from './schema/block-commands';
39
+ export { registerBlockContextActions } from './schema/context-actions';
40
+ export type { BlockContextAction, BlockActionContext, BlockContextActionProvider } from './schema/context-actions';
35
41
  export type { BlockCommandContext, BlockCommandHandler } from './schema/block-commands';
36
42
  export type { PluginCommandId, AnyCommandId } from './schema/command-id';
37
43
  export { registerGlobalCommand } from './schema/global-commands';
@@ -97,6 +103,7 @@ export { caretOffsetAtPoint } from './cursor/point-offset';
97
103
  export type { CaretTarget } from './schema/block-kind-descriptor';
98
104
  export { CURSOR_END } from './block-component';
99
105
  export type { CursorEnd } from './block-component';
106
+ export { POINTER_GESTURE_ATTR } from './selection/pointer-gesture';
100
107
  export type { EditorSelection, SelectionPoint } from './selection/primitives';
101
108
  export { SELECTION_END } from './block-component';
102
109
  export type { SelectionEnd } from './block-component';
package/dist/plugin.js CHANGED
@@ -38,9 +38,21 @@ export { OPENER_PRIORITIES } from './schema/opener-priorities';
38
38
  // never type into existence: a completer reads one typed line and answers the lines that
39
39
  // complete it, plus where the caret seats inside the mint.
40
40
  export { registerBlockCompleter } from './schema/block-completions';
41
+ // ── Code-block languages (pre-freeze) ────────────────────────────────────────
42
+ // The registry behind fenced-code highlighting. The editor bootstraps a curated set (every
43
+ // grammar is bundle weight for every consumer), so a host needing more registers them itself,
44
+ // BEFORE mounting an editor: a block on screen re-tokenizes only when its bytes next change.
45
+ // An unregistered language is not an error — the fence still round-trips, just untokenized.
46
+ // `listLanguages` lists each language once; `getLanguageAliases` holds the other spellings.
47
+ export { registerLanguage, listLanguages, getLanguageAliases } from './components/blocks/code/code-languages';
48
+ // The code block's own tokenizer, for a plugin whose source surface wants the same highlighting
49
+ // (block math paints its LaTeX with it). Text-preserving: the fragment's textContent is `body`.
50
+ export { tokenizeBody as highlightCode } from './components/blocks/code/code-renderer';
41
51
  // ── Command mint (pre-freeze) ────────────────────────────────────────────────
42
52
  // A (kind, name) block-command mints a PluginCommandId; AnyCommandId spans built-in and minted.
43
53
  export { registerBlockCommand } from './schema/block-commands';
54
+ // The block context menu: a kind's right-click actions, empty unless something registers them.
55
+ export { registerBlockContextActions } from './schema/context-actions';
44
56
  // A global command is process-wide but runs against the dispatching instance's EditorContext.
45
57
  export { registerGlobalCommand } from './schema/global-commands';
46
58
  // ── Parse / serialize helpers (pre-freeze) ───────────────────────────────────
@@ -153,4 +165,8 @@ export { registerPasteTransform } from './tree-operations/paste/paste-transforms
153
165
  // names one), and the sentinel for "wherever this leaf ends".
154
166
  export { caretOffsetAtPoint } from './cursor/point-offset';
155
167
  export { CURSOR_END } from './block-component';
168
+ // ── Pointer gestures (pre-freeze) ────────────────────────────────────────────
169
+ // Put this attribute on an element whose drags are your widget's own (a pan, a brush) and the
170
+ // editor's pointer arms leave a press inside it alone: no block range, no click ladder.
171
+ export { POINTER_GESTURE_ATTR } from './selection/pointer-gesture';
156
172
  export { SELECTION_END } from './block-component';