@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.
- package/README.md +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +18 -0
- package/dist/a11y-strings.js +18 -0
- package/dist/action-contracts.d.ts +7 -1
- package/dist/ambient/ambient-dom.js +5 -1
- package/dist/block-component.d.ts +14 -0
- package/dist/components/BlockDragHandle.svelte +35 -24
- package/dist/components/BlockHost.svelte +16 -9
- package/dist/components/Editor.svelte +381 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +17 -3
- package/dist/components/TailInsert.svelte +107 -0
- package/dist/components/TailInsert.svelte.d.ts +17 -0
- package/dist/components/block-content-selector.d.ts +6 -2
- package/dist/components/block-content-selector.js +6 -2
- package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/code/CodeBlock.svelte +211 -30
- package/dist/components/blocks/code/CodeBlockRail.svelte +686 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +4 -0
- package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
- package/dist/components/blocks/code/code-context-actions.js +24 -0
- package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
- package/dist/components/blocks/code/code-fence-exit.js +26 -0
- package/dist/components/blocks/code/code-languages.d.ts +6 -0
- package/dist/components/blocks/code/code-languages.js +11 -0
- package/dist/components/blocks/code/code-renderer.js +11 -0
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
- package/dist/components/blocks/editable-leaf.d.ts +37 -6
- package/dist/components/blocks/editable-leaf.js +243 -29
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
- package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.js +11 -2
- package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
- package/dist/components/blocks/surface-wiring.svelte.js +8 -1
- package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
- package/dist/components/blocks/table/TableBlock.svelte +205 -186
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
- package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
- package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
- package/dist/components/blocks/table/cell-clipboard.js +34 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
- package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
- package/dist/components/blocks/table/table-cell-paste.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
- package/dist/components/blocks/table/table-menu-model.js +36 -17
- package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
- package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
- package/dist/components/blocks/text/delimiter-autopair.js +216 -0
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
- package/dist/components/blocks/text/edge-policy-dispatch.js +56 -2
- package/dist/components/blocks/text/live-selection-edit.js +27 -0
- package/dist/components/blocks/text/text-keydown.d.ts +7 -1
- package/dist/components/blocks/text/text-keydown.js +11 -1
- package/dist/components/blocks/text/text-render.d.ts +1 -1
- package/dist/components/blocks/text/text-render.js +3 -1
- package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
- package/dist/components/blocks/text/widget-interaction.js +144 -22
- package/dist/components/drag-handle.d.ts +35 -0
- package/dist/components/drag-handle.js +126 -0
- package/dist/components/editor-root-focus.d.ts +19 -0
- package/dist/components/editor-root-focus.js +67 -0
- package/dist/components/editor-root-geometry.d.ts +39 -0
- package/dist/components/editor-root-geometry.js +91 -0
- package/dist/components/editor-root-keydown.d.ts +1 -1
- package/dist/components/editor-root-keydown.js +12 -3
- package/dist/components/editor-root-listeners.d.ts +10 -6
- package/dist/components/editor-root-listeners.js +23 -22
- package/dist/components/editor-root-mode-flip.d.ts +36 -0
- package/dist/components/editor-root-mode-flip.js +92 -0
- package/dist/components/image/ImageOverlayHost.svelte +14 -6
- package/dist/components/image/ImageProperties.svelte +512 -70
- package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
- package/dist/components/image/ImageResizeHandles.svelte +59 -34
- package/dist/components/image/image-crop.d.ts +39 -0
- package/dist/components/image/image-crop.js +74 -0
- package/dist/components/image/image-edit-commit.d.ts +1 -0
- package/dist/components/image/image-edit-commit.js +21 -5
- package/dist/components/image/image-source-bytes.js +10 -3
- package/dist/components/image/image-widget-editing.js +1 -0
- package/dist/components/image/widget-dom.js +5 -1
- package/dist/components/link-card/link-card-commit.js +1 -1
- package/dist/components/lrd-map-gate.js +1 -1
- package/dist/components/menu/BlockMenu.svelte +315 -0
- package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
- package/dist/components/menu/MenuIcon.svelte +153 -0
- package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
- package/dist/components/menu/clipboard-actions.d.ts +12 -0
- package/dist/components/menu/clipboard-actions.js +42 -0
- package/dist/components/menu/default-context-actions.d.ts +15 -0
- package/dist/components/menu/default-context-actions.js +76 -0
- package/dist/components/menu/flyout-placement.d.ts +6 -0
- package/dist/components/menu/flyout-placement.js +25 -0
- package/dist/core/inline/format-toggle.d.ts +12 -4
- package/dist/core/inline/format-toggle.js +94 -40
- package/dist/core/inline/image-dimensions.d.ts +3 -0
- package/dist/core/inline/image-dimensions.js +46 -10
- package/dist/core/inline/inline-widgets.d.ts +11 -0
- package/dist/core/inline/scan/brackets.js +1 -0
- package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
- package/dist/core/inline/scan/plugin-syntax.js +15 -1
- package/dist/core/inline-render.d.ts +6 -0
- package/dist/core/inline-render.js +32 -0
- package/dist/core/nodes.d.ts +14 -0
- package/dist/cursor/edge-affinity.js +2 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/widget-offset.d.ts +6 -0
- package/dist/cursor/widget-offset.js +61 -4
- package/dist/debug/interaction-trace.d.ts +4 -0
- package/dist/debug/interaction-trace.js +15 -0
- package/dist/decorations/decoration-state.svelte.js +1 -1
- package/dist/editor-actions/ancestry-folds.d.ts +2 -2
- package/dist/editor-actions/ancestry-folds.js +1 -1
- package/dist/editor-actions/block-edit-scope.js +1 -1
- package/dist/editor-actions/commit/text-batch.d.ts +3 -2
- package/dist/editor-actions/commit/text-batch.js +1 -1
- package/dist/editor-actions/commit/undo-controller.js +4 -2
- package/dist/editor-actions/container-edit.js +2 -1
- package/dist/editor-actions/enter-completion.d.ts +2 -0
- package/dist/editor-actions/enter-completion.js +23 -2
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/reorder-action.js +19 -10
- package/dist/editor-actions/reorder-drag.js +29 -1
- package/dist/editor-actions/replacement-focus.d.ts +1 -1
- package/dist/editor-actions/replacement-focus.js +1 -1
- package/dist/editor-actions/search-replace.js +1 -1
- package/dist/editor-actions/table-context.d.ts +4 -1
- package/dist/editor-actions/table-context.js +57 -1
- package/dist/editor-events.d.ts +3 -0
- package/dist/editor-keys.d.ts +33 -0
- package/dist/editor-props.d.ts +21 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +11 -0
- package/dist/plugins/latex/BlockMath.svelte +264 -29
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
- package/dist/plugins/latex/index.d.ts +2 -1
- package/dist/plugins/latex/latex-kind.js +36 -3
- package/dist/plugins/latex/math-completion.js +4 -1
- package/dist/plugins/latex/math-layout.d.ts +15 -0
- package/dist/plugins/latex/math-layout.js +13 -0
- package/dist/plugins/latex/math-source.d.ts +19 -0
- package/dist/plugins/latex/math-source.js +97 -0
- package/dist/plugins/latex/register.d.ts +11 -2
- package/dist/plugins/latex/register.js +3 -1
- package/dist/plugins/latex/renderer.d.ts +3 -3
- package/dist/plugins/latex/renderer.js +13 -6
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +49 -24
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/context-actions.d.ts +31 -0
- package/dist/schema/context-actions.js +23 -0
- package/dist/schema/fenced-code-raw.js +31 -1
- package/dist/schema/operations.d.ts +8 -1
- package/dist/schema/reserved-chords.js +25 -4
- package/dist/schema/table-cell-raw.d.ts +1 -1
- package/dist/schema/table-cell-raw.js +1 -1
- package/dist/selection/block-hit-test.js +3 -2
- package/dist/selection/char-endpoint-snap.js +1 -1
- package/dist/selection/clipboard-text.js +6 -1
- package/dist/selection/covered-block.d.ts +10 -0
- package/dist/selection/covered-block.js +24 -0
- package/dist/selection/cross-block/dispatch.d.ts +3 -0
- package/dist/selection/cross-block/dispatch.js +13 -1
- package/dist/selection/cross-block/format-range.d.ts +1 -1
- package/dist/selection/cross-block/format-range.js +4 -15
- package/dist/selection/cross-block/format-toggle.js +1 -1
- package/dist/selection/cross-block/keydown.js +1 -1
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +32 -30
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +62 -13
- package/dist/selection/dead-space-caret.d.ts +10 -0
- package/dist/selection/dead-space-caret.js +47 -1
- package/dist/selection/double-click-trim.d.ts +17 -0
- package/dist/selection/double-click-trim.js +57 -0
- package/dist/selection/drag-pointer.d.ts +7 -2
- package/dist/selection/drag-pointer.js +61 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.js +1 -1
- package/dist/selection/path-lookup.js +1 -1
- package/dist/selection/range-delete-ceremony.js +4 -2
- package/dist/selection/range-delete-chrome.js +2 -1
- package/dist/selection/range-delete-table-coverage.js +2 -1
- package/dist/selection/range-delete-table.js +3 -2
- package/dist/selection/range-delete.js +30 -2
- package/dist/selection/selection-restore.js +1 -1
- package/dist/selection/selection-state.svelte.d.ts +6 -0
- package/dist/selection/selection-state.svelte.js +36 -1
- package/dist/selection/table-endpoint-snap.js +1 -1
- package/dist/selection/table-rect-extend.js +1 -1
- package/dist/styles/editor-theme.css +57 -28
- package/dist/styles/editor.css +217 -18
- package/dist/testing/container-conformance.js +2 -2
- package/dist/testing/inline-conformance.js +2 -1
- package/dist/tree-operations/blockquote.js +1 -1
- package/dist/tree-operations/chain-rebuild.d.ts +63 -0
- package/dist/tree-operations/chain-rebuild.js +142 -0
- package/dist/tree-operations/children.d.ts +1 -1
- package/dist/tree-operations/children.js +1 -1
- package/dist/tree-operations/cleanup.js +1 -1
- package/dist/tree-operations/content-write.d.ts +50 -0
- package/dist/tree-operations/content-write.js +263 -0
- package/dist/tree-operations/index.d.ts +8 -3
- package/dist/tree-operations/index.js +6 -2
- package/dist/tree-operations/list/exit-replacement.js +1 -1
- package/dist/tree-operations/list/unwrap-merge.js +3 -3
- package/dist/tree-operations/node-ops.d.ts +17 -234
- package/dist/tree-operations/node-ops.js +47 -1113
- package/dist/tree-operations/node-primitives.d.ts +75 -0
- package/dist/tree-operations/node-primitives.js +117 -0
- package/dist/tree-operations/paste/apply.js +1 -1
- package/dist/tree-operations/paste/body-write.d.ts +1 -1
- package/dist/tree-operations/paste/body-write.js +2 -2
- package/dist/tree-operations/paste/container-match.js +4 -2
- package/dist/tree-operations/paste/dispatch.js +2 -1
- package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
- package/dist/tree-operations/paste/focus-target.d.ts +1 -1
- package/dist/tree-operations/paste/list-absorb.js +1 -1
- package/dist/tree-operations/paste/list-break-out.js +1 -1
- package/dist/tree-operations/paste/parent-scope.js +1 -1
- package/dist/tree-operations/paste/paste-replacement.js +1 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +1 -1
- package/dist/tree-operations/path-mutate.d.ts +1 -1
- package/dist/tree-operations/path-mutate.js +2 -1
- package/dist/tree-operations/reorder-unit.js +1 -1
- package/dist/tree-operations/reorder.d.ts +5 -2
- package/dist/tree-operations/reorder.js +55 -2
- package/dist/tree-operations/settle.d.ts +105 -0
- package/dist/tree-operations/settle.js +660 -0
- package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
- package/dist/tree-operations/table-grid-clipboard.js +90 -0
- package/dist/tree-operations/unshare.d.ts +15 -77
- package/dist/tree-operations/unshare.js +15 -166
- package/docs/guide/consumer-guide.md +126 -96
- package/docs/guide/plugin-api.md +31 -3
- package/docs/guide/plugin-guide.md +26 -3
- package/package.json +4 -2
- package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
- package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
- package/dist/components/blocks/table/TableGrip.svelte +0 -91
- package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
- package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
- package/dist/components/blocks/table/table-drop-target.js +0 -16
- package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
- package/dist/components/blocks/table/table-reorder-drag.js +0 -97
package/dist/styles/editor.css
CHANGED
|
@@ -168,6 +168,29 @@
|
|
|
168
168
|
max-width: 100%;
|
|
169
169
|
height: auto;
|
|
170
170
|
display: block;
|
|
171
|
+
/* Enough to take the hard corner off a photo sitting in prose, not enough to read as a
|
|
172
|
+
card: half the surface radius the block chrome uses. */
|
|
173
|
+
border-radius: 4px;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* A cropped image: the widget is the frame (width and aspect set inline from the `|WxH` hint,
|
|
177
|
+
capped to the column) and the image pans inside it, sized and offset by `image-crop.ts`. */
|
|
178
|
+
:where(.editor) .md-image-widget.md-image-cropped {
|
|
179
|
+
overflow: hidden;
|
|
180
|
+
max-width: 100%;
|
|
181
|
+
/* The frame clips the picture, so the rounding belongs to it; the picture inside is square
|
|
182
|
+
again or its own corners would cut inside the frame's. */
|
|
183
|
+
border-radius: 4px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
:where(.editor) .md-image-widget.md-image-cropped img {
|
|
187
|
+
border-radius: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
:where(.editor) .md-image-widget.md-image-cropped img {
|
|
191
|
+
position: absolute;
|
|
192
|
+
max-width: none;
|
|
193
|
+
object-fit: cover;
|
|
171
194
|
}
|
|
172
195
|
|
|
173
196
|
/* Image-bearing list-item / blockquote paragraph: the ambient marker sits at the image's
|
|
@@ -249,6 +272,12 @@
|
|
|
249
272
|
pointer-events: none;
|
|
250
273
|
}
|
|
251
274
|
|
|
275
|
+
/* The tint IS the widget's selection paint: native `::selection` on the text inside it (a KaTeX
|
|
276
|
+
box is text) would stack a second wash on the same pixels. */
|
|
277
|
+
:where(.editor) [data-inline-widget].md-widget-selected ::selection {
|
|
278
|
+
background: transparent;
|
|
279
|
+
}
|
|
280
|
+
|
|
252
281
|
/* Synthetic caret on the snap-target edge — Chromium doesn't reliably render
|
|
253
282
|
the live caret adjacent to a contenteditable=false neighbor. */
|
|
254
283
|
:where(.editor) [data-inline-widget].md-snap-after::before,
|
|
@@ -263,12 +292,14 @@
|
|
|
263
292
|
pointer-events: none;
|
|
264
293
|
}
|
|
265
294
|
|
|
295
|
+
/* Clear of the widget's own edge: against a picture, a caret drawn tight to the frame reads as
|
|
296
|
+
part of the border rather than as a caret waiting for the next character. */
|
|
266
297
|
:where(.editor) [data-inline-widget].md-snap-after::before {
|
|
267
|
-
right: -
|
|
298
|
+
right: -4px;
|
|
268
299
|
}
|
|
269
300
|
|
|
270
301
|
:where(.editor) [data-inline-widget].md-snap-before::before {
|
|
271
|
-
left: -
|
|
302
|
+
left: -4px;
|
|
272
303
|
}
|
|
273
304
|
|
|
274
305
|
@keyframes md-snap-caret-blink {
|
|
@@ -350,7 +381,7 @@
|
|
|
350
381
|
/* Dimmed monospace gives inline raw HTML a code-ish feel without becoming a code span. */
|
|
351
382
|
:where(.editor) .md-raw-html {
|
|
352
383
|
color: var(--md-raw-html-color, rgba(120, 120, 120, 0.85));
|
|
353
|
-
font-family: var(--font-
|
|
384
|
+
font-family: var(--font-code, ui-monospace, monospace);
|
|
354
385
|
font-size: 0.92em;
|
|
355
386
|
}
|
|
356
387
|
|
|
@@ -367,7 +398,7 @@
|
|
|
367
398
|
width: 100%;
|
|
368
399
|
box-sizing: border-box;
|
|
369
400
|
min-height: 1.4em;
|
|
370
|
-
font-family: var(--font-
|
|
401
|
+
font-family: var(--font-code, ui-monospace, monospace);
|
|
371
402
|
font-size: 0.9em;
|
|
372
403
|
line-height: 1.5;
|
|
373
404
|
color: inherit;
|
|
@@ -394,6 +425,13 @@
|
|
|
394
425
|
color: var(--syntax-code, currentColor);
|
|
395
426
|
}
|
|
396
427
|
|
|
428
|
+
/* An inline widget's source while it is revealed for editing (`$x$`, a footnote ref): the
|
|
429
|
+
inline-code wash, so the span being edited reads as a span, in the prose face so the line
|
|
430
|
+
does not reflow around it. */
|
|
431
|
+
::highlight(md-inline-reveal) {
|
|
432
|
+
background-color: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
|
|
433
|
+
}
|
|
434
|
+
|
|
397
435
|
/* Raw-HTML block source text; inline raw HTML keeps --md-raw-html-color above. */
|
|
398
436
|
:where(.editor) .text-editable-block.raw-block {
|
|
399
437
|
color: var(--syntax-comment, currentColor);
|
|
@@ -404,7 +442,7 @@
|
|
|
404
442
|
}
|
|
405
443
|
|
|
406
444
|
:where(.editor) .thematic-break-rule[role='separator'] > hr {
|
|
407
|
-
border-top-color: var(--syntax-separator, var(--color-
|
|
445
|
+
border-top-color: var(--syntax-separator, var(--color-border, #3e3e3b));
|
|
408
446
|
}
|
|
409
447
|
|
|
410
448
|
/* The [contenteditable='false'] qualifier scopes to the structurally-injected ambient
|
|
@@ -417,12 +455,69 @@
|
|
|
417
455
|
color: var(--syntax-task, currentColor);
|
|
418
456
|
}
|
|
419
457
|
|
|
420
|
-
/*
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
458
|
+
/* ── Menus ──────────────────────────────────────────────────────────────────
|
|
459
|
+
One surface for every menu the editor opens (the table's context menu, the code rail's
|
|
460
|
+
picker and overflow), copied from the host app's context menu value for value and reading
|
|
461
|
+
the same tokens, so a menu here is indistinguishable from one of the host's. */
|
|
462
|
+
:where(.editor) .md-menu {
|
|
463
|
+
position: fixed;
|
|
464
|
+
z-index: 2000;
|
|
465
|
+
min-width: 168px;
|
|
466
|
+
padding: 4px;
|
|
467
|
+
background: var(--color-bg, #2c2c2a);
|
|
468
|
+
border: 1px solid var(--color-border, #3e3e3b);
|
|
469
|
+
border-radius: 8px;
|
|
470
|
+
box-shadow: var(--menu-shadow, 0 12px 32px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.35));
|
|
471
|
+
font-family: var(--font-ui, system-ui, sans-serif);
|
|
472
|
+
font-size: 13px;
|
|
473
|
+
line-height: 1.4;
|
|
474
|
+
color: var(--color-text-primary, #e8e8e5);
|
|
475
|
+
user-select: none;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
:where(.editor) .md-menu-item {
|
|
479
|
+
display: flex;
|
|
480
|
+
align-items: center;
|
|
481
|
+
gap: 9px;
|
|
482
|
+
width: 100%;
|
|
483
|
+
padding: 7px 10px;
|
|
484
|
+
border: none;
|
|
485
|
+
border-radius: 5px;
|
|
486
|
+
background: transparent;
|
|
487
|
+
color: inherit;
|
|
488
|
+
font: inherit;
|
|
489
|
+
text-align: left;
|
|
490
|
+
cursor: pointer;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
:where(.editor) .md-menu-item:hover,
|
|
494
|
+
:where(.editor) .md-menu-item:focus-visible,
|
|
495
|
+
:where(.editor) .md-menu-item[aria-pressed='true'],
|
|
496
|
+
:where(.editor) .md-menu-item[data-active='true'] {
|
|
497
|
+
background: var(--menu-item-hover, rgba(255, 255, 255, 0.07));
|
|
498
|
+
outline: none;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
:where(.editor) .md-menu-item:disabled {
|
|
502
|
+
opacity: 0.4;
|
|
503
|
+
cursor: default;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
:where(.editor) .md-menu-item:disabled:hover {
|
|
507
|
+
background: transparent;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
:where(.editor) .md-menu-icon {
|
|
511
|
+
display: inline-flex;
|
|
512
|
+
align-items: center;
|
|
513
|
+
flex-shrink: 0;
|
|
514
|
+
color: var(--color-ui-muted, #8f8f89);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
:where(.editor) .md-menu-divider {
|
|
518
|
+
height: 1px;
|
|
519
|
+
margin: 4px 6px;
|
|
520
|
+
background: var(--color-border, #3e3e3b);
|
|
426
521
|
}
|
|
427
522
|
|
|
428
523
|
/* ── Rendered chrome, shared by every marker-hiding mode ──────────────────────
|
|
@@ -450,17 +545,33 @@
|
|
|
450
545
|
}
|
|
451
546
|
|
|
452
547
|
/* Chrome with nothing behind it has nowhere to hide: a construct whose only bytes are its own
|
|
453
|
-
markers
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
548
|
+
markers has no caret position, so an editable rung shows them dimmed as source does — but
|
|
549
|
+
only while the surface holds focus, since the seat is for a caret and a block nobody is in
|
|
550
|
+
reads as the empty construct it is. Mirrored in `cursor/widget-offset.ts` (same modes, same
|
|
551
|
+
families); reading keeps its silence. `[contenteditable]` scopes the stamp to the walk
|
|
552
|
+
containers its JS twin reads, which a decoration on the block host cannot reach. */
|
|
458
553
|
:where(.editor):is([data-presentation^='preview-'], [data-presentation='live'])
|
|
459
|
-
[contenteditable][data-content-empty]
|
|
554
|
+
[contenteditable][data-content-empty]:focus-within
|
|
460
555
|
:is(.md-marker:not([contenteditable='false']), .md-fence-line) {
|
|
461
556
|
display: inline;
|
|
462
557
|
}
|
|
463
558
|
|
|
559
|
+
/* A code block or table is a box, and prose gives it nothing to stand off from: blocks stack flush,
|
|
560
|
+
with only a text line's own leading between them. The box brings its own breathing room —
|
|
561
|
+
about a text line's leading each side, so code-to-code and code-to-prose match. Padding,
|
|
562
|
+
not margin: the height model measures the host's border box. */
|
|
563
|
+
:where(.editor) .block-host:is([data-block-kind='fencedCode'], [data-block-kind='table']) {
|
|
564
|
+
padding-block: 6px;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/* Room for the add strips (`TableBlock.svelte`) INSIDE the host's box: on the right so a
|
|
568
|
+
full-width table still leaves the column strip inside the editor's scroll box, and below so
|
|
569
|
+
the row strip never lies over whatever follows the table (the trailing insert row, a block). */
|
|
570
|
+
:where(.editor) .block-host[data-block-kind='table'] {
|
|
571
|
+
padding-right: 30px;
|
|
572
|
+
padding-bottom: 24px;
|
|
573
|
+
}
|
|
574
|
+
|
|
464
575
|
/* Ambient list markers own the hanging indent (padding/text-indent are keyed to the span's
|
|
465
576
|
ch width), so they keep their box: visibility, not display. */
|
|
466
577
|
:where(.editor):is(
|
|
@@ -498,7 +609,21 @@
|
|
|
498
609
|
visibility: visible;
|
|
499
610
|
}
|
|
500
611
|
|
|
501
|
-
/* GitHub task lists
|
|
612
|
+
/* GitHub task lists: no bullet, the box at the margin, the text one gap after it. The `- `
|
|
613
|
+
marker collapses to nothing (its bytes stay for the walk), the `[ ]` span becomes a fixed
|
|
614
|
+
slot so checked and unchecked are the same width, and the box paints in that slot. */
|
|
615
|
+
:where(.editor):is(
|
|
616
|
+
[data-presentation='reading'],
|
|
617
|
+
[data-presentation^='preview-'],
|
|
618
|
+
[data-presentation='live']
|
|
619
|
+
)
|
|
620
|
+
.list-item-block[data-list-marker='task']
|
|
621
|
+
.task-list-marker {
|
|
622
|
+
display: inline-block;
|
|
623
|
+
width: 0;
|
|
624
|
+
overflow: hidden;
|
|
625
|
+
}
|
|
626
|
+
|
|
502
627
|
:where(.editor):is(
|
|
503
628
|
[data-presentation='reading'],
|
|
504
629
|
[data-presentation^='preview-'],
|
|
@@ -506,7 +631,72 @@
|
|
|
506
631
|
)
|
|
507
632
|
.list-item-block[data-list-marker='task']
|
|
508
633
|
.task-checkbox {
|
|
634
|
+
/* Visible inside the hidden ambient marker, its `[ ]` bytes gone by colour: a hidden span
|
|
635
|
+
leaves the accessibility tree and the hit-test, and the pseudo-elements draw the box. */
|
|
509
636
|
visibility: visible;
|
|
637
|
+
color: transparent;
|
|
638
|
+
position: relative;
|
|
639
|
+
display: inline-block;
|
|
640
|
+
/* Slot + gap; `TASK_HANGING_INDENT` in task-checkbox.ts is these plus the trailing space. */
|
|
641
|
+
width: 1.2em;
|
|
642
|
+
margin-right: 0.65em;
|
|
643
|
+
vertical-align: baseline;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
:where(.editor):is(
|
|
647
|
+
[data-presentation='reading'],
|
|
648
|
+
[data-presentation^='preview-'],
|
|
649
|
+
[data-presentation='live']
|
|
650
|
+
)
|
|
651
|
+
.list-item-block[data-list-marker='task']
|
|
652
|
+
.task-checkbox::before {
|
|
653
|
+
content: '';
|
|
654
|
+
visibility: visible;
|
|
655
|
+
position: absolute;
|
|
656
|
+
left: 50%;
|
|
657
|
+
top: 50%;
|
|
658
|
+
width: 1.2em;
|
|
659
|
+
height: 1.2em;
|
|
660
|
+
transform: translate(-50%, -50%);
|
|
661
|
+
box-sizing: border-box;
|
|
662
|
+
border: 1.5px solid var(--color-ui-muted, #8f8f89);
|
|
663
|
+
border-radius: 0.28em;
|
|
664
|
+
background: transparent;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/* Checked: the box fills with the text colour and the tick is cut from it in the page's
|
|
668
|
+
background — two borders of a rotated box, so it needs no glyph and scales with the type. */
|
|
669
|
+
:where(.editor):is(
|
|
670
|
+
[data-presentation='reading'],
|
|
671
|
+
[data-presentation^='preview-'],
|
|
672
|
+
[data-presentation='live']
|
|
673
|
+
)
|
|
674
|
+
.list-item-block[data-task-checked='true']
|
|
675
|
+
.task-checkbox::before {
|
|
676
|
+
background: var(--color-text-primary, currentColor);
|
|
677
|
+
border-color: var(--color-text-primary, currentColor);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
:where(.editor):is(
|
|
681
|
+
[data-presentation='reading'],
|
|
682
|
+
[data-presentation^='preview-'],
|
|
683
|
+
[data-presentation='live']
|
|
684
|
+
)
|
|
685
|
+
.list-item-block[data-task-checked='true']
|
|
686
|
+
.task-checkbox::after {
|
|
687
|
+
content: '';
|
|
688
|
+
visibility: visible;
|
|
689
|
+
position: absolute;
|
|
690
|
+
/* The slot's centre, nudged up and right: the visible strokes are the L's right and
|
|
691
|
+
bottom edges, which rotation swings down and left. */
|
|
692
|
+
left: 53%;
|
|
693
|
+
top: 45%;
|
|
694
|
+
width: 0.32em;
|
|
695
|
+
height: 0.6em;
|
|
696
|
+
box-sizing: border-box;
|
|
697
|
+
border: solid var(--color-bg, #2c2c2a);
|
|
698
|
+
border-width: 0 0.15em 0.15em 0;
|
|
699
|
+
transform: translate(-50%, -50%) rotate(45deg);
|
|
510
700
|
}
|
|
511
701
|
|
|
512
702
|
/* ── Reading mode ─────────────────────────────────────────────────────────────
|
|
@@ -550,6 +740,15 @@
|
|
|
550
740
|
display: inline;
|
|
551
741
|
}
|
|
552
742
|
|
|
743
|
+
/* The body's caret anchor stands in for the closer line while that line hides (see
|
|
744
|
+
`code-renderer.ts`); wherever the closer paints it would add a blank line instead. */
|
|
745
|
+
:where(.editor):not([data-presentation]) br[data-caret-anchor='closer'],
|
|
746
|
+
:where(.editor)[data-presentation^='preview-']
|
|
747
|
+
.block-host[data-focused]
|
|
748
|
+
br[data-caret-anchor='closer'] {
|
|
749
|
+
display: none;
|
|
750
|
+
}
|
|
751
|
+
|
|
553
752
|
/* Reference labels reveal on the focused block under preview-block. */
|
|
554
753
|
:where(.editor)[data-presentation='preview-block'] .block-host[data-focused] .md-ref-label {
|
|
555
754
|
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/
|
|
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/
|
|
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-
|
|
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[];
|
|
@@ -0,0 +1,142 @@
|
|
|
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 { dropChildSpans } from '../schema/child-spans';
|
|
6
|
+
import { trimTrailingLineEnding } from '../core/lines';
|
|
7
|
+
import { perfEnabled, recordRebuildDepth } from '../perf/instruments';
|
|
8
|
+
import { rebuildOwnedContainer, walkUnsharing } from './unshare';
|
|
9
|
+
import { absorbWindowSeams } from './settle';
|
|
10
|
+
import { lineOpensAs, reclassifyContainer } from './content-write';
|
|
11
|
+
import { settleSublistSeparator } from './list/sublist-separator';
|
|
12
|
+
/**
|
|
13
|
+
* Longest prefix of `chain` still attached under `root`, identity-checked level by level. A
|
|
14
|
+
* commit mutation can splice a node out mid-ceremony, and rebuilding the detached node's raw
|
|
15
|
+
* against its emptied children writes `raw: ''`; ancestors above the detachment still rebuild.
|
|
16
|
+
*/
|
|
17
|
+
export function attachedChainPrefix(root, chain) {
|
|
18
|
+
let parentChildren = root.children;
|
|
19
|
+
for (let i = 0; i < chain.length; i++) {
|
|
20
|
+
if (!parentChildren.includes(chain[i]))
|
|
21
|
+
return chain.slice(0, i);
|
|
22
|
+
parentChildren = chain[i].children ?? [];
|
|
23
|
+
}
|
|
24
|
+
return chain;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rebuild raws along an owned spine chain innermost-first, re-deriving each container's kind and
|
|
28
|
+
* settling the seams at its own slot; chain- rather than path-based so it survives index shifts.
|
|
29
|
+
* Both passes gate on a boundary line of the rebuilt raw moving. `folds` is required-nullable: a
|
|
30
|
+
* fold splices the PARENT's array, so only a caller that reconciles that scope's ids/refs passes
|
|
31
|
+
* a sink.
|
|
32
|
+
*/
|
|
33
|
+
export function rebuildUnsharedChain(root, chain, sharing, folds, grammar, hint) {
|
|
34
|
+
const reclassified = [];
|
|
35
|
+
// The bytes chain[i + 1] held before this pass. It rides up only from a door that named the
|
|
36
|
+
// leaf's own: a caller passing no hint re-derives at every level (editor.md § 9).
|
|
37
|
+
let childPreviousRaw;
|
|
38
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
39
|
+
const node = chain[i];
|
|
40
|
+
const rawBefore = node.raw;
|
|
41
|
+
const child = chain[i + 1];
|
|
42
|
+
rebuildOwnedContainer(node, sharing, child && childPreviousRaw !== undefined
|
|
43
|
+
? childRawChange(node, child, childPreviousRaw, hint?.path[i + 1])
|
|
44
|
+
: undefined);
|
|
45
|
+
if (hint)
|
|
46
|
+
childPreviousRaw = i === chain.length - 1 ? hint.leafPreviousRaw : rawBefore;
|
|
47
|
+
const openerMoved = firstLine(rawBefore) !== firstLine(node.raw);
|
|
48
|
+
const closerMoved = lastLine(rawBefore) !== lastLine(node.raw);
|
|
49
|
+
if (!openerMoved && !closerMoved)
|
|
50
|
+
continue;
|
|
51
|
+
const owner = i === 0 ? null : chain[i - 1];
|
|
52
|
+
const siblings = (owner ?? root).children;
|
|
53
|
+
const index = siblings ? childIndexOf(siblings, node, hint?.path[i]) : -1;
|
|
54
|
+
if (!siblings || index < 0)
|
|
55
|
+
continue;
|
|
56
|
+
if (openerMoved && lineOpensAs(firstLine(node.raw), grammar) !== node.kind) {
|
|
57
|
+
const replacement = reclassifyContainer({ children: siblings }, index, grammar);
|
|
58
|
+
if (replacement) {
|
|
59
|
+
sharing.stamp(replacement);
|
|
60
|
+
reclassified.push({ siblings, index, previous: node, replacement });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Ahead of the seam ask, which then reads the settled bytes: a list rebuilt down to an
|
|
64
|
+
// empty marker owes the paragraph above it a separating line.
|
|
65
|
+
if (openerMoved)
|
|
66
|
+
settleSublistSeparator(siblings, index);
|
|
67
|
+
// After the re-derive: the seam reads whatever occupies the slot now.
|
|
68
|
+
if (folds) {
|
|
69
|
+
const before = folds.length;
|
|
70
|
+
settleSlotSeams({ siblings, owner, depth: i, index, openerMoved, closerMoved }, sharing, folds);
|
|
71
|
+
// A fold re-tiled the owner's children, so its spans describe a shape that is gone.
|
|
72
|
+
if (folds.length > before && owner)
|
|
73
|
+
dropChildSpans(owner);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (perfEnabled())
|
|
77
|
+
recordRebuildDepth(chain.length);
|
|
78
|
+
return reclassified;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The changed-child hint for `node`, or undefined when `child` cannot be placed in it. The path
|
|
82
|
+
* index is a guess; an identity match is what makes it an answer.
|
|
83
|
+
*/
|
|
84
|
+
function childRawChange(node, child, previousRaw, guess) {
|
|
85
|
+
const siblings = node.children;
|
|
86
|
+
if (!siblings)
|
|
87
|
+
return undefined;
|
|
88
|
+
const index = childIndexOf(siblings, child, guess);
|
|
89
|
+
return index < 0 ? undefined : { index, previousRaw };
|
|
90
|
+
}
|
|
91
|
+
/** `indexOf` with a guess first: the scan is O(children) through the `$state` proxy. */
|
|
92
|
+
function childIndexOf(siblings, child, guess) {
|
|
93
|
+
if (guess !== undefined && siblings[guess] === child)
|
|
94
|
+
return guess;
|
|
95
|
+
return siblings.indexOf(child);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Ask the joins at a rebuilt container's slot, since its new bytes can stop interrupting a
|
|
99
|
+
* neighbour. `absorbWindowSeams` walks `at - 1 … at + added - 1`, so the two arguments below
|
|
100
|
+
* name exactly the sides whose line moved.
|
|
101
|
+
*/
|
|
102
|
+
function settleSlotSeams(slot, sharing, folds) {
|
|
103
|
+
const { siblings, index, openerMoved, closerMoved } = slot;
|
|
104
|
+
// The rollback snapshot is captured only once a fold is certain: an eager copy here cost
|
|
105
|
+
// O(children) reactive reads on every keystroke inside a large container.
|
|
106
|
+
let before = null;
|
|
107
|
+
const landing = { index, offset: 0 };
|
|
108
|
+
const settled = absorbWindowSeams({ children: siblings }, openerMoved ? index : index + 1, openerMoved && closerMoved ? 1 : 0, index, { op: 'noop' }, sharing, landing, index, () => {
|
|
109
|
+
before ??= siblings.slice();
|
|
110
|
+
});
|
|
111
|
+
if (settled.change.op === 'noop')
|
|
112
|
+
return;
|
|
113
|
+
folds.push({
|
|
114
|
+
depth: slot.depth,
|
|
115
|
+
siblings,
|
|
116
|
+
owner: slot.owner,
|
|
117
|
+
change: settled.change,
|
|
118
|
+
// A non-noop change means a splice ran, so the capture ran first.
|
|
119
|
+
before: before,
|
|
120
|
+
landing
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** The container's opener line. */
|
|
124
|
+
function firstLine(raw) {
|
|
125
|
+
const nl = raw.indexOf('\n');
|
|
126
|
+
return nl < 0 ? raw : raw.slice(0, nl);
|
|
127
|
+
}
|
|
128
|
+
/** The container's closing line: its last line carrying bytes, without the ending. */
|
|
129
|
+
function lastLine(raw) {
|
|
130
|
+
const body = trimTrailingLineEnding(raw);
|
|
131
|
+
const nl = body.lastIndexOf('\n');
|
|
132
|
+
return nl < 0 ? body : body.slice(nl + 1);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Unshare the spine to `path` and rebuild it innermost-first, tolerating paths that ran out of
|
|
136
|
+
* range mid-walk (post-delete rebuild passes). Prefer `rebuildUnsharedChain` when indices may
|
|
137
|
+
* have shifted since the unshare.
|
|
138
|
+
*/
|
|
139
|
+
export function rebuildUnsharedAncestry(root, path, sharing, folds, grammar) {
|
|
140
|
+
const chain = walkUnsharing(root, path, sharing, false);
|
|
141
|
+
return rebuildUnsharedChain(root, chain, sharing, folds, grammar);
|
|
142
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Container children mutations that keep `childIds` in lockstep, for out-of-commit-scope
|
|
3
|
-
* writes (discovered descendants/ancestors — see the `node-
|
|
3
|
+
* writes (discovered descendants/ancestors — see the `node-primitives.ts` header). Inside a
|
|
4
4
|
* commit scope the StructuralChange descriptor owns id/ref sync; here a hand-rolled
|
|
5
5
|
* splice would let the parallel id array drift and break Svelte's keyed-each rendering.
|
|
6
6
|
* The same shape change invalidates `childSpans`, so both parallel arrays settle here.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Container children mutations that keep `childIds` in lockstep, for out-of-commit-scope
|
|
3
|
-
* writes (discovered descendants/ancestors — see the `node-
|
|
3
|
+
* writes (discovered descendants/ancestors — see the `node-primitives.ts` header). Inside a
|
|
4
4
|
* commit scope the StructuralChange descriptor owns id/ref sync; here a hand-rolled
|
|
5
5
|
* splice would let the parallel id array drift and break Svelte's keyed-each rendering.
|
|
6
6
|
* The same shape change invalidates `childSpans`, so both parallel arrays settle here.
|
|
@@ -0,0 +1,50 @@
|
|
|
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 type { AnyBlockKind, CstNode } from '../core/nodes';
|
|
7
|
+
import type { NodeView } from '../core/node-views';
|
|
8
|
+
import { type GrammarView } from '../schema/block-openers';
|
|
9
|
+
import type { SharingState } from './sharing';
|
|
10
|
+
import { type StructuralChange } from './structural-change';
|
|
11
|
+
import { type BodyParentArg, type NodeParent } from './node-primitives';
|
|
12
|
+
/** What a content write settled: its change widened by every fold, and the offset the written
|
|
13
|
+
* text starts at inside that change's window (nonzero once a fold above absorbed it). */
|
|
14
|
+
export interface SettledContent {
|
|
15
|
+
change: StructuralChange;
|
|
16
|
+
textStart: number;
|
|
17
|
+
}
|
|
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 declare function updateNodeContent(parent: BodyParentArg, blockIndex: number, text: string, grammar?: GrammarView, sharing?: SharingState): SettledContent;
|
|
24
|
+
/**
|
|
25
|
+
* Refresh a node in place from its own reparse, keeping its object identity. The id resync is
|
|
26
|
+
* unconditional: the reparse can change the child count while the caller reports `noop`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function adoptReparsedFields(target: CstNode, parsed: CstNode | undefined): void;
|
|
29
|
+
/**
|
|
30
|
+
* What the grammar opens `line` as, read in isolation: asked of the opener registry and never
|
|
31
|
+
* a kind list, so a kind registered later is covered the day it registers.
|
|
32
|
+
*/
|
|
33
|
+
export declare function lineOpensAs(line: string, grammar?: GrammarView): AnyBlockKind;
|
|
34
|
+
/** Whether the ambient grammar still leaves `NEXT_PROSE_LINE` an ordinary paragraph. */
|
|
35
|
+
export declare function probeLineOpensAsProse(grammar?: GrammarView): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Re-derive the container at `index` from its own (already rebuilt) raw, replacing it in the
|
|
38
|
+
* slot when that raw now opens as a different kind (editor.md § 8). Eligibility is the opener
|
|
39
|
+
* registry: registering an opener is exactly the claim that `parse(raw)` reproduces the kind.
|
|
40
|
+
*/
|
|
41
|
+
export declare function reclassifyContainer(parent: NodeParent, index: number, grammar?: GrammarView): CstNode | null;
|
|
42
|
+
/**
|
|
43
|
+
* Where a caret at `offset` in the written text lands once {@link updateNodeContent}'s folds
|
|
44
|
+
* settled: an absorb ABOVE leaves the predecessor holding the bytes, so the slot the gesture
|
|
45
|
+
* named is gone and the offset carries what that predecessor put in front of it.
|
|
46
|
+
*/
|
|
47
|
+
export declare function settledCaretTarget(settled: SettledContent, at: number, offset: number, children: readonly NodeView[]): {
|
|
48
|
+
index: number;
|
|
49
|
+
offset: number;
|
|
50
|
+
};
|