@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
@@ -5,7 +5,7 @@
5
5
  * horizontal extension clamps at the column edges, since a rectangle has no sideways exit.
6
6
  */
7
7
  import { metadataOf } from '../core/nodes';
8
- import { isBlockNode, nodeAt } from '../tree-operations/node-ops';
8
+ import { isBlockNode, nodeAt } from '../tree-operations/node-primitives';
9
9
  import { cellRowCol } from '../cursor/coordinate-spaces';
10
10
  import { pathsEqual } from './path-math';
11
11
  /**
@@ -9,40 +9,51 @@
9
9
  /* ── Host-chrome tier: the consumer's vocabulary, defaults behind the class ── */
10
10
 
11
11
  :where(.aragonite-editor-theme) {
12
+ /* Two faces, because they answer different questions. `--font-editor` is the SURFACE
13
+ face — prose, headings, markers — and a host may legitimately set it proportional.
14
+ `--font-code` is what must stay monospace whatever that choice is: code blocks, code
15
+ spans, raw HTML, revealed source. Defaulted to the same stack, so a consumer that
16
+ never names the second one sees exactly what it saw before. */
12
17
  --font-editor: 'Cascadia Code', 'Cascadia Mono', 'Consolas', ui-monospace, monospace;
18
+ --font-code: 'Cascadia Code', 'Cascadia Mono', 'Consolas', ui-monospace, monospace;
19
+ /* The menus' and popovers' face: the host's own UI font where it names one. */
20
+ --font-ui: 'Inter', system-ui, sans-serif;
13
21
  /* Type-scale root: headings, code and markers are all `em`-relative, so one override
14
22
  scales the surface. A themed host sets it on any ancestor instead. */
15
23
  --editor-font-size: 1rem;
16
24
 
17
25
  /* Corner radii by role: small controls (buttons, inputs, chips) and elevated surfaces
18
26
  (overlays, popovers). Chrome whose corner is neither keeps its own literal. */
19
- --radius-ui: 3px;
27
+ --radius-ui: 4px;
20
28
  --radius-surface: 8px;
21
29
 
22
- --color-surface: #1b1c21;
23
- --color-text-secondary: #d6d9e0;
24
- --color-text-primary: #ffffff;
25
- --color-border: #3d4047;
26
- --color-ui-muted: #a4a4a4;
27
- --color-ui-dulled: #afb1b3;
28
- /* Split per mode: no single value clears AA on both a near-black and a white surface. */
29
- --color-accent: #6c9d83;
30
+ /* The page ground, which the menus and the image toolbar paint on. */
31
+ --color-bg: #2c2c2a;
32
+ --color-surface: #1a1a19;
33
+ --color-text-secondary: #cfcfca;
34
+ --color-text-primary: #e8e8e5;
35
+ --color-border: #3e3e3b;
36
+ --color-ui-muted: #8f8f89;
37
+ --color-ui-dulled: #a3a39d;
38
+ --color-accent: #567b67;
30
39
  /* Base the selection, search-match and reorder washes derive from, so one declaration
31
- moves all of them; the washes themselves stay editor-owned. */
32
- --color-selection: #6496ff;
33
- --color-error: #e06c75;
40
+ moves all of them; the washes themselves stay editor-owned. Tracking the accent means
41
+ a host that recolors one recolors all four. */
42
+ --color-selection: var(--color-accent, #567b67);
43
+ --color-error: #ff5f57;
34
44
  }
35
45
 
36
46
  :where(.aragonite-editor-theme)[data-editor-theme='light'] {
37
- --color-surface: #ffffff;
38
- --color-text-secondary: #1f2328;
39
- --color-text-primary: #000000;
40
- --color-border: #d0d7de;
41
- --color-ui-muted: #6e7781;
42
- --color-ui-dulled: #8c959f;
43
- --color-accent: #496e5b;
44
- --color-selection: #6496ff;
45
- --color-error: #cf222e;
47
+ --color-bg: #dfddd7;
48
+ --color-surface: #efeee9;
49
+ --color-text-secondary: #4a4a45;
50
+ --color-text-primary: #2a2a27;
51
+ --color-border: #c9c7c0;
52
+ --color-ui-muted: #83837b;
53
+ --color-ui-dulled: #71716a;
54
+ --color-accent: #567b67;
55
+ --color-selection: var(--color-accent, #567b67);
56
+ --color-error: #d03025;
46
57
  }
47
58
 
48
59
  /* ── Editor-owned tier: defaults on `.editor`, so they hold with or without the class ── */
@@ -58,6 +69,10 @@
58
69
  --color-bg-secondary, which is measured against the base bg. */
59
70
  --color-ui-faint: rgba(255, 255, 255, 0.07);
60
71
 
72
+ /* One radius for a picture, the crop frame that clips it and the selection ring on the
73
+ overlay above it: half the surface radius, enough to soften a photo without a card look. */
74
+ --md-image-radius: 4px;
75
+
61
76
  /* Markdown syntax override points, deliberately neutral so the default rendering carries no
62
77
  extra palette. Mode-independent: the chrome tokens they chain to carry the light/dark
63
78
  split. */
@@ -69,7 +84,10 @@
69
84
  --syntax-code: currentColor;
70
85
  --syntax-comment: currentColor;
71
86
  --syntax-list: currentColor;
72
- --syntax-separator: var(--color-ui-muted, #a4a4a4);
87
+ /* The rendered rule's colour, and its ONLY consumer. A divider separates rather than
88
+ announces, so it defaults to the border token; mid-grey at this weight competed with
89
+ the text either side. A host that wants a louder rule still names this token. */
90
+ --syntax-separator: var(--color-border, #3e3e3b);
73
91
  --syntax-task: currentColor;
74
92
  --syntax-task-done: rgba(128, 128, 128, 0.7);
75
93
 
@@ -98,8 +116,8 @@
98
116
  /* Presentational tokens (mode-independent). */
99
117
  /* Washes over the host-declarable selection base, alpha apart, so a host that names one
100
118
  base moves the selection, the search matches and the reorder chrome together. */
101
- --selection-overlay-bg: color-mix(in srgb, var(--color-selection, #6496ff) 30%, transparent);
102
- --search-match-bg: color-mix(in srgb, var(--color-selection, #6496ff) 22%, transparent);
119
+ --selection-overlay-bg: rgba(100, 150, 255, 0.3);
120
+ --search-match-bg: rgba(100, 150, 255, 0.22);
103
121
  /* Painted on top of the match text, so it must keep an alpha channel or it hides it. */
104
122
  --search-match-active-bg: rgba(216, 166, 87, 0.55);
105
123
  --md-ref-label-color: rgba(128, 128, 128, 0.45);
@@ -111,38 +129,64 @@
111
129
  --md-reorder-indicator: var(--color-selection, #6496ff);
112
130
  /* Wash on the container being reordered within during a nested drag. Tinted with the reorder
113
131
  accent, not neutral grey, so the shift stays perceptible for low-contrast vision. */
114
- --reorder-scope-bg: color-mix(in srgb, var(--color-selection, #6496ff) 14%, transparent);
132
+ --reorder-scope-bg: rgba(100, 150, 255, 0.14);
115
133
  --syntax-marker-dim: 0.65;
116
134
 
135
+ /* Menu/popout elevation, per mode: a shadow tuned for a near-black surface is invisible
136
+ over a white one. Editor-owned rather than host-family, since a host palette names
137
+ colors, not elevation. */
138
+ --menu-shadow: 0 12px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35);
139
+ /* The rest of the host app's menu family, so a menu here is the host's menu to the pixel. */
140
+ --menu-item-hover: rgba(255, 255, 255, 0.07);
141
+ --menu-search-divider: rgba(255, 255, 255, 0.08);
142
+ --menu-scrollbar-thumb: rgba(255, 255, 255, 0.18);
143
+ --menu-scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
144
+
117
145
  /* Placeholder tint behind off-window content (VR-8), so a spacer that outruns the
118
146
  window recompute reads as "loading" rather than a white tear. */
119
147
  --vr-spacer-bg: rgba(255, 255, 255, 0.025);
120
148
  }
121
149
 
150
+ /* The washes follow a host's `--color-selection` where the engine can mix; the static values
151
+ above are the same tints of the default base, for engines without `color-mix` (WebKit before
152
+ Safari 16.2). */
153
+ @supports (color: color-mix(in srgb, red, blue)) {
154
+ :where(.editor, .aragonite-editor-theme) {
155
+ --selection-overlay-bg: color-mix(in srgb, var(--color-selection, #6496ff) 30%, transparent);
156
+ --search-match-bg: color-mix(in srgb, var(--color-selection, #6496ff) 22%, transparent);
157
+ --reorder-scope-bg: color-mix(in srgb, var(--color-selection, #6496ff) 14%, transparent);
158
+ }
159
+ }
160
+
122
161
  :where(.editor, .aragonite-editor-theme)[data-editor-theme='light'] {
123
- --color-bg-secondary: rgba(0, 0, 0, 0.05);
162
+ --color-bg-secondary: rgba(0, 0, 0, 0.035);
124
163
  --color-bg-elevated: #f5f6f8;
125
164
  --color-bg-muted: #eaeaea;
126
165
  --color-text-muted: #57606a;
127
166
  --color-ui-faint: rgba(0, 0, 0, 0.06);
167
+ --menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.06);
168
+ --menu-item-hover: rgba(0, 0, 0, 0.05);
169
+ --menu-search-divider: rgba(0, 0, 0, 0.07);
170
+ --menu-scrollbar-thumb: rgba(0, 0, 0, 0.2);
171
+ --menu-scrollbar-thumb-hover: rgba(0, 0, 0, 0.32);
128
172
 
129
173
  /* --syntax-* tokens are mode-independent — no light overrides. */
130
174
 
131
175
  /* Code block syntax highlighting — GitHub Light-inspired palette */
132
- --code-tok-keyword: #cf222e;
176
+ --code-tok-keyword: #c1202b;
133
177
  --code-tok-string: #0a3069;
134
178
  --code-tok-number: #0550ae;
135
179
  --code-tok-literal: #0550ae;
136
- --code-tok-comment: #666f79;
180
+ --code-tok-comment: #5c646d;
137
181
  --code-tok-type: #953800;
138
- --code-tok-function: #8250df;
139
- --code-tok-operator: #cf222e;
182
+ --code-tok-function: #7447c6;
183
+ --code-tok-operator: #c1202b;
140
184
  --code-tok-meta: #953800;
141
185
  --code-tok-attr: #116329;
142
186
  --code-tok-regexp: #116329;
143
187
  --code-tok-symbol: #0550ae;
144
188
  --code-tok-added: #116329;
145
- --code-tok-removed: #cf222e;
189
+ --code-tok-removed: #c1202b;
146
190
  --code-tok-heading: #0550ae;
147
191
  --code-tok-link: #0550ae;
148
192
 
@@ -168,6 +168,28 @@
168
168
  max-width: 100%;
169
169
  height: auto;
170
170
  display: block;
171
+ border-radius: var(--md-image-radius);
172
+ }
173
+
174
+ /* A cropped image: the widget is the frame (width and aspect set inline from the `|WxH` hint,
175
+ capped to the column) and the image pans inside it, sized and offset by `image-crop.ts`. */
176
+ :where(.editor) .md-image-widget.md-image-cropped {
177
+ overflow: hidden;
178
+ max-width: 100%;
179
+ /* The frame clips the picture, so the rounding belongs to it. `clip-path` alongside
180
+ `overflow`, because a rounded overflow clip is the one Chromium drops when the picture
181
+ inside gets its own compositing layer, and a crop is exactly when that happens. */
182
+ border-radius: var(--md-image-radius);
183
+ clip-path: inset(0 round var(--md-image-radius));
184
+ }
185
+
186
+ /* The picture keeps its own radius rather than going square inside the frame: at zoom 1 it sits
187
+ exactly on the frame, so the two corners coincide, and above 1 its corners are outside the
188
+ frame entirely. Either way the crop cannot come out with harder corners than the plain image. */
189
+ :where(.editor) .md-image-widget.md-image-cropped img {
190
+ position: absolute;
191
+ max-width: none;
192
+ object-fit: cover;
171
193
  }
172
194
 
173
195
  /* Image-bearing list-item / blockquote paragraph: the ambient marker sits at the image's
@@ -249,6 +271,12 @@
249
271
  pointer-events: none;
250
272
  }
251
273
 
274
+ /* The tint IS the widget's selection paint: native `::selection` on the text inside it (a KaTeX
275
+ box is text) would stack a second wash on the same pixels. */
276
+ :where(.editor) [data-inline-widget].md-widget-selected ::selection {
277
+ background: transparent;
278
+ }
279
+
252
280
  /* Synthetic caret on the snap-target edge — Chromium doesn't reliably render
253
281
  the live caret adjacent to a contenteditable=false neighbor. */
254
282
  :where(.editor) [data-inline-widget].md-snap-after::before,
@@ -263,12 +291,14 @@
263
291
  pointer-events: none;
264
292
  }
265
293
 
294
+ /* Clear of the widget's own edge: against a picture, a caret drawn tight to the frame reads as
295
+ part of the border rather than as a caret waiting for the next character. */
266
296
  :where(.editor) [data-inline-widget].md-snap-after::before {
267
- right: -2px;
297
+ right: -4px;
268
298
  }
269
299
 
270
300
  :where(.editor) [data-inline-widget].md-snap-before::before {
271
- left: -2px;
301
+ left: -4px;
272
302
  }
273
303
 
274
304
  @keyframes md-snap-caret-blink {
@@ -290,6 +320,20 @@
290
320
  z-index: 10;
291
321
  }
292
322
 
323
+ /* A selected image says so with a hairline ring. It rides the overlay, not the widget: a cropped
324
+ widget clips its own pseudo-elements, and a border on the picture would move the layout. Held
325
+ just off the edge so it reads as a ring around the picture rather than part of it, over a
326
+ translucent dark line that keeps it legible against a pale photo. */
327
+ :where(.editor) .md-image-overlay::before {
328
+ content: '';
329
+ position: absolute;
330
+ inset: -2px;
331
+ border: 1px solid var(--color-accent, #567b67);
332
+ border-radius: calc(var(--md-image-radius, 4px) + 2px);
333
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.18);
334
+ pointer-events: none;
335
+ }
336
+
293
337
  :where(.editor) .md-image-overlay > * {
294
338
  pointer-events: auto;
295
339
  }
@@ -350,7 +394,7 @@
350
394
  /* Dimmed monospace gives inline raw HTML a code-ish feel without becoming a code span. */
351
395
  :where(.editor) .md-raw-html {
352
396
  color: var(--md-raw-html-color, rgba(120, 120, 120, 0.85));
353
- font-family: var(--font-editor, ui-monospace, monospace);
397
+ font-family: var(--font-code, ui-monospace, monospace);
354
398
  font-size: 0.92em;
355
399
  }
356
400
 
@@ -367,7 +411,7 @@
367
411
  width: 100%;
368
412
  box-sizing: border-box;
369
413
  min-height: 1.4em;
370
- font-family: var(--font-editor, ui-monospace, monospace);
414
+ font-family: var(--font-code, ui-monospace, monospace);
371
415
  font-size: 0.9em;
372
416
  line-height: 1.5;
373
417
  color: inherit;
@@ -394,6 +438,13 @@
394
438
  color: var(--syntax-code, currentColor);
395
439
  }
396
440
 
441
+ /* An inline widget's source while it is revealed for editing (`$x$`, a footnote ref): the
442
+ inline-code wash, so the span being edited reads as a span, in the prose face so the line
443
+ does not reflow around it. */
444
+ ::highlight(md-inline-reveal) {
445
+ background-color: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
446
+ }
447
+
397
448
  /* Raw-HTML block source text; inline raw HTML keeps --md-raw-html-color above. */
398
449
  :where(.editor) .text-editable-block.raw-block {
399
450
  color: var(--syntax-comment, currentColor);
@@ -404,7 +455,7 @@
404
455
  }
405
456
 
406
457
  :where(.editor) .thematic-break-rule[role='separator'] > hr {
407
- border-top-color: var(--syntax-separator, var(--color-ui-muted, #a4a4a4));
458
+ border-top-color: var(--syntax-separator, var(--color-border, #3e3e3b));
408
459
  }
409
460
 
410
461
  /* The [contenteditable='false'] qualifier scopes to the structurally-injected ambient
@@ -417,12 +468,69 @@
417
468
  color: var(--syntax-task, currentColor);
418
469
  }
419
470
 
420
- /* Table grips reveal on table hover, in pure CSS with no per-grip reactive state. The grip's
421
- own <style> holds the at-rest state, so it never intercepts a caret click until then. */
422
- :where(.editor) [role='table']:hover [data-table-col-grip],
423
- :where(.editor) [role='table']:hover [data-table-row-grip] {
424
- opacity: 1;
425
- pointer-events: auto;
471
+ /* ── Menus ──────────────────────────────────────────────────────────────────
472
+ One surface for every menu the editor opens (the table's context menu, the code rail's
473
+ picker and overflow), copied from the host app's context menu value for value and reading
474
+ the same tokens, so a menu here is indistinguishable from one of the host's. */
475
+ :where(.editor) .md-menu {
476
+ position: fixed;
477
+ z-index: 2000;
478
+ min-width: 168px;
479
+ padding: 4px;
480
+ background: var(--color-bg, #2c2c2a);
481
+ border: 1px solid var(--color-border, #3e3e3b);
482
+ border-radius: 8px;
483
+ box-shadow: var(--menu-shadow, 0 12px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35));
484
+ font-family: var(--font-ui, system-ui, sans-serif);
485
+ font-size: 13px;
486
+ line-height: 1.4;
487
+ color: var(--color-text-primary, #e8e8e5);
488
+ user-select: none;
489
+ }
490
+
491
+ :where(.editor) .md-menu-item {
492
+ display: flex;
493
+ align-items: center;
494
+ gap: 9px;
495
+ width: 100%;
496
+ padding: 7px 10px;
497
+ border: none;
498
+ border-radius: 5px;
499
+ background: transparent;
500
+ color: inherit;
501
+ font: inherit;
502
+ text-align: left;
503
+ cursor: pointer;
504
+ }
505
+
506
+ :where(.editor) .md-menu-item:hover,
507
+ :where(.editor) .md-menu-item:focus-visible,
508
+ :where(.editor) .md-menu-item[aria-pressed='true'],
509
+ :where(.editor) .md-menu-item[data-active='true'] {
510
+ background: var(--menu-item-hover, rgba(255, 255, 255, 0.07));
511
+ outline: none;
512
+ }
513
+
514
+ :where(.editor) .md-menu-item:disabled {
515
+ opacity: 0.4;
516
+ cursor: default;
517
+ }
518
+
519
+ :where(.editor) .md-menu-item:disabled:hover {
520
+ background: transparent;
521
+ }
522
+
523
+ :where(.editor) .md-menu-icon {
524
+ display: inline-flex;
525
+ align-items: center;
526
+ flex-shrink: 0;
527
+ color: var(--color-ui-muted, #8f8f89);
528
+ }
529
+
530
+ :where(.editor) .md-menu-divider {
531
+ height: 1px;
532
+ margin: 4px 6px;
533
+ background: var(--color-border, #3e3e3b);
426
534
  }
427
535
 
428
536
  /* ── Rendered chrome, shared by every marker-hiding mode ──────────────────────
@@ -450,17 +558,33 @@
450
558
  }
451
559
 
452
560
  /* Chrome with nothing behind it has nowhere to hide: a construct whose only bytes are its own
453
- markers would paint as an empty line with no caret position, so an editable rung shows them
454
- dimmed exactly as source does. Mirrored in `cursor/widget-offset.ts` (same two modes, same
455
- two families); reading takes no keystrokes and keeps its silence. The `[contenteditable]`
456
- qualifier scopes the stamp to the walk containers its JS twin reads, which is what a block
457
- decoration spelling `data-content-empty` onto the block host cannot reach. */
561
+ markers has no caret position, so an editable rung shows them dimmed as source does — but
562
+ only while the surface holds focus, since the seat is for a caret and a block nobody is in
563
+ reads as the empty construct it is. Mirrored in `cursor/widget-offset.ts` (same modes, same
564
+ families); reading keeps its silence. `[contenteditable]` scopes the stamp to the walk
565
+ containers its JS twin reads, which a decoration on the block host cannot reach. */
458
566
  :where(.editor):is([data-presentation^='preview-'], [data-presentation='live'])
459
- [contenteditable][data-content-empty]
567
+ [contenteditable][data-content-empty]:focus-within
460
568
  :is(.md-marker:not([contenteditable='false']), .md-fence-line) {
461
569
  display: inline;
462
570
  }
463
571
 
572
+ /* A code block or table is a box, and prose gives it nothing to stand off from: blocks stack flush,
573
+ with only a text line's own leading between them. The box brings its own breathing room —
574
+ about a text line's leading each side, so code-to-code and code-to-prose match. Padding,
575
+ not margin: the height model measures the host's border box. */
576
+ :where(.editor) .block-host:is([data-block-kind='fencedCode'], [data-block-kind='table']) {
577
+ padding-block: 6px;
578
+ }
579
+
580
+ /* Room for the add strips (`TableBlock.svelte`) INSIDE the host's box: on the right so a
581
+ full-width table still leaves the column strip inside the editor's scroll box, and below so
582
+ the row strip never lies over whatever follows the table (the trailing insert row, a block). */
583
+ :where(.editor) .block-host[data-block-kind='table'] {
584
+ padding-right: 30px;
585
+ padding-bottom: 24px;
586
+ }
587
+
464
588
  /* Ambient list markers own the hanging indent (padding/text-indent are keyed to the span's
465
589
  ch width), so they keep their box: visibility, not display. */
466
590
  :where(.editor):is(
@@ -498,7 +622,21 @@
498
622
  visibility: visible;
499
623
  }
500
624
 
501
- /* GitHub task lists carry no bullet, so the `- ` prefix hides and the checkbox stays. */
625
+ /* GitHub task lists: no bullet, the box at the margin, the text one gap after it. The `- `
626
+ marker collapses to nothing (its bytes stay for the walk), the `[ ]` span becomes a fixed
627
+ slot so checked and unchecked are the same width, and the box paints in that slot. */
628
+ :where(.editor):is(
629
+ [data-presentation='reading'],
630
+ [data-presentation^='preview-'],
631
+ [data-presentation='live']
632
+ )
633
+ .list-item-block[data-list-marker='task']
634
+ .task-list-marker {
635
+ display: inline-block;
636
+ width: 0;
637
+ overflow: hidden;
638
+ }
639
+
502
640
  :where(.editor):is(
503
641
  [data-presentation='reading'],
504
642
  [data-presentation^='preview-'],
@@ -506,7 +644,110 @@
506
644
  )
507
645
  .list-item-block[data-list-marker='task']
508
646
  .task-checkbox {
647
+ /* Visible inside the hidden ambient marker, its `[ ]` bytes gone by colour: a hidden span
648
+ leaves the accessibility tree and the hit-test, and the pseudo-elements draw the box. */
509
649
  visibility: visible;
650
+ color: transparent;
651
+ position: relative;
652
+ display: inline-block;
653
+ /* Slot + gap; `TASK_HANGING_INDENT` in task-checkbox.ts is these plus the trailing space. */
654
+ width: 1.2em;
655
+ margin-right: 0.65em;
656
+ vertical-align: baseline;
657
+ }
658
+
659
+ :where(.editor):is(
660
+ [data-presentation='reading'],
661
+ [data-presentation^='preview-'],
662
+ [data-presentation='live']
663
+ )
664
+ .list-item-block[data-list-marker='task']
665
+ .task-checkbox::before {
666
+ content: '';
667
+ visibility: visible;
668
+ /* Centred by layout and sized to whole pixels, never by a translate: a transformed box
669
+ rasterises at fractional edges on a scaled desktop and reads shorter than it is wide.
670
+ The unrounded pair is the fallback for a browser without `round()`. */
671
+ position: absolute;
672
+ inset: 0;
673
+ margin: auto;
674
+ width: 1.2em;
675
+ height: 1.2em;
676
+ width: round(1.2em, 1px);
677
+ height: round(1.2em, 1px);
678
+ box-sizing: border-box;
679
+ border: 1.5px solid var(--color-ui-muted, #8f8f89);
680
+ border-radius: 0.28em;
681
+ background: transparent;
682
+ }
683
+
684
+ /* Checked: the box fills with the text colour and the tick is cut from it in the page's
685
+ background — two borders of a rotated box, so it needs no glyph and scales with the type. */
686
+ :where(.editor):is(
687
+ [data-presentation='reading'],
688
+ [data-presentation^='preview-'],
689
+ [data-presentation='live']
690
+ )
691
+ .list-item-block[data-task-checked='true']
692
+ .task-checkbox::before {
693
+ background: var(--color-text-primary, currentColor);
694
+ border-color: var(--color-text-primary, currentColor);
695
+ }
696
+
697
+ :where(.editor):is(
698
+ [data-presentation='reading'],
699
+ [data-presentation^='preview-'],
700
+ [data-presentation='live']
701
+ )
702
+ .list-item-block[data-task-checked='true']
703
+ .task-checkbox::after {
704
+ content: '';
705
+ visibility: visible;
706
+ position: absolute;
707
+ /* The slot's centre, nudged up and right: the visible strokes are the L's right and
708
+ bottom edges, which rotation swings down and left. */
709
+ left: 53%;
710
+ top: 45%;
711
+ width: 0.32em;
712
+ height: 0.6em;
713
+ box-sizing: border-box;
714
+ border: solid var(--color-bg, #2c2c2a);
715
+ border-width: 0 0.15em 0.15em 0;
716
+ transform: translate(-50%, -50%) rotate(45deg);
717
+ }
718
+
719
+ /* The span is the SLOT, a full line-height tall; the box is the pseudo-element inside it. The
720
+ hover tint the source modes put on the span (ListItemBlock) would draw a tall rectangle
721
+ round the square, so in the rendered modes it moves onto the box: an open box fills with
722
+ the tint, a checked one is already a solid fill and keeps it. */
723
+ :where(.editor):is(
724
+ [data-presentation='reading'],
725
+ [data-presentation^='preview-'],
726
+ [data-presentation='live']
727
+ )
728
+ .list-item-block[data-list-marker='task']
729
+ .task-checkbox:hover {
730
+ background-color: transparent;
731
+ }
732
+
733
+ :where(.editor):is(
734
+ [data-presentation='reading'],
735
+ [data-presentation^='preview-'],
736
+ [data-presentation='live']
737
+ )
738
+ .list-item-block[data-list-marker='task']
739
+ .task-checkbox::before {
740
+ transition: background-color 60ms ease-out;
741
+ }
742
+
743
+ :where(.editor):is(
744
+ [data-presentation='reading'],
745
+ [data-presentation^='preview-'],
746
+ [data-presentation='live']
747
+ )
748
+ .list-item-block[data-list-marker='task']:not([data-task-checked='true'])
749
+ .task-checkbox:hover::before {
750
+ background-color: var(--md-marker-hover-bg, rgba(128, 128, 128, 0.15));
510
751
  }
511
752
 
512
753
  /* ── Reading mode ─────────────────────────────────────────────────────────────
@@ -550,6 +791,15 @@
550
791
  display: inline;
551
792
  }
552
793
 
794
+ /* The body's caret anchor stands in for the closer line while that line hides (see
795
+ `code-renderer.ts`); wherever the closer paints it would add a blank line instead. */
796
+ :where(.editor):not([data-presentation]) br[data-caret-anchor='closer'],
797
+ :where(.editor)[data-presentation^='preview-']
798
+ .block-host[data-focused]
799
+ br[data-caret-anchor='closer'] {
800
+ display: none;
801
+ }
802
+
553
803
  /* Reference labels reveal on the focused block under preview-block. */
554
804
  :where(.editor)[data-presentation='preview-block'] .block-host[data-focused] .md-ref-label {
555
805
  display: inline;
@@ -19,7 +19,7 @@ import { isBlockOpenerRegistered } from '../schema/block-openers';
19
19
  import { getBlockKindDescriptor } from '../schema/block-kind-descriptor';
20
20
  import { rebuildContainerRawIfContainer } from '../schema/container-raw';
21
21
  import { createSharingState } from '../tree-operations/sharing';
22
- import { rebuildUnsharedAncestry } from '../tree-operations/unshare';
22
+ import { rebuildUnsharedAncestry } from '../tree-operations/chain-rebuild';
23
23
  import { assertParseConverged } from './parse-convergence';
24
24
  import { createHeadlessActions, mountBlockListState, recordingFocus, stubBlockEdit, stubStickyColumn } from './headless-actions';
25
25
  import { assert, assertExemptionDocumented, assertIndices, assertIs, assertRebuildIsParseCanonical, assertReasonDocumented, fail, findFirstOfKind, firstChildOfKind, nodeAtPath, pathPassesThroughKind } from './conformance-core';
@@ -487,7 +487,7 @@ function assertContentStartSpaceIsRebuilt(kind, descriptor) {
487
487
  /**
488
488
  * `container.bodyWrap` is probed, never trusted: the separator settle reads it to decide whether
489
489
  * a freed blank line belongs to the wrap, and a kind whose parse disagrees loses its body head
490
- * on reload (`tree-operations/node-ops.clearRedundantSeparator`).
490
+ * on reload (`tree-operations/settle.clearRedundantSeparator`).
491
491
  */
492
492
  function assertBodyWrapMatchesParse(kind, descriptor) {
493
493
  if (descriptor.containerContract === 'grid')
@@ -379,7 +379,8 @@ function assertRewriteReproducesSource(fixture, node, rung) {
379
379
  ...(node.title !== undefined ? { title: node.title } : {}),
380
380
  ...(node.label !== undefined ? { label: node.label } : {}),
381
381
  ...(node.width !== undefined ? { width: node.width } : {}),
382
- ...(node.height !== undefined ? { height: node.height } : {})
382
+ ...(node.height !== undefined ? { height: node.height } : {}),
383
+ ...(node.crop !== undefined ? { crop: node.crop } : {})
383
384
  };
384
385
  assertIs(rung.rewriteImage(source, fields), source, `rewriteImage re-emits ${JSON.stringify(source)} from the fields the scan read out of it`);
385
386
  }
@@ -2,7 +2,7 @@ import { cloneMetadata, cloneNode } from './clone';
2
2
  import { rebuildBlockquoteRaw } from '../schema/container-rebuilders';
3
3
  import { rebuildContainerRaw } from '../schema/container-raw';
4
4
  import { assignIds } from '../block-id';
5
- import { emptyParagraph } from './node-ops';
5
+ import { emptyParagraph } from './node-primitives';
6
6
  import { trailingLineEnding } from '../core/lines';
7
7
  /**
8
8
  * Unwrap a quote-shaped container's first child (Rule U2), returning fresh clones without
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The ancestry rebuild: raws re-derived along an owned spine chain innermost-first, each
3
+ * container's kind re-derived and the seam at its own slot asked on the way out (editor.md § 9).
4
+ */
5
+ import type { CstNode } from '../core/nodes';
6
+ import type { SharingState } from './sharing';
7
+ import type { NodeParent } from './node-primitives';
8
+ import type { StructuralChange } from './structural-change';
9
+ import type { GrammarView } from '../schema/block-openers';
10
+ import { type TrackedPosition } from './settle';
11
+ /**
12
+ * Longest prefix of `chain` still attached under `root`, identity-checked level by level. A
13
+ * commit mutation can splice a node out mid-ceremony, and rebuilding the detached node's raw
14
+ * against its emptied children writes `raw: ''`; ancestors above the detachment still rebuild.
15
+ */
16
+ export declare function attachedChainPrefix(root: NodeParent, chain: CstNode[]): CstNode[];
17
+ /**
18
+ * One container slot a rebuild re-kinded. `previous` is the rollback register: a commit
19
+ * unwinding after the swap has already published `replacement` into a live children array.
20
+ */
21
+ export interface ContainerReclassification {
22
+ siblings: CstNode[];
23
+ index: number;
24
+ previous: CstNode;
25
+ replacement: CstNode;
26
+ }
27
+ /**
28
+ * A fold the rebuilt container's own slot owed: its bytes stopped interrupting a neighbour, so
29
+ * the parent's array reloads as fewer blocks. `before` is the pre-splice array, the rollback
30
+ * register, since the splice lands in an array no commit descriptor covers.
31
+ */
32
+ export interface AncestrySeamFold {
33
+ /** Chain level of the folded container, so a caller composes the owner's path from its own. */
34
+ depth: number;
35
+ siblings: CstNode[];
36
+ /** The chain node owning `siblings`, or null when they are the rebuild root's children. */
37
+ owner: CstNode | null;
38
+ change: StructuralChange;
39
+ before: CstNode[];
40
+ landing: TrackedPosition;
41
+ }
42
+ /**
43
+ * What the typing door knows and a bare chain does not: where the chain sits in the document,
44
+ * and the bytes its leaf carried before the write. Both are guesses the rebuild identity-checks.
45
+ */
46
+ export interface ChainWriteHint {
47
+ path: number[];
48
+ leafPreviousRaw: string;
49
+ }
50
+ /**
51
+ * Rebuild raws along an owned spine chain innermost-first, re-deriving each container's kind and
52
+ * settling the seams at its own slot; chain- rather than path-based so it survives index shifts.
53
+ * Both passes gate on a boundary line of the rebuilt raw moving. `folds` is required-nullable: a
54
+ * fold splices the PARENT's array, so only a caller that reconciles that scope's ids/refs passes
55
+ * a sink.
56
+ */
57
+ export declare function rebuildUnsharedChain(root: NodeParent | CstNode, chain: CstNode[], sharing: SharingState, folds: AncestrySeamFold[] | null, grammar: GrammarView | undefined, hint?: ChainWriteHint): ContainerReclassification[];
58
+ /**
59
+ * Unshare the spine to `path` and rebuild it innermost-first, tolerating paths that ran out of
60
+ * range mid-walk (post-delete rebuild passes). Prefer `rebuildUnsharedChain` when indices may
61
+ * have shifted since the unshare.
62
+ */
63
+ export declare function rebuildUnsharedAncestry(root: NodeParent, path: number[], sharing: SharingState, folds: AncestrySeamFold[] | null, grammar: GrammarView | undefined): ContainerReclassification[];