@voithos-labs/aragonite 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/README.md +6 -21
  2. package/THIRD-PARTY-NOTICES.md +25 -0
  3. package/dist/a11y-strings.d.ts +18 -0
  4. package/dist/a11y-strings.js +18 -0
  5. package/dist/action-contracts.d.ts +7 -1
  6. package/dist/ambient/ambient-dom.js +5 -1
  7. package/dist/block-component.d.ts +14 -0
  8. package/dist/components/BlockDragHandle.svelte +35 -24
  9. package/dist/components/BlockHost.svelte +16 -9
  10. package/dist/components/Editor.svelte +381 -198
  11. package/dist/components/Editor.svelte.d.ts +1 -1
  12. package/dist/components/SelectionOverlay.svelte +17 -3
  13. package/dist/components/TailInsert.svelte +107 -0
  14. package/dist/components/TailInsert.svelte.d.ts +17 -0
  15. package/dist/components/block-content-selector.d.ts +6 -2
  16. package/dist/components/block-content-selector.js +6 -2
  17. package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
  18. package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
  19. package/dist/components/blocks/code/CodeBlock.svelte +211 -30
  20. package/dist/components/blocks/code/CodeBlockRail.svelte +686 -0
  21. package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
  22. package/dist/components/blocks/code/code-bootstrap.js +4 -0
  23. package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
  24. package/dist/components/blocks/code/code-context-actions.js +24 -0
  25. package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
  26. package/dist/components/blocks/code/code-fence-exit.js +26 -0
  27. package/dist/components/blocks/code/code-languages.d.ts +6 -0
  28. package/dist/components/blocks/code/code-languages.js +11 -0
  29. package/dist/components/blocks/code/code-renderer.js +11 -0
  30. package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
  31. package/dist/components/blocks/editable-leaf.d.ts +37 -6
  32. package/dist/components/blocks/editable-leaf.js +243 -29
  33. package/dist/components/blocks/editable-surface.d.ts +9 -0
  34. package/dist/components/blocks/editable-surface.js +22 -3
  35. package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
  36. package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
  37. package/dist/components/blocks/list/task-checkbox.js +11 -2
  38. package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
  39. package/dist/components/blocks/surface-wiring.svelte.js +8 -1
  40. package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
  41. package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
  42. package/dist/components/blocks/table/TableBlock.svelte +205 -186
  43. package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
  44. package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
  45. package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
  46. package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
  47. package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
  48. package/dist/components/blocks/table/cell-clipboard.js +34 -1
  49. package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
  50. package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
  51. package/dist/components/blocks/table/table-cell-paste.js +2 -1
  52. package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
  53. package/dist/components/blocks/table/table-menu-model.js +36 -17
  54. package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
  55. package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
  56. package/dist/components/blocks/text/delimiter-autopair.js +216 -0
  57. package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
  58. package/dist/components/blocks/text/edge-policy-dispatch.js +56 -2
  59. package/dist/components/blocks/text/live-selection-edit.js +27 -0
  60. package/dist/components/blocks/text/text-keydown.d.ts +7 -1
  61. package/dist/components/blocks/text/text-keydown.js +11 -1
  62. package/dist/components/blocks/text/text-render.d.ts +1 -1
  63. package/dist/components/blocks/text/text-render.js +3 -1
  64. package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
  65. package/dist/components/blocks/text/widget-interaction.js +144 -22
  66. package/dist/components/drag-handle.d.ts +35 -0
  67. package/dist/components/drag-handle.js +126 -0
  68. package/dist/components/editor-root-focus.d.ts +19 -0
  69. package/dist/components/editor-root-focus.js +67 -0
  70. package/dist/components/editor-root-geometry.d.ts +39 -0
  71. package/dist/components/editor-root-geometry.js +91 -0
  72. package/dist/components/editor-root-keydown.d.ts +1 -1
  73. package/dist/components/editor-root-keydown.js +12 -3
  74. package/dist/components/editor-root-listeners.d.ts +10 -6
  75. package/dist/components/editor-root-listeners.js +23 -22
  76. package/dist/components/editor-root-mode-flip.d.ts +36 -0
  77. package/dist/components/editor-root-mode-flip.js +92 -0
  78. package/dist/components/image/ImageOverlayHost.svelte +14 -6
  79. package/dist/components/image/ImageProperties.svelte +512 -70
  80. package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
  81. package/dist/components/image/ImageResizeHandles.svelte +59 -34
  82. package/dist/components/image/image-crop.d.ts +39 -0
  83. package/dist/components/image/image-crop.js +74 -0
  84. package/dist/components/image/image-edit-commit.d.ts +1 -0
  85. package/dist/components/image/image-edit-commit.js +21 -5
  86. package/dist/components/image/image-source-bytes.js +10 -3
  87. package/dist/components/image/image-widget-editing.js +1 -0
  88. package/dist/components/image/widget-dom.js +5 -1
  89. package/dist/components/link-card/link-card-commit.js +1 -1
  90. package/dist/components/lrd-map-gate.js +1 -1
  91. package/dist/components/menu/BlockMenu.svelte +315 -0
  92. package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
  93. package/dist/components/menu/MenuIcon.svelte +153 -0
  94. package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
  95. package/dist/components/menu/clipboard-actions.d.ts +12 -0
  96. package/dist/components/menu/clipboard-actions.js +42 -0
  97. package/dist/components/menu/default-context-actions.d.ts +15 -0
  98. package/dist/components/menu/default-context-actions.js +76 -0
  99. package/dist/components/menu/flyout-placement.d.ts +6 -0
  100. package/dist/components/menu/flyout-placement.js +25 -0
  101. package/dist/core/inline/format-toggle.d.ts +12 -4
  102. package/dist/core/inline/format-toggle.js +94 -40
  103. package/dist/core/inline/image-dimensions.d.ts +3 -0
  104. package/dist/core/inline/image-dimensions.js +46 -10
  105. package/dist/core/inline/inline-widgets.d.ts +11 -0
  106. package/dist/core/inline/scan/brackets.js +1 -0
  107. package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
  108. package/dist/core/inline/scan/plugin-syntax.js +15 -1
  109. package/dist/core/inline-render.d.ts +6 -0
  110. package/dist/core/inline-render.js +32 -0
  111. package/dist/core/nodes.d.ts +14 -0
  112. package/dist/cursor/edge-affinity.js +2 -1
  113. package/dist/cursor/overlay-remeasure.js +8 -0
  114. package/dist/cursor/reveal-source.js +7 -2
  115. package/dist/cursor/widget-offset.d.ts +6 -0
  116. package/dist/cursor/widget-offset.js +61 -4
  117. package/dist/debug/interaction-trace.d.ts +4 -0
  118. package/dist/debug/interaction-trace.js +15 -0
  119. package/dist/decorations/decoration-state.svelte.js +1 -1
  120. package/dist/editor-actions/ancestry-folds.d.ts +2 -2
  121. package/dist/editor-actions/ancestry-folds.js +1 -1
  122. package/dist/editor-actions/block-edit-scope.js +1 -1
  123. package/dist/editor-actions/commit/text-batch.d.ts +3 -2
  124. package/dist/editor-actions/commit/text-batch.js +1 -1
  125. package/dist/editor-actions/commit/undo-controller.js +4 -2
  126. package/dist/editor-actions/container-edit.js +2 -1
  127. package/dist/editor-actions/enter-completion.d.ts +2 -0
  128. package/dist/editor-actions/enter-completion.js +23 -2
  129. package/dist/editor-actions/inline-range-commit.js +1 -1
  130. package/dist/editor-actions/reorder-action.js +19 -10
  131. package/dist/editor-actions/reorder-drag.js +29 -1
  132. package/dist/editor-actions/replacement-focus.d.ts +1 -1
  133. package/dist/editor-actions/replacement-focus.js +1 -1
  134. package/dist/editor-actions/search-replace.js +1 -1
  135. package/dist/editor-actions/table-context.d.ts +4 -1
  136. package/dist/editor-actions/table-context.js +57 -1
  137. package/dist/editor-events.d.ts +3 -0
  138. package/dist/editor-keys.d.ts +33 -0
  139. package/dist/editor-props.d.ts +21 -12
  140. package/dist/index.d.ts +1 -1
  141. package/dist/plugin.d.ts +6 -0
  142. package/dist/plugin.js +11 -0
  143. package/dist/plugins/latex/BlockMath.svelte +264 -29
  144. package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
  145. package/dist/plugins/latex/index.d.ts +2 -1
  146. package/dist/plugins/latex/latex-kind.js +36 -3
  147. package/dist/plugins/latex/math-completion.js +4 -1
  148. package/dist/plugins/latex/math-layout.d.ts +15 -0
  149. package/dist/plugins/latex/math-layout.js +13 -0
  150. package/dist/plugins/latex/math-source.d.ts +19 -0
  151. package/dist/plugins/latex/math-source.js +97 -0
  152. package/dist/plugins/latex/register.d.ts +11 -2
  153. package/dist/plugins/latex/register.js +3 -1
  154. package/dist/plugins/latex/renderer.d.ts +3 -3
  155. package/dist/plugins/latex/renderer.js +13 -6
  156. package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
  157. package/dist/reactivity/list-windowing.svelte.js +49 -24
  158. package/dist/schema/block-completions.d.ts +8 -0
  159. package/dist/schema/block-completions.js +11 -0
  160. package/dist/schema/context-actions.d.ts +31 -0
  161. package/dist/schema/context-actions.js +23 -0
  162. package/dist/schema/fenced-code-raw.js +31 -1
  163. package/dist/schema/operations.d.ts +8 -1
  164. package/dist/schema/reserved-chords.js +25 -4
  165. package/dist/schema/table-cell-raw.d.ts +1 -1
  166. package/dist/schema/table-cell-raw.js +1 -1
  167. package/dist/selection/block-hit-test.js +3 -2
  168. package/dist/selection/char-endpoint-snap.js +1 -1
  169. package/dist/selection/clipboard-text.js +6 -1
  170. package/dist/selection/covered-block.d.ts +10 -0
  171. package/dist/selection/covered-block.js +24 -0
  172. package/dist/selection/cross-block/dispatch.d.ts +3 -0
  173. package/dist/selection/cross-block/dispatch.js +13 -1
  174. package/dist/selection/cross-block/format-range.d.ts +1 -1
  175. package/dist/selection/cross-block/format-range.js +4 -15
  176. package/dist/selection/cross-block/format-toggle.js +1 -1
  177. package/dist/selection/cross-block/keydown.js +1 -1
  178. package/dist/selection/cross-block/ops.js +1 -1
  179. package/dist/selection/cross-block/paste.js +32 -30
  180. package/dist/selection/cross-block/type-replace.d.ts +3 -2
  181. package/dist/selection/cross-block/type-replace.js +62 -13
  182. package/dist/selection/dead-space-caret.d.ts +10 -0
  183. package/dist/selection/dead-space-caret.js +47 -1
  184. package/dist/selection/double-click-trim.d.ts +17 -0
  185. package/dist/selection/double-click-trim.js +57 -0
  186. package/dist/selection/drag-pointer.d.ts +7 -2
  187. package/dist/selection/drag-pointer.js +61 -2
  188. package/dist/selection/gap-caret.js +1 -1
  189. package/dist/selection/keyboard-extend.js +1 -1
  190. package/dist/selection/path-lookup.js +1 -1
  191. package/dist/selection/range-delete-ceremony.js +4 -2
  192. package/dist/selection/range-delete-chrome.js +2 -1
  193. package/dist/selection/range-delete-table-coverage.js +2 -1
  194. package/dist/selection/range-delete-table.js +3 -2
  195. package/dist/selection/range-delete.js +30 -2
  196. package/dist/selection/selection-restore.js +1 -1
  197. package/dist/selection/selection-state.svelte.d.ts +6 -0
  198. package/dist/selection/selection-state.svelte.js +36 -1
  199. package/dist/selection/table-endpoint-snap.js +1 -1
  200. package/dist/selection/table-rect-extend.js +1 -1
  201. package/dist/styles/editor-theme.css +57 -28
  202. package/dist/styles/editor.css +217 -18
  203. package/dist/testing/container-conformance.js +2 -2
  204. package/dist/testing/inline-conformance.js +2 -1
  205. package/dist/tree-operations/blockquote.js +1 -1
  206. package/dist/tree-operations/chain-rebuild.d.ts +63 -0
  207. package/dist/tree-operations/chain-rebuild.js +142 -0
  208. package/dist/tree-operations/children.d.ts +1 -1
  209. package/dist/tree-operations/children.js +1 -1
  210. package/dist/tree-operations/cleanup.js +1 -1
  211. package/dist/tree-operations/content-write.d.ts +50 -0
  212. package/dist/tree-operations/content-write.js +263 -0
  213. package/dist/tree-operations/index.d.ts +8 -3
  214. package/dist/tree-operations/index.js +6 -2
  215. package/dist/tree-operations/list/exit-replacement.js +1 -1
  216. package/dist/tree-operations/list/unwrap-merge.js +3 -3
  217. package/dist/tree-operations/node-ops.d.ts +17 -234
  218. package/dist/tree-operations/node-ops.js +47 -1113
  219. package/dist/tree-operations/node-primitives.d.ts +75 -0
  220. package/dist/tree-operations/node-primitives.js +117 -0
  221. package/dist/tree-operations/paste/apply.js +1 -1
  222. package/dist/tree-operations/paste/body-write.d.ts +1 -1
  223. package/dist/tree-operations/paste/body-write.js +2 -2
  224. package/dist/tree-operations/paste/container-match.js +4 -2
  225. package/dist/tree-operations/paste/dispatch.js +2 -1
  226. package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
  227. package/dist/tree-operations/paste/focus-target.d.ts +1 -1
  228. package/dist/tree-operations/paste/list-absorb.js +1 -1
  229. package/dist/tree-operations/paste/list-break-out.js +1 -1
  230. package/dist/tree-operations/paste/parent-scope.js +1 -1
  231. package/dist/tree-operations/paste/paste-replacement.js +1 -1
  232. package/dist/tree-operations/paste/replace-block-at-parent.js +1 -1
  233. package/dist/tree-operations/path-mutate.d.ts +1 -1
  234. package/dist/tree-operations/path-mutate.js +2 -1
  235. package/dist/tree-operations/reorder-unit.js +1 -1
  236. package/dist/tree-operations/reorder.d.ts +5 -2
  237. package/dist/tree-operations/reorder.js +55 -2
  238. package/dist/tree-operations/settle.d.ts +105 -0
  239. package/dist/tree-operations/settle.js +660 -0
  240. package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
  241. package/dist/tree-operations/table-grid-clipboard.js +90 -0
  242. package/dist/tree-operations/unshare.d.ts +15 -77
  243. package/dist/tree-operations/unshare.js +15 -166
  244. package/docs/guide/consumer-guide.md +126 -96
  245. package/docs/guide/plugin-api.md +31 -3
  246. package/docs/guide/plugin-guide.md +26 -3
  247. package/package.json +4 -2
  248. package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
  249. package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
  250. package/dist/components/blocks/table/TableGrip.svelte +0 -91
  251. package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
  252. package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
  253. package/dist/components/blocks/table/table-drop-target.js +0 -16
  254. package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
  255. package/dist/components/blocks/table/table-reorder-drag.js +0 -97
@@ -0,0 +1,263 @@
1
+ /**
2
+ * The content write: every byte that enters a node crosses `updateNodeContent`, the sole
3
+ * re-parse transfer funnel (editor.md § 6, § 8), and the container twin that re-derives a
4
+ * container's kind from its rebuilt raw.
5
+ */
6
+ import { isBlankParagraph, parse } from '../core/parser';
7
+ import { escalatedFenceLength, matchFenceOpen } from '../core/parsers/fence-syntax';
8
+ import { isBlockOpenerRegistered } from '../schema/block-openers';
9
+ import { trailingLineEnding } from '../core/lines';
10
+ import { assignChildIdsDeep } from '../block-id';
11
+ import { perfEnabled, recordContainerKindReparse } from '../perf/instruments';
12
+ import { getBlockKindDescriptor, tryGetBlockKindDescriptor } from '../schema/block-kind-descriptor';
13
+ import { resyncChildIds } from './children';
14
+ import { spliceMany } from './splice-many';
15
+ import { replacePreservingFirst } from './structural-change';
16
+ import { NEXT_PROSE_LINE, ensureEditableContainers, forBody, writeOwnRaw } from './node-primitives';
17
+ import { absorbWindowSeams, focusTargetInReplacement, releaseWrapPeel, restoreSeparatorAfterBlank, restoreSeparatorOnFill, settleSeparatorOnBlank, widenForTailMint } from './settle';
18
+ /**
19
+ * Update raw and re-parse. A kind change mints the reparsed block into the slot rather than
20
+ * reassigning `kind` in place, and multi-block text mints every parsed block; only a same-kind
21
+ * single-block edit writes in place, so routine typing keeps the node's object identity.
22
+ */
23
+ export function updateNodeContent(parent, blockIndex, text, grammar, sharing) {
24
+ const wasBlank = isBlankParagraph(parent.children[blockIndex]);
25
+ const change = writeParsedContent(parent, blockIndex, text, grammar);
26
+ const lastWritten = lastMintedIndex(change, blockIndex);
27
+ // One blank line served BOTH sides: it separated this block from the one above and stood in
28
+ // as the separator of the block beneath it. Ending it owes each their own.
29
+ if (wasBlank && !isBlankParagraph(parent.children[blockIndex])) {
30
+ restoreSeparatorOnFill(parent, blockIndex, sharing);
31
+ restoreSeparatorAfterBlank(parent, followerIndexAfter(change, blockIndex), sharing);
32
+ releaseWrapPeel(parent, lastWritten);
33
+ return settleWriteSeams(parent, blockIndex, lastWritten, change, sharing);
34
+ }
35
+ // The reverse transition: the block IS the separating line now, so the run it joins gives
36
+ // back the second one. The last block minted is the one that meets the follower.
37
+ if (!wasBlank && isBlankParagraph(parent.children[lastWritten])) {
38
+ const settled = parent.children.length;
39
+ settleSeparatorOnBlank(parent, lastWritten, sharing);
40
+ const widened = widenForTailMint(change, settled, parent.children.length);
41
+ return settleWriteSeams(parent, blockIndex, lastWritten, widened, sharing);
42
+ }
43
+ // Same-kind typing INSIDE content must never pay a neighbour reparse.
44
+ if (change.op === 'noop')
45
+ return { change, textStart: 0 };
46
+ return settleWriteSeams(parent, blockIndex, lastWritten, change, sharing);
47
+ }
48
+ /**
49
+ * Ask every join the write disturbed and report where the written text ended up: an absorb
50
+ * ABOVE leaves the predecessor standing, so its bytes now sit in front of the text.
51
+ */
52
+ function settleWriteSeams(parent, blockIndex, lastWritten, change, sharing) {
53
+ const tracked = { index: blockIndex, offset: 0 };
54
+ const settled = absorbWindowSeams(parent, blockIndex, lastWritten - blockIndex + 1, blockIndex, change, sharing, tracked);
55
+ return {
56
+ change: settled.change,
57
+ textStart: textOffsetInWindow(parent.children, settled.change, tracked)
58
+ };
59
+ }
60
+ /**
61
+ * The tracked position as an offset in the settled window's committed text, the space every
62
+ * caret door measures in, where the head block's own leading trivia is outside the window.
63
+ */
64
+ function textOffsetInWindow(children, change, tracked) {
65
+ const at = change.op === 'noop' ? tracked.index : change.at;
66
+ let pos = 0;
67
+ for (let i = at; i < tracked.index; i++) {
68
+ pos += (i === at ? 0 : children[i].leadingTrivia.length) + children[i].raw.length;
69
+ }
70
+ const ownTrivia = tracked.index > at ? children[tracked.index].leadingTrivia.length : 0;
71
+ return pos + ownTrivia + tracked.offset;
72
+ }
73
+ /** Where the filled block's follower ended up: a multi-block reparse pushes it down. */
74
+ function followerIndexAfter(change, blockIndex) {
75
+ return change.op === 'replace' ? change.at + change.newCount : blockIndex + 1;
76
+ }
77
+ /** The last block the write left in the slot: a multi-block reparse mints past the first. */
78
+ function lastMintedIndex(change, blockIndex) {
79
+ return change.op === 'replace' ? change.at + change.newCount - 1 : blockIndex;
80
+ }
81
+ /**
82
+ * The written bytes parsed, with the construct they leave OPEN closed off first: an unterminated
83
+ * construct reads every block below it as its body at the next parse, and the seam settle would
84
+ * converge the live tree to exactly that reading.
85
+ */
86
+ function closeWrittenConstruct(parent, blockIndex, text, oldKind, grammar) {
87
+ // Fragment scope: this is one block's bytes, whatever its position, so a position-scoped
88
+ // kind must not mint here.
89
+ const parsed = parse(text, { grammar, scope: 'fragment' });
90
+ if (blockIndex + 1 >= parent.children.length)
91
+ return { text, parsed };
92
+ if (parsed.children.length === 1 && parsed.children[0].kind === oldKind)
93
+ return { text, parsed };
94
+ const terminator = openConstructTerminator(text, parsed.children, grammar);
95
+ if (!terminator)
96
+ return { text, parsed };
97
+ const closed = text + terminator;
98
+ return { text: closed, parsed: parse(closed, { grammar, scope: 'fragment' }) };
99
+ }
100
+ /**
101
+ * The terminator written bytes owe when their last construct absorbs to EOF, asked of the grammar
102
+ * rather than a kind list. Null when the bytes terminate themselves, or when no fence opener
103
+ * explains the absorb (the one family whose closer its opener determines).
104
+ */
105
+ function openConstructTerminator(text, blocks, grammar) {
106
+ // The terminator is a line of its own, so bytes whose last line is still open have none to
107
+ // append to (G4.20's unterminated tail slice, which absorbs nothing while it stands alone).
108
+ if (!text.endsWith('\n') || blocks.length === 0)
109
+ return null;
110
+ const ending = trailingLineEnding(text);
111
+ const probe = parse(text + ending + NEXT_PROSE_LINE + ending, { grammar, scope: 'fragment' });
112
+ if (probe.children.length !== blocks.length)
113
+ return null;
114
+ const raw = blocks[blocks.length - 1].raw;
115
+ const nl = raw.indexOf('\n');
116
+ const opener = matchFenceOpen(nl < 0 ? raw : raw.slice(0, nl));
117
+ if (!opener)
118
+ return null;
119
+ const body = nl < 0 ? '' : raw.slice(nl + 1);
120
+ const run = escalatedFenceLength(body, opener.marker, opener.length);
121
+ return opener.indent + opener.marker.repeat(run) + ending;
122
+ }
123
+ function writeParsedContent(parent, blockIndex, text, grammar) {
124
+ const node = parent.children[blockIndex];
125
+ const oldKind = node.kind;
126
+ const oldDescriptor = getBlockKindDescriptor(oldKind);
127
+ // Ahead of every reparse below, so a write lands on the kind its committed bytes
128
+ // describe, not the kind the pre-escape text would parse to (`bodyWrite`).
129
+ const bodyText = forBody(parent, text);
130
+ // A context-dependent kind has no standalone recognizer, so reparsing would downgrade it:
131
+ // keep the kind and write raw through its own legality pass.
132
+ if (oldDescriptor.contextDependentKind) {
133
+ writeOwnRaw(node, bodyText, grammar);
134
+ return { op: 'noop' };
135
+ }
136
+ const { text: newText, parsed: reparsed } = closeWrittenConstruct(parent, blockIndex, bodyText, oldKind, grammar);
137
+ const parsed = reparsed.children;
138
+ const first = parsed[0];
139
+ // A marker-consuming container (a GitHub alert) needs its raw rebuilt from the backfilled
140
+ // body, or G1.1 stale-raw fires.
141
+ const firstBackfilled = !!first && isEmptyEditableContainer(first);
142
+ if (first)
143
+ ensureEditableContainers(first);
144
+ // Text-leading blanks fold into the first block's raw (the single-block shape); the
145
+ // rest keep their own trivia.
146
+ if (parsed.length > 1) {
147
+ const rest = parsed.slice(1);
148
+ for (const sibling of rest)
149
+ ensureEditableContainers(sibling);
150
+ first.raw = first.leadingTrivia + first.raw;
151
+ first.leadingTrivia = node.leadingTrivia;
152
+ if (firstBackfilled)
153
+ reconcileBackfilledRaw(first);
154
+ // The peeled line has no follower inside the splice, so it stays in raw.
155
+ rest[rest.length - 1].raw += reparsed.suffix;
156
+ spliceMany(parent.children, blockIndex, 1, parsed);
157
+ return replacePreservingFirst(blockIndex, 1, parsed.length);
158
+ }
159
+ const newKind = first?.kind ?? 'paragraph';
160
+ // In-place refresh keeps the node's object identity: component, IME state, and inline
161
+ // cache are all keyed on it.
162
+ if (newKind === oldKind) {
163
+ node.raw = newText;
164
+ adoptReparsedFields(node, first);
165
+ if (firstBackfilled)
166
+ reconcileBackfilledRaw(node);
167
+ return { op: 'noop' };
168
+ }
169
+ const replacement = first ?? { kind: 'paragraph', leadingTrivia: '', raw: newText };
170
+ replacement.raw = newText;
171
+ replacement.leadingTrivia = node.leadingTrivia;
172
+ if (firstBackfilled)
173
+ reconcileBackfilledRaw(replacement);
174
+ parent.children.splice(blockIndex, 1, replacement);
175
+ return replacePreservingFirst(blockIndex, 1, 1);
176
+ }
177
+ /**
178
+ * Refresh a node in place from its own reparse, keeping its object identity. The id resync is
179
+ * unconditional: the reparse can change the child count while the caller reports `noop`.
180
+ */
181
+ export function adoptReparsedFields(target, parsed) {
182
+ target.metadata = parsed?.metadata;
183
+ target.children = parsed?.children;
184
+ resyncChildIds(target);
185
+ assignChildIdsDeep(target);
186
+ target.innerPrefix = parsed?.innerPrefix;
187
+ target.innerSuffix = parsed?.innerSuffix;
188
+ }
189
+ /** A container `ensureEditableContainers` will backfill; read BEFORE the backfill runs. */
190
+ function isEmptyEditableContainer(node) {
191
+ const d = getBlockKindDescriptor(node.kind);
192
+ return d.isContainer && d.blockFocus !== 'whole-block' && (node.children?.length ?? 0) === 0;
193
+ }
194
+ /**
195
+ * Sync a just-backfilled container's `raw` to its synthesized body. Needed only for a
196
+ * marker-consuming container whose typed raw lacks a blank body line (`> [!TYPE]`).
197
+ */
198
+ function reconcileBackfilledRaw(node) {
199
+ getBlockKindDescriptor(node.kind).rebuildRaw?.(node);
200
+ }
201
+ // ── Container kind re-derivation ──
202
+ /**
203
+ * What the grammar opens `line` as, read in isolation: asked of the opener registry and never
204
+ * a kind list, so a kind registered later is covered the day it registers.
205
+ */
206
+ export function lineOpensAs(line, grammar) {
207
+ return parse(`${line}\n`, { grammar, scope: 'fragment' }).children[0]?.kind ?? 'paragraph';
208
+ }
209
+ /** Whether the ambient grammar still leaves `NEXT_PROSE_LINE` an ordinary paragraph. */
210
+ export function probeLineOpensAsProse(grammar) {
211
+ return lineOpensAs(NEXT_PROSE_LINE, grammar) === 'paragraph';
212
+ }
213
+ /**
214
+ * Re-derive the container at `index` from its own (already rebuilt) raw, replacing it in the
215
+ * slot when that raw now opens as a different kind (editor.md § 8). Eligibility is the opener
216
+ * registry: registering an opener is exactly the claim that `parse(raw)` reproduces the kind.
217
+ */
218
+ export function reclassifyContainer(parent, index, grammar) {
219
+ const node = parent.children[index];
220
+ if (!node)
221
+ return null;
222
+ const descriptor = tryGetBlockKindDescriptor(node.kind);
223
+ if (!descriptor?.isContainer || !isBlockOpenerRegistered(node.kind))
224
+ return null;
225
+ if (perfEnabled())
226
+ recordContainerKindReparse();
227
+ const parsed = parse(node.raw, { grammar, scope: 'fragment' }).children;
228
+ // A container's raw is one block by construction; a multi-block reparse means bytes
229
+ // this seam has no slot for, left to the gesture that owns the mutation.
230
+ if (parsed.length !== 1 || parsed[0].kind === node.kind)
231
+ return null;
232
+ const replacement = parsed[0];
233
+ const backfilled = isEmptyEditableContainer(replacement);
234
+ ensureEditableContainers(replacement);
235
+ // The slot's trivia is authoritative, so restore the bytes before overwriting it or
236
+ // anything the parse split off the front vanishes with it.
237
+ replacement.raw = node.raw;
238
+ replacement.leadingTrivia = node.leadingTrivia;
239
+ if (backfilled)
240
+ reconcileBackfilledRaw(replacement);
241
+ // A freshly-parsed node carries no childIds and this swap publishes under the slot's
242
+ // reused component instance, so undefined keys would reach the nested keyed `{#each}`.
243
+ assignChildIdsDeep(replacement);
244
+ parent.children[index] = replacement;
245
+ // Write-then-re-read (tree-operations/unshare.ts header).
246
+ return parent.children[index];
247
+ }
248
+ /**
249
+ * Where a caret at `offset` in the written text lands once {@link updateNodeContent}'s folds
250
+ * settled: an absorb ABOVE leaves the predecessor holding the bytes, so the slot the gesture
251
+ * named is gone and the offset carries what that predecessor put in front of it.
252
+ */
253
+ export function settledCaretTarget(settled, at, offset, children) {
254
+ const { change, textStart } = settled;
255
+ if (change.op !== 'replace')
256
+ return { index: at, offset };
257
+ const shifted = offset + textStart;
258
+ if (change.newCount <= 1)
259
+ return { index: change.at, offset: shifted };
260
+ const blocks = children.slice(change.at, change.at + change.newCount);
261
+ const target = focusTargetInReplacement(blocks, shifted);
262
+ return { index: change.at + target.index, offset: target.offset };
263
+ }
@@ -1,5 +1,9 @@
1
- export type { NodeParent, MergeIntoPrevResult, MergeResult, SplitResult } from './node-ops';
2
- export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf, deleteNode, updateNodeContent, reclassifyContainer, focusTargetInReplacement, ensureEditableContainers, normalizeReplacementTrivia, emptyParagraph, paragraphNode, restoreSeparatorOnFill, dropDoubledSeparator, nodeAt } from './node-ops';
1
+ export type { NodeParent } from './node-primitives';
2
+ export { nodeAt, emptyParagraph, paragraphNode, ensureEditableContainers, normalizeReplacementTrivia } from './node-primitives';
3
+ export { deleteNode, focusTargetInReplacement, restoreSeparatorOnFill, dropDoubledSeparator } from './settle';
4
+ export { updateNodeContent, reclassifyContainer } from './content-write';
5
+ export type { MergeIntoPrevResult, MergeResult, SplitResult } from './node-ops';
6
+ export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf } from './node-ops';
3
7
  export { unwrapFirstItemFromList, mergeListItemIntoPrevious } from './list/unwrap-merge';
4
8
  export { renumberOrderedList, normalizeItemMarkerToList } from './list/ordered-markers';
5
9
  export { isItemUserEmpty } from './list/empty-check';
@@ -11,6 +15,7 @@ export { insertEmptyRow, insertEmptyColumn, deleteRow, deleteColumn, cycleAlignm
11
15
  export { copyRectangleAsSubTable } from './sub-table-copy';
12
16
  export type { CellPos } from './sub-table-copy';
13
17
  export { cascadeCleanupEmptyAncestors } from './cleanup';
14
- export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer, rebuildUnsharedChain, rebuildUnsharedAncestry } from './unshare';
18
+ export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer } from './unshare';
19
+ export { rebuildUnsharedChain, rebuildUnsharedAncestry } from './chain-rebuild';
15
20
  export { buildPastedReplacement } from './paste/paste-replacement';
16
21
  export { cloneDocument, cloneNode } from './clone';
@@ -1,4 +1,7 @@
1
- export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf, deleteNode, updateNodeContent, reclassifyContainer, focusTargetInReplacement, ensureEditableContainers, normalizeReplacementTrivia, emptyParagraph, paragraphNode, restoreSeparatorOnFill, dropDoubledSeparator, nodeAt } from './node-ops';
1
+ export { nodeAt, emptyParagraph, paragraphNode, ensureEditableContainers, normalizeReplacementTrivia } from './node-primitives';
2
+ export { deleteNode, focusTargetInReplacement, restoreSeparatorOnFill, dropDoubledSeparator } from './settle';
3
+ export { updateNodeContent, reclassifyContainer } from './content-write';
4
+ export { splitNode, assertSplitLanding, assertSingleNodeSink, mergeWithNext, mergeIntoPrevDeepLeaf } from './node-ops';
2
5
  export { unwrapFirstItemFromList, mergeListItemIntoPrevious } from './list/unwrap-merge';
3
6
  export { renumberOrderedList, normalizeItemMarkerToList } from './list/ordered-markers';
4
7
  export { isItemUserEmpty } from './list/empty-check';
@@ -9,6 +12,7 @@ export { liftFirstChildKeepingContainer } from './container-lift';
9
12
  export { insertEmptyRow, insertEmptyColumn, deleteRow, deleteColumn, cycleAlignment } from './table-mutations';
10
13
  export { copyRectangleAsSubTable } from './sub-table-copy';
11
14
  export { cascadeCleanupEmptyAncestors } from './cleanup';
12
- export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer, rebuildUnsharedChain, rebuildUnsharedAncestry } from './unshare';
15
+ export { ensureUnsharedPath, ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedChildren, ensureUnsharedSubtree, rebuildOwnedContainer } from './unshare';
16
+ export { rebuildUnsharedChain, rebuildUnsharedAncestry } from './chain-rebuild';
13
17
  export { buildPastedReplacement } from './paste/paste-replacement';
14
18
  export { cloneDocument, cloneNode } from './clone';
@@ -1,7 +1,7 @@
1
1
  import { metadataOf } from '../../core/nodes';
2
2
  import { trailingLineEnding } from '../../core/lines';
3
3
  import { cloneNode } from '../clone';
4
- import { emptyParagraph } from '../node-ops';
4
+ import { emptyParagraph } from '../node-primitives';
5
5
  import { assembleListHalf } from './list-builders';
6
6
  import { partitionItemChildren } from './item-partition';
7
7
  import { orderedBaseOf } from './ordered-markers';
@@ -110,20 +110,20 @@ function relocateRemainingChildren(list, targetPath, targetItem, currentItem, li
110
110
  for (let i = 0; i < child.children.length; i++) {
111
111
  const item = sharing ? ensureUnsharedChild(child, i, sharing) : child.children[i];
112
112
  item.leadingTrivia = '';
113
- // discovered-descendant mutation, see node-ops.ts header
113
+ // discovered-descendant mutation, see node-primitives.ts header
114
114
  pushChild(depthOneList, item);
115
115
  }
116
116
  rebuildListRaw(depthOneList);
117
117
  continue;
118
118
  }
119
- // discovered-descendant mutation, see node-ops.ts header
119
+ // discovered-descendant mutation, see node-primitives.ts header
120
120
  pushChild(targetItem, child);
121
121
  }
122
122
  else {
123
123
  // A trailing paragraph keeps its blank-line separator, or the two lazy-continue
124
124
  // into one on reload. Other leaves start fresh and need none.
125
125
  child.leadingTrivia = child.kind === 'paragraph' ? lineEnding : '';
126
- // discovered-descendant mutation, see node-ops.ts header
126
+ // discovered-descendant mutation, see node-primitives.ts header
127
127
  pushChild(targetItem, child);
128
128
  }
129
129
  }
@@ -1,132 +1,40 @@
1
1
  /**
2
- * Kind-agnostic CST node mutations: path resolution, split, merge, delete, update. Children-array
3
- * contract: an op mutating a container's top-level children takes the array as a parameter and
4
- * mutates that, never `node.children` — the caller owns and republishes it, so a direct splice is
5
- * overwritten. A descendant found by walking the live tree is the exception: mutate it in place on
6
- * a caller-unshared spine (`unshare.ts`), a STRUCTURAL one via `commitMultiScope`.
2
+ * The split and merge primitives, the two ops that re-tile a body's blocks around a caret, plus
3
+ * the join cleanup every destructive join crosses (live-mode.md § 4.5). They mutate and report;
4
+ * the settle around them is the ceremony's (`settle.ts`).
7
5
  */
8
- import type { AnyBlockKind, CstNode, Document } from '../core/nodes';
9
- import type { DocumentView, NodeView } from '../core/node-views';
10
- import { type GrammarView } from '../schema/block-openers';
6
+ import { type CstNode } from '../core/nodes';
7
+ import type { NodeView } from '../core/node-views';
8
+ import type { GrammarView } from '../schema/block-openers';
11
9
  import { type CleanedJoin, type InlineResolverRef, type JoinSeam } from '../schema/inline-construct-policy';
12
10
  import type { PresentationMode } from '../presentation-mode';
13
11
  import type { SharingState } from './sharing';
14
12
  import { type StructuralChange } from './structural-change';
15
- /** A children array an op mutates structurally — splice, delete, reorder. */
16
- export type NodeParent = {
17
- children: CstNode[];
18
- };
19
- /**
20
- * A {@link NodeParent} that has answered which container owns it — the byte sinks need that
21
- * answer for the owner's `bodyWrite` grammar and its wrap slots. Both nullable rather than
22
- * optional so every byte-writing site answers: `undefined` is a real answer (the document
23
- * root), skipping the question is a compile error.
24
- */
25
- export type BodyParent = NodeParent & {
26
- ownerKind: AnyBlockKind | undefined;
27
- owner: CstNode | undefined;
28
- /**
29
- * The document root's foldable trailing line, when these children are its.
30
- * Optional, not nullable: only a ceremony-backed site may carry it, because the settle
31
- * that consumes it appends a block — the out-of-ceremony write paths stay slotless so a
32
- * structural materialization is unrepresentable there.
33
- */
34
- suffix?: string;
35
- };
36
- /**
37
- * What the byte sinks accept. A whole `Document` is admitted because it IS the answer
38
- * (the root owns no body grammar); a bare `{ children }` literal still cannot compile.
39
- */
40
- export type BodyParentArg = BodyParent | Document;
41
- /**
42
- * What the separator settles accept: anything that can answer where the body starts, whether
43
- * it holds the owner's kind directly (the container node, the Document) or the sink's answer
44
- * to it ({@link BodyParentArg}, which this absorbs). Wider than the byte sinks: a settle
45
- * writes a line ending, not body text.
46
- */
47
- export type SeparatorParent = {
48
- kind?: string;
49
- ownerKind?: AnyBlockKind;
50
- suffix?: string;
51
- children?: CstNode[];
52
- owner?: CstNode;
53
- };
54
- /**
55
- * Text made legal as a child's raw inside a container of kind `ownerKind`. Exported for
56
- * sinks that build their own bytes rather than handing text to {@link updateNodeContent}.
57
- */
58
- export declare function normalizeBodyWrite(ownerKind: AnyBlockKind | undefined, raw: string): string;
59
- /**
60
- * `raw` made legal as `node`'s OWN bytes — {@link normalizeBodyWrite}'s node-side twin, for the
61
- * kind's own grammar rather than its container's. Sinks that REPLACE the node with a reparse of
62
- * the result call this door: the reparse re-derives metadata from the bytes, so structure the
63
- * rule restores from the old metadata (a fence closer a truncation consumed) must land first.
64
- */
65
- export declare function normalizeOwnRaw(node: NodeView, raw: string): string;
66
- /**
67
- * Write `raw` as `node`'s OWN bytes through its kind's rule — {@link normalizeOwnRaw}'s in-place
68
- * sink. Every sink writing a leaf's bytes without the kind's surface in front of it owes one of
69
- * the two (pinned by `lint/leaf-raw-write-rule`).
70
- */
71
- export declare function writeOwnRaw(node: CstNode, raw: string, grammar: GrammarView | undefined): void;
72
- /**
73
- * The paragraph mint. Every argument is required: a paragraph's raw ENDS in a line ending,
74
- * so a mint site must answer which document it lands in (G4.20) rather than strand a lone
75
- * LF in a CRLF file. Returns a fresh node every call — a shared instance would alias across
76
- * tree positions and corrupt the snapshot/unshare model (G1.9).
77
- */
78
- export declare function paragraphNode(leadingTrivia: string, text: string, lineEnding: string): CstNode;
79
- /** The empty-paragraph placeholder keeping an emptied document or container caret-addressable. */
80
- export declare function emptyParagraph(leadingTrivia: string, lineEnding: string): CstNode;
81
- export declare function nodeAt(doc: Document, path: number[]): CstNode | Document | null;
82
- export declare function nodeAt(doc: DocumentView, path: number[]): NodeView | DocumentView | null;
83
- /** `nodeAt` pre-narrowed through `isBlockNode`: null at the document root or on no match. */
84
- export declare function blockNodeAt(doc: Document, path: number[]): CstNode | null;
85
- export declare function blockNodeAt(doc: DocumentView, path: number[]): NodeView | null;
86
- /**
87
- * Narrow a `nodeAt` result to `CstNode`. Structural, not kind-based: a plugin may mint
88
- * `'document'` as a block kind, so only the absence of `raw` discriminates.
89
- */
90
- export declare function isBlockNode(node: CstNode | Document): node is CstNode;
91
- export declare function isBlockNode(node: NodeView | DocumentView): node is NodeView;
13
+ import { type BodyParentArg, type NodeParent } from './node-primitives';
92
14
  /** What a split leaves the caret: the structural splice, plus where the second half's head
93
- * landed — past `blockIndex + 1` when the first half reparsed to several blocks. */
15
+ * landed, past `blockIndex + 1` when the first half reparsed to several blocks. */
94
16
  export interface SplitResult {
95
17
  change: StructuralChange;
96
18
  secondHalfIndex: number;
97
19
  }
98
20
  /**
99
- * The landing a site is about to consume, held to the primitive's answer (G1.34). The
100
- * top-level path seats the caret at it and the list path splices at it, so both cross this
101
- * on their way to using it; a re-derived `blockIndex + 1` warns instead of shipping.
21
+ * The landing a site is about to consume, held to the primitive's answer (G1.34): a re-derived
22
+ * `blockIndex + 1` warns instead of shipping.
102
23
  */
103
24
  export declare function assertSplitLanding(split: SplitResult, landing: number): void;
104
25
  /**
105
26
  * What a one-slot sink is about to put in its slot, held to one node (G1.35). Asked at the WRITE
106
- * with the nodes being written, so the guard answers for sink N+1 — one that skips the refusal its
107
- * siblings make, or splices a plural replacement into a slot that holds one.
27
+ * with the nodes being written, so the guard answers for sink N+1.
108
28
  */
109
29
  export declare function assertSingleNodeSink(sink: string, installed: readonly CstNode[]): void;
110
30
  /**
111
31
  * Split the node at `blockIndex` at raw `offset` (display-relative). The first half inherits the
112
- * original ID and the whole structural suffix (a setext underline), which a plain cut would strand
113
- * below as junk. The second half opens with a blank separator wherever one does structural work
114
- * ({@link separatorSplitsOffNextLine}) — without it GFM lazy continuation folds the halves back
115
- * into one block on reload.
32
+ * original ID and the whole structural suffix (a setext underline); the second half opens with a
33
+ * blank separator wherever one does structural work ({@link separatorSplitsOffNextLine}).
116
34
  */
117
35
  export declare function splitNode(parent: BodyParentArg, blockIndex: number, offset: number, sharing: SharingState | undefined, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined): SplitResult;
118
36
  /**
119
- * The stand-in for whatever the user types into the second half: the maximally-continuable
120
- * line, so the predicate answers for the worst case rather than one construct. Openers are
121
- * arbitrary code, so no line is unclaimable by construction — {@link probeLineOpensAsProse}
122
- * is the runtime check.
123
- */
124
- export declare const NEXT_PROSE_LINE = "x";
125
- /** Whether the ambient grammar still leaves {@link NEXT_PROSE_LINE} an ordinary paragraph. */
126
- export declare function probeLineOpensAsProse(grammar?: GrammarView): boolean;
127
- /**
128
- * `join.mergedRaw` with the delimiter runs the join orphaned at its seam dropped — live only,
129
- * where those runs are unpainted and a literal concatenation surfaces bytes the reader never saw
37
+ * `join.mergedRaw` with the delimiter runs the join orphaned at its seam dropped, live only
130
38
  * (live-mode.md § 4.5). The one registered cleaner verifies its own bytes and otherwise declines,
131
39
  * leaving the literal join every other mode gets. Every destructive join crosses this door.
132
40
  */
@@ -134,7 +42,7 @@ export declare function cleanJoinedRaw(join: JoinSeam, presentationMode: Present
134
42
  /**
135
43
  * The bytes a single-block edit leaves when it deletes `range` out of `display`. A delete-then-
136
44
  * insert is a join like any other, so it crosses the same cleanup, and the returned offset is
137
- * where the two sides now meet (a cleanup that drops a run on the first side moves it).
45
+ * where the two sides now meet.
138
46
  */
139
47
  export declare function cutRangeFromDisplay(node: NodeView, display: string, range: {
140
48
  start: number;
@@ -144,7 +52,7 @@ export declare function cutRangeFromDisplay(node: NodeView, display: string, ran
144
52
  offset: number;
145
53
  };
146
54
  /** What a join leaves the caret: the structural splice, plus where the two blocks met in the
147
- * survivor's bytes — which a seam cleanup moves when it drops a run on the first block's side. */
55
+ * survivor's bytes, which a seam cleanup moves when it drops a run on the first block's side. */
148
56
  export interface MergeResult {
149
57
  change: StructuralChange;
150
58
  joinOffset: number;
@@ -160,8 +68,7 @@ export interface MergeIntoPrevResult {
160
68
  }
161
69
  /**
162
70
  * Merge `curr` into the deepest prose leaf of `prev`, writing into that leaf rather than
163
- * reparsing concatenated raw — preserves prev's component identity, IME state, and the
164
- * leaves' inline caches. Pass `sharing` to unshare everything the merge writes. Null when
71
+ * reparsing concatenated raw, which preserves prev's component identity and IME state. Null when
165
72
  * no mergeable leaf exists, so the caller can fall back to move-focus.
166
73
  */
167
74
  export declare function mergeIntoPrevDeepLeaf(parent: BodyParentArg, blockIndex: number, sharing: SharingState | undefined, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined, grammar?: GrammarView): MergeIntoPrevResult | null;
@@ -170,127 +77,3 @@ export declare function mergeIntoPrevDeepLeaf(parent: BodyParentArg, blockIndex:
170
77
  * merged block inherits the current block's ID. Noop at the tail.
171
78
  */
172
79
  export declare function mergeWithNext(parent: NodeParent, blockIndex: number, presentationMode: PresentationMode | undefined, linkRef: InlineResolverRef | undefined): MergeResult;
173
- /**
174
- * Settle the separator at `index`: nothing above it needs one at the body head or below a blank
175
- * block, where the parser would read the extra line as one more empty paragraph. Every splice
176
- * that changes what precedes a block settles through this family (syntax-tree.md § Blank lines).
177
- * `sharing` owns the write (G1.9).
178
- */
179
- export declare function clearRedundantSeparator(parent: SeparatorParent, index: number, sharing?: SharingState): void;
180
- /**
181
- * A blank block IS a blank line, so it and its follower share ONE separator: two of them reload
182
- * as a second empty paragraph (G2.13). The follower's is the one that stands, so a later fill of
183
- * this slot (a paste over a cut range) still finds the follower separated. The run-level twin
184
- * {@link settleSeparatorOnBlank} keeps the first already-standing line instead, the same bytes
185
- * the other way round.
186
- */
187
- export declare function dropDoubledSeparator(parent: SeparatorParent, index: number, sharing?: SharingState): void;
188
- /**
189
- * The separator a block takes back when it stops being blank: its own blank line was what stood
190
- * between it and a non-blank predecessor. Call at the fill — {@link clearRedundantSeparator}
191
- * frees a separator in exactly the cases this declines, and a block that was never blank keeps
192
- * whatever its bytes earned (a paragraph under a heading needs none).
193
- */
194
- export declare function restoreSeparatorOnFill(parent: SeparatorParent, index: number, sharing?: SharingState): void;
195
- /**
196
- * The settle a block turning INTO a blank line owes: it joins the blank run around it, and a run
197
- * carries exactly ONE separating line — across every block in it AND its follower, since a blank
198
- * block is the follower's line too. The line already standing is the one kept, wherever in the
199
- * run it sits; a mint lands at the run's head, the only slot one may take.
200
- */
201
- export declare function settleSeparatorOnBlank(parent: SeparatorParent, index: number, sharing?: SharingState): void;
202
- /**
203
- * The commit ceremony's settle door: derive the spliced window from the change and settle it
204
- * against `before`, the pre-mutate children the ceremony still holds. Nodes surviving inside the
205
- * window are not removals, so a coarse descriptor over an in-place write settles as one.
206
- * `tracked` is the committing door's caret, carried through the folds. Returns the change widened
207
- * by a tail line the settle materialized.
208
- */
209
- export declare function settleSeparator(parent: SeparatorParent, before: readonly CstNode[], change: StructuralChange, sharing?: SharingState, tracked?: TrackedPosition): StructuralChange;
210
- /**
211
- * The out-of-commit-scope twin of {@link settleSeparator}, for a container discovered by walking
212
- * the live tree: it splices through the `childIds` door and reads the pre-splice span itself.
213
- */
214
- export declare function spliceChildrenSettled(parent: CstNode | Document, at: number, removeCount: number, replacement: CstNode[], sharing?: SharingState): void;
215
- /**
216
- * A byte position the folds carry with them: the slot it lives in, and its offset inside that
217
- * slot's raw. Written in place, since each fold re-tiles the bytes under it.
218
- */
219
- export interface TrackedPosition {
220
- index: number;
221
- offset: number;
222
- }
223
- /** What a splice settled: its change widened by every fold, and where a tracked index landed. */
224
- export interface SettledSplice {
225
- change: StructuralChange;
226
- landing: number;
227
- }
228
- /**
229
- * The seam question at every join the splice at `at` disturbed — its window's two edges and the
230
- * joins inside it — since a move can invalidate a join that was already correct. Each
231
- * fold cascades downward, so the next seam is asked past what that one ate. `tracked` rides the
232
- * folds for a caller placing a caret in bytes an absorb can move. `headProbe` names the one block
233
- * whose bytes changed, letting each ask decline on its first line alone; dropped once anything
234
- * folds, since the index it names has moved by then.
235
- */
236
- export declare function absorbWindowSeams(parent: NodeParent, at: number, added: number, landing: number, change: StructuralChange, sharing?: SharingState, tracked?: TrackedPosition, headProbe?: number, onBeforeSplice?: () => void): SettledSplice;
237
- /** The materialized tail reported inside the sink's one contiguous window. */
238
- export declare function widenForTailMint(change: StructuralChange, before: number, after: number): StructuralChange;
239
- /**
240
- * Remove the node at `blockIndex`, leaving the next sibling separated from its new predecessor
241
- * and no more. Takes {@link BodyParentArg} because the settle can hand a freed line to the
242
- * owner's wrap slots. Pass `sharing` to unshare the nodes written — the successor's trivia is
243
- * the op's only in-place write.
244
- */
245
- export declare function deleteNode(parent: BodyParentArg, blockIndex: number, sharing?: SharingState): StructuralChange;
246
- /** What a content write settled: its change widened by every fold, and the offset the written
247
- * text starts at inside that change's window — nonzero once a fold above absorbed it. */
248
- export interface SettledContent {
249
- change: StructuralChange;
250
- textStart: number;
251
- }
252
- /**
253
- * Update raw and re-parse. The sole re-parse transfer funnel: a kind change mints the reparsed
254
- * block into the slot rather than reassigning `kind` in place, and multi-block text mints every
255
- * parsed block. Only a same-kind single-block edit writes fields in place, so routine typing keeps
256
- * the node's object identity; `replacePreservingFirst` carries the id/ref across a mint. `sharing`
257
- * owns the separator settle's writes, which land on the run's OTHER blocks.
258
- */
259
- export declare function updateNodeContent(parent: BodyParentArg, blockIndex: number, text: string, grammar?: GrammarView, sharing?: SharingState): SettledContent;
260
- /**
261
- * What the grammar opens `line` as, read in isolation — the opener-significance test, asked of
262
- * the opener registry and never a kind list, so a kind registered later is covered the day it
263
- * registers.
264
- */
265
- export declare function lineOpensAs(line: string, grammar?: GrammarView): AnyBlockKind;
266
- /**
267
- * Re-derive the container at `index` from its own (already rebuilt) raw, replacing it in
268
- * the slot when that raw now opens as a different kind. The container twin of
269
- * `updateNodeContent`'s kind-change arm (editor.md § 8), which no leaf reparse can see.
270
- * Eligibility is the opener registry, not a kind list — registering an opener is exactly
271
- * the claim that `parse(raw)` reproduces the kind, so re-deriving one without would destroy it.
272
- */
273
- export declare function reclassifyContainer(parent: NodeParent, index: number, grammar?: GrammarView): CstNode | null;
274
- /**
275
- * Map a post-edit caret offset (in the committed text) to the parsed block it falls in,
276
- * as a local display offset. The first block's body starts at 0; an offset inside
277
- * inter-block trivia lands at the next block's start; past-the-end clamps to the last.
278
- */
279
- export declare function focusTargetInReplacement(nodes: readonly NodeView[], offset: number): {
280
- index: number;
281
- offset: number;
282
- };
283
- /**
284
- * Where a caret at `offset` in the written text lands once {@link updateNodeContent}'s folds
285
- * settled: an absorb ABOVE leaves the predecessor holding the bytes, so the slot the gesture
286
- * named is gone and the offset carries what that predecessor put in front of it. Every caret
287
- * door onto the content funnel asks this, so the settle is answered in one place.
288
- */
289
- export declare function settledCaretTarget(settled: SettledContent, at: number, offset: number, children: readonly NodeView[]): {
290
- index: number;
291
- offset: number;
292
- };
293
- /** First node inherits the original block's leadingTrivia; subsequent nodes keep theirs. */
294
- export declare function normalizeReplacementTrivia(original: CstNode, replacement: CstNode[]): CstNode[];
295
- /** Ensure every container has at least one child block, so the cursor always has a target. */
296
- export declare function ensureEditableContainers(node: CstNode): void;