@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.
- package/README.md +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +19 -0
- package/dist/a11y-strings.js +19 -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 +37 -24
- package/dist/components/BlockHost.svelte +18 -11
- package/dist/components/Editor.svelte +434 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +25 -17
- package/dist/components/SelectionOverlay.svelte.d.ts +1 -1
- 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 +704 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +16 -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 +22 -3
- 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 +248 -30
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListBlock.svelte +1 -0
- package/dist/components/blocks/list/ListItemBlock.svelte +14 -5
- package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +2 -0
- 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/click-snap-guard.d.ts +3 -0
- package/dist/components/blocks/text/click-snap-guard.js +8 -0
- 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 +89 -20
- package/dist/components/blocks/text/live-selection-edit.d.ts +5 -5
- package/dist/components/blocks/text/live-selection-edit.js +37 -7
- package/dist/components/blocks/text/text-clipboard.js +2 -2
- 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 +181 -37
- package/dist/components/drag-handle.d.ts +41 -0
- package/dist/components/drag-handle.js +134 -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 +3 -6
- package/dist/components/editor-root-listeners.js +3 -24
- 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 +67 -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/SelectionToolbar.svelte +385 -0
- package/dist/components/menu/SelectionToolbar.svelte.d.ts +16 -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 +19 -0
- package/dist/core/inline/inline-widgets.js +5 -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/transparency.js +3 -3
- 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/height-oracle.d.ts +2 -3
- package/dist/cursor/height-oracle.js +0 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/scroll-hold.d.ts +10 -0
- package/dist/cursor/scroll-hold.js +22 -0
- package/dist/cursor/scrollport.d.ts +9 -0
- package/dist/cursor/scrollport.js +30 -1
- package/dist/cursor/visual-lines.d.ts +5 -4
- package/dist/cursor/visual-lines.js +56 -11
- package/dist/cursor/widget-edge-snap.d.ts +28 -0
- package/dist/cursor/widget-edge-snap.js +38 -0
- package/dist/cursor/widget-offset.d.ts +9 -0
- package/dist/cursor/widget-offset.js +71 -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/decorations/reserved-attrs.js +1 -0
- 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/focus/focus-dispatch.js +7 -4
- package/dist/editor-actions/focus/focus-landing.d.ts +10 -1
- package/dist/editor-actions/focus/focus-landing.js +20 -6
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/plugin/container.d.ts +7 -0
- package/dist/editor-actions/plugin/container.js +3 -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 +24 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +16 -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 +60 -10
- 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 +103 -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/plugins/mermaid/MermaidBlock.svelte +19 -9
- package/dist/plugins/parrot/ParrotBlock.svelte +3 -1
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +75 -30
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/commands.d.ts +9 -5
- package/dist/schema/commands.js +12 -7
- 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 +37 -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 +3 -29
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +31 -35
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +52 -13
- package/dist/selection/dead-space-caret.d.ts +13 -0
- package/dist/selection/dead-space-caret.js +57 -1
- package/dist/selection/drag-pointer.d.ts +26 -2
- package/dist/selection/drag-pointer.js +103 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.d.ts +3 -2
- package/dist/selection/keyboard-extend.js +4 -3
- package/dist/selection/multi-click.d.ts +42 -0
- package/dist/selection/multi-click.js +203 -0
- package/dist/selection/native-bridge.d.ts +3 -0
- package/dist/selection/native-bridge.js +31 -2
- package/dist/selection/path-lookup.d.ts +10 -2
- package/dist/selection/path-lookup.js +11 -4
- package/dist/selection/pointer-gesture.d.ts +9 -0
- package/dist/selection/pointer-gesture.js +11 -0
- package/dist/selection/primitives.d.ts +7 -0
- package/dist/selection/primitives.js +19 -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.d.ts +4 -0
- package/dist/selection/range-delete.js +31 -3
- package/dist/selection/selection-drop.d.ts +34 -0
- package/dist/selection/selection-drop.js +253 -0
- 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 +75 -31
- package/dist/styles/editor.css +268 -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.d.ts +3 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +5 -2
- package/dist/tree-operations/paste/replacement-parse.d.ts +17 -0
- package/dist/tree-operations/paste/replacement-parse.js +22 -0
- 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 +136 -104
- package/docs/guide/plugin-api.md +73 -29
- package/docs/guide/plugin-guide.md +55 -23
- package/package.json +9 -7
- 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
|
@@ -39,7 +39,7 @@ export interface OperationDetailMap {
|
|
|
39
39
|
parentItemIdx?: number;
|
|
40
40
|
nestedItemIdx?: number;
|
|
41
41
|
} | {
|
|
42
|
-
source: 'paste-dispatch' | 'paste-dispatch-table-cell' | 'cross-block-
|
|
42
|
+
source: 'paste-dispatch' | 'paste-dispatch-table-cell' | 'cross-block-covered-block' | 'selection-drop';
|
|
43
43
|
};
|
|
44
44
|
paste: {
|
|
45
45
|
count: number;
|
|
@@ -70,6 +70,13 @@ export interface OperationDetailMap {
|
|
|
70
70
|
colIdx: number;
|
|
71
71
|
side: 'left' | 'right';
|
|
72
72
|
};
|
|
73
|
+
/** A grid pasted from `rowIdx`/`colIdx`, the table grown to hold it. */
|
|
74
|
+
tablePasteGrid: {
|
|
75
|
+
rowIdx: number;
|
|
76
|
+
colIdx: number;
|
|
77
|
+
rows: number;
|
|
78
|
+
cols: number;
|
|
79
|
+
};
|
|
73
80
|
tableDeleteColumn: {
|
|
74
81
|
colIdx: number;
|
|
75
82
|
crossBlock?: true;
|
|
@@ -15,12 +15,30 @@ export const HARDCODED_CHORD_SITES = [
|
|
|
15
15
|
keys: [],
|
|
16
16
|
note: 'Mod-click link activation — no keystroke is consumed.'
|
|
17
17
|
},
|
|
18
|
+
{
|
|
19
|
+
file: 'components/editor-root-keydown.ts',
|
|
20
|
+
chords: [],
|
|
21
|
+
keys: ['Escape'],
|
|
22
|
+
note: 'Escape closes the find bar (a reserved UI chord, enumerated). The modifier read is a REFUSAL: an unchorded character over a live range goes to the range’s text door, a chorded one to the arms below it.'
|
|
23
|
+
},
|
|
18
24
|
{
|
|
19
25
|
file: 'components/editor-root-listeners.ts',
|
|
20
26
|
chords: [],
|
|
21
27
|
keys: [],
|
|
22
28
|
note: 'The Mod-held `data-mod-active` affordance tracks flag state only — no keystroke is consumed.'
|
|
23
29
|
},
|
|
30
|
+
{
|
|
31
|
+
file: 'selection/selection-drop.ts',
|
|
32
|
+
chords: [],
|
|
33
|
+
keys: [],
|
|
34
|
+
note: 'Ctrl/Alt at the DROP is the platform copy modifier, read off a DragEvent — no keystroke is consumed.'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
file: 'selection/multi-click.ts',
|
|
38
|
+
chords: [],
|
|
39
|
+
keys: [],
|
|
40
|
+
note: 'A modified multi-click is left to the browser (Shift extends, Mod activates) — no keystroke is consumed.'
|
|
41
|
+
},
|
|
24
42
|
{
|
|
25
43
|
file: 'components/GapCaret.svelte',
|
|
26
44
|
chords: [],
|
|
@@ -45,8 +63,8 @@ export const HARDCODED_CHORD_SITES = [
|
|
|
45
63
|
{
|
|
46
64
|
file: 'components/blocks/editable-leaf.ts',
|
|
47
65
|
chords: [],
|
|
48
|
-
keys: ['Enter'],
|
|
49
|
-
note:
|
|
66
|
+
keys: ['Backspace', 'Enter'],
|
|
67
|
+
note: "Shift-click gate on the rendered surface — a pointer read; plain Backspace (no modifier) at the top of an empty painted source deletes the block, as the code block does. The reveal's own undo resolves its chord through the keymap, so it claims none here."
|
|
50
68
|
},
|
|
51
69
|
{
|
|
52
70
|
file: 'components/link-card/LinkCard.svelte',
|
|
@@ -54,6 +72,12 @@ export const HARDCODED_CHORD_SITES = [
|
|
|
54
72
|
keys: ['Enter', 'K', 'Tab', 'k'],
|
|
55
73
|
note: "Backwards step of the open card's focus trap, plus the entry chord swallowed as a no-op where the focus already is — the kind keymaps claim it everywhere else. Escape lives on the host, which must also close a card the document still holds the caret for."
|
|
56
74
|
},
|
|
75
|
+
{
|
|
76
|
+
file: 'components/menu/BlockMenu.svelte',
|
|
77
|
+
chords: [],
|
|
78
|
+
keys: ['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'Enter', 'Escape'],
|
|
79
|
+
note: 'The open block menu steps and picks; the modifier read is the guard that leaves every chord to the editor.'
|
|
80
|
+
},
|
|
57
81
|
{
|
|
58
82
|
file: 'components/blocks/table/TableActionMenu.svelte',
|
|
59
83
|
chords: ['Shift+Tab'],
|
|
@@ -115,8 +139,17 @@ export const HARDCODED_CHORD_SITES = [
|
|
|
115
139
|
{
|
|
116
140
|
file: 'components/blocks/text/widget-interaction.ts',
|
|
117
141
|
chords: ['Shift+ArrowLeft', 'Shift+ArrowRight'],
|
|
118
|
-
keys: [
|
|
119
|
-
|
|
142
|
+
keys: [
|
|
143
|
+
'Arrow*',
|
|
144
|
+
'ArrowDown',
|
|
145
|
+
'ArrowLeft',
|
|
146
|
+
'ArrowRight',
|
|
147
|
+
'ArrowUp',
|
|
148
|
+
'Backspace',
|
|
149
|
+
'Delete',
|
|
150
|
+
'Escape'
|
|
151
|
+
],
|
|
152
|
+
note: 'Extends a selection into a widget. A modified arrow is also swallowed while a widget is selected, but that is a caret-coherence guard in a transient state, not a binding; a plain vertical arrow seats a caret and declines.'
|
|
120
153
|
},
|
|
121
154
|
{
|
|
122
155
|
file: 'components/image/ImageResizeHandles.svelte',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The tableCell raw-write rule, declared on the kind as `normalizeRawWrite` and applied at the
|
|
3
|
-
* write sink (`tree-operations/
|
|
3
|
+
* write sink (`tree-operations/content-write.updateNodeContent`). A cell's bytes are joined verbatim
|
|
4
4
|
* into its row, and the parser truncates a row reparsing wider than the delimiter's column
|
|
5
5
|
* count, so a bare `|` or line break reaching a cell raw deletes the last column's content.
|
|
6
6
|
* Both passes are prefix-composable, which is what makes `escapedCellOffset` an exact caret image.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The tableCell raw-write rule, declared on the kind as `normalizeRawWrite` and applied at the
|
|
3
|
-
* write sink (`tree-operations/
|
|
3
|
+
* write sink (`tree-operations/content-write.updateNodeContent`). A cell's bytes are joined verbatim
|
|
4
4
|
* into its row, and the parser truncates a row reparsing wider than the delimiter's column
|
|
5
5
|
* count, so a bare `|` or line break reaching a cell raw deletes the last column's content.
|
|
6
6
|
* Both passes are prefix-composable, which is what makes `escapedCellOffset` an exact caret image.
|
|
@@ -26,10 +26,11 @@ export function blockAtPoint(editorRoot, clientX, clientY) {
|
|
|
26
26
|
path,
|
|
27
27
|
// A cell-addressed kind's first contenteditable is one of its CELLS, so declaring
|
|
28
28
|
// the drag hook withdraws the block-level surface rather than offering that. The
|
|
29
|
-
// whole-block editing host is chrome, not characters, so it is excluded too
|
|
29
|
+
// whole-block editing host is chrome, not characters, so it is excluded too, as is
|
|
30
|
+
// the selection overlay (`contenteditable="false"`, mounted while a range is live).
|
|
30
31
|
charSurface: dragHitTest
|
|
31
32
|
? null
|
|
32
|
-
: wrapper.querySelector(`[contenteditable]:not([${WHOLE_BLOCK_INPUT_ATTR}])`),
|
|
33
|
+
: wrapper.querySelector(`[contenteditable]:not([${WHOLE_BLOCK_INPUT_ATTR}]):not(.selection-overlay)`),
|
|
33
34
|
foreignDragHitTest: dragHitTest && ((cx, cy) => dragHitTest(wrapper, cx, cy)),
|
|
34
35
|
caretTargetAtPoint: caretTarget && ((cx, cy) => caretTarget(wrapper, cx, cy))
|
|
35
36
|
};
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* order against the other endpoint, never the anchor/focus role, which `normalize` reorders later.
|
|
7
7
|
*/
|
|
8
8
|
import { displayLength, snapToScalarBoundary } from '../core/lines';
|
|
9
|
-
import { isBlockNode, nodeAt } from '../tree-operations/node-
|
|
9
|
+
import { isBlockNode, nodeAt } from '../tree-operations/node-primitives';
|
|
10
10
|
import { isWholeBlockUnit } from '../schema/whole-block-unit';
|
|
11
11
|
import { comparePaths } from './path-math';
|
|
12
12
|
import { isWholeBlockEndpoint } from './primitives';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { makeBlockNode, metadataOf } from '../core/nodes';
|
|
5
5
|
import { cloneMetadata } from '../tree-operations/clone';
|
|
6
|
-
import { isBlockNode, nodeAt } from '../tree-operations/node-
|
|
6
|
+
import { isBlockNode, nodeAt } from '../tree-operations/node-primitives';
|
|
7
7
|
import { walkBetween, normalize, charOffsetOf, cellIndexOf } from './primitives';
|
|
8
8
|
import { snapCrossBlockTableEndpoints } from './table-endpoint-snap';
|
|
9
9
|
import { isStrictAncestorOf, pathHasPrefix, pathsEqual, sharedPrefixLength } from './path-math';
|
|
@@ -39,6 +39,11 @@ export function collectCrossBlockText(doc, anchor, focus) {
|
|
|
39
39
|
}
|
|
40
40
|
const startRaw = isBlockNode(startNode) ? startNode.raw : '';
|
|
41
41
|
const endRaw = isBlockNode(endNode) ? endNode.raw : '';
|
|
42
|
+
// One leaf taken whole (`SelectionState.wholeUnitPath`): the only same-path prose pair the
|
|
43
|
+
// state stores, and the head/tail split below would emit its bytes twice.
|
|
44
|
+
if (pathsEqual(start.path, end.path) && !start.cellCoordinate) {
|
|
45
|
+
return startRaw.slice(start.offset, end.offset);
|
|
46
|
+
}
|
|
42
47
|
let effectiveStartPath = start.path;
|
|
43
48
|
let chromeStart = null;
|
|
44
49
|
let startTail = '';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block a range holds whole, for the gestures that insert over it. Such a range has no seam
|
|
3
|
+
* inside it to merge at: the block leaves the tree entirely, so bytes that follow must land in
|
|
4
|
+
* its SLOT rather than at a caret in whatever took the slot.
|
|
5
|
+
*/
|
|
6
|
+
import type { DocumentView } from '../core/node-views';
|
|
7
|
+
import type { SelectionPoint } from './primitives';
|
|
8
|
+
/** The block both endpoints address and jointly span, or null. Cell coordinates index a grid
|
|
9
|
+
* rather than characters: the table's own coverage rule is `cross-block/paste.ts`'s. */
|
|
10
|
+
export declare function blockCoveredWhole(doc: DocumentView, anchor: SelectionPoint | null, focus: SelectionPoint | null): number[] | null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block a range holds whole, for the gestures that insert over it. Such a range has no seam
|
|
3
|
+
* inside it to merge at: the block leaves the tree entirely, so bytes that follow must land in
|
|
4
|
+
* its SLOT rather than at a caret in whatever took the slot.
|
|
5
|
+
*/
|
|
6
|
+
import { displayLength } from '../core/lines';
|
|
7
|
+
import { isBlockNode, nodeAt } from '../tree-operations/node-primitives';
|
|
8
|
+
import { pathsEqual } from './path-math';
|
|
9
|
+
/** The block both endpoints address and jointly span, or null. Cell coordinates index a grid
|
|
10
|
+
* rather than characters: the table's own coverage rule is `cross-block/paste.ts`'s. */
|
|
11
|
+
export function blockCoveredWhole(doc, anchor, focus) {
|
|
12
|
+
if (!anchor || !focus)
|
|
13
|
+
return null;
|
|
14
|
+
if (!pathsEqual(anchor.path, focus.path))
|
|
15
|
+
return null;
|
|
16
|
+
if (anchor.cellCoordinate || focus.cellCoordinate)
|
|
17
|
+
return null;
|
|
18
|
+
const node = nodeAt(doc, anchor.path);
|
|
19
|
+
if (!node || !isBlockNode(node))
|
|
20
|
+
return null;
|
|
21
|
+
const low = Math.min(anchor.offset, focus.offset);
|
|
22
|
+
const high = Math.max(anchor.offset, focus.offset);
|
|
23
|
+
return low === 0 && high >= displayLength(node.raw) ? anchor.path.slice() : null;
|
|
24
|
+
}
|
|
@@ -71,6 +71,9 @@ export interface CrossBlockHandlers {
|
|
|
71
71
|
* programmatic insertion: no gesture to consume, so `replacement` carries the payload. */
|
|
72
72
|
handlePaste(e: ClipboardEvent | null, replacement?: string): Promise<boolean>;
|
|
73
73
|
handleBeforeInput(e: InputEvent): Promise<boolean>;
|
|
74
|
+
/** Type-replace from a door that carries no InputEvent: the editor root, where a range over a
|
|
75
|
+
* block with no character position leaves no editing surface for `beforeinput` to fire on. */
|
|
76
|
+
insertText(text: string): Promise<boolean>;
|
|
74
77
|
handleCompositionStart(): boolean;
|
|
75
78
|
/** Cross-block range delete for Cut handlers, after they synchronously wrote the clipboard. */
|
|
76
79
|
performCrossBlockDeleteFromEvent(): Promise<void>;
|
|
@@ -28,6 +28,14 @@ export function createCrossBlockHandlers(ctx) {
|
|
|
28
28
|
// (each editable surface, the editor root) inherits them. Keydown gates its own destructive
|
|
29
29
|
// branches, since it also carries navigation, which stays live.
|
|
30
30
|
const reading = () => isReadingMode(ctx.getPresentationMode);
|
|
31
|
+
const insertText = async (text) => {
|
|
32
|
+
if (reading())
|
|
33
|
+
return true;
|
|
34
|
+
if (!ctx.selection.isCrossBlock)
|
|
35
|
+
return false;
|
|
36
|
+
await handleCrossBlockTypeReplace(ctx, mutationCtx, text);
|
|
37
|
+
return true;
|
|
38
|
+
};
|
|
31
39
|
return {
|
|
32
40
|
handleKeyDown: keydown.handleKeyDown,
|
|
33
41
|
handleCompositionStart: keydown.handleCompositionStart,
|
|
@@ -44,8 +52,12 @@ export function createCrossBlockHandlers(ctx) {
|
|
|
44
52
|
e.preventDefault();
|
|
45
53
|
return true;
|
|
46
54
|
}
|
|
47
|
-
|
|
55
|
+
if (!ctx.selection.isCrossBlock || e.inputType !== 'insertText')
|
|
56
|
+
return false;
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
return insertText(e.data ?? '');
|
|
48
59
|
},
|
|
60
|
+
insertText,
|
|
49
61
|
performCrossBlockDeleteFromEvent: async () => {
|
|
50
62
|
// Reached from cut handlers after the clipboard write; declining the delete
|
|
51
63
|
// degrades a reading-mode cut to a copy.
|
|
@@ -10,7 +10,7 @@ import type { DocumentView } from '../../core/node-views';
|
|
|
10
10
|
import type { InlineMarkKind } from '../../schema/inline-construct-policy';
|
|
11
11
|
import type { PresentationMode } from '../../presentation-mode';
|
|
12
12
|
import type { GrammarView } from '../../schema/block-openers';
|
|
13
|
-
import { type NodeParent } from '../../tree-operations/node-
|
|
13
|
+
import { type NodeParent } from '../../tree-operations/node-primitives';
|
|
14
14
|
import type { SharingState } from '../../tree-operations/sharing';
|
|
15
15
|
import { type SelectionPoint } from '../primitives';
|
|
16
16
|
export interface CrossBlockFormatWrite {
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
* through the single-block seam, so its arms, candidates and mode verification are the same ones.
|
|
7
7
|
* A grid joins by its cells, which the range covers whole.
|
|
8
8
|
*/
|
|
9
|
-
import { activeInlineFormats, inlineFormatsCovering, isInlineFormatActive, isInlineFormatActiveAfter, toggleInlineFormat } from '../../core/inline/format-toggle';
|
|
9
|
+
import { activeInlineFormats, inlineFormatsCovering, isInlineFormatActive, isInlineFormatActiveAfter, toggleInlineFormat, withoutBoundaryWhitespace } from '../../core/inline/format-toggle';
|
|
10
10
|
import { getContentRange } from '../../core/inline';
|
|
11
11
|
import { ownTrailingLineEnding, trimTrailingLineEnding } from '../../core/lines';
|
|
12
12
|
import { tryGetBlockKindDescriptor } from '../../schema/block-kind-descriptor';
|
|
13
|
-
import { normalizeBodyWrite, writeOwnRaw } from '../../tree-operations/node-
|
|
14
|
-
import { ensureUnsharedPath
|
|
13
|
+
import { normalizeBodyWrite, writeOwnRaw } from '../../tree-operations/node-primitives';
|
|
14
|
+
import { ensureUnsharedPath } from '../../tree-operations/unshare';
|
|
15
|
+
import { rebuildUnsharedChain } from '../../tree-operations/chain-rebuild';
|
|
15
16
|
import { comparePaths } from '../path-math';
|
|
16
17
|
import { charOffsetOf } from '../primitives';
|
|
17
18
|
import { coveredGridCells, gridEndpointCellIndex } from '../table-endpoint-snap';
|
|
@@ -170,18 +171,6 @@ function contentSpan(node, path, from, to) {
|
|
|
170
171
|
const selection = withoutBoundaryWhitespace(display, clampToContent(from ?? content.start, content), clampToContent(to ?? content.end, content));
|
|
171
172
|
return selection && { path, edit: { display, content, selection } };
|
|
172
173
|
}
|
|
173
|
-
/** The span minus its boundary whitespace, null once nothing is left. Markdown opens and closes a
|
|
174
|
-
* run against a word, never a space, so an untrimmed edge yields delimiters that form no
|
|
175
|
-
* construct — which the modes that paint them write anyway (`wrapCandidates` is unverified there). */
|
|
176
|
-
function withoutBoundaryWhitespace(display, from, to) {
|
|
177
|
-
let start = from;
|
|
178
|
-
let end = to;
|
|
179
|
-
while (start < end && /\s/.test(display[start]))
|
|
180
|
-
start++;
|
|
181
|
-
while (end > start && /\s/.test(display[end - 1]))
|
|
182
|
-
end--;
|
|
183
|
-
return start === end ? null : { start, end };
|
|
184
|
-
}
|
|
185
174
|
const clampToContent = (offset, content) => Math.min(Math.max(offset, content.start), content.end);
|
|
186
175
|
/**
|
|
187
176
|
* Whether the span actually changed sides. The single-block seam decides its own arm from the
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { ownTrailingLineEnding } from '../../core/lines';
|
|
8
8
|
import { docPathFrom } from '../../cursor/coordinate-spaces';
|
|
9
9
|
import { inlineMarkForCommand } from '../../schema/inline-construct-policy';
|
|
10
|
-
import { blockNodeAt } from '../../tree-operations/node-
|
|
10
|
+
import { blockNodeAt } from '../../tree-operations/node-primitives';
|
|
11
11
|
import { comparePaths } from '../path-math';
|
|
12
12
|
import { restoreSelection } from '../selection-restore';
|
|
13
13
|
import { applyCrossBlockFormat, crossBlockActiveFormats, planCrossBlockFormat } from './format-range';
|
|
@@ -4,16 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { CURSOR_START } from '../../block-component';
|
|
6
6
|
import { performCrossBlockDelete, performCrossBlockDeleteSync } from './ops';
|
|
7
|
-
import { isBlockNode } from '../../tree-operations/node-
|
|
7
|
+
import { isBlockNode } from '../../tree-operations/node-primitives';
|
|
8
8
|
import { isReadingMode } from '../../presentation-mode';
|
|
9
9
|
import { eventToChord } from '../../schema/keybindings';
|
|
10
10
|
import { dispatchKeyCommand } from '../../schema/block-commands';
|
|
11
11
|
import { collapseCrossBlock, extendFocusToNextBlock, extendFocusToPreviousBlock, extendFocusToDocEdge, selectWholeDocument, scrollFocusBlockIntoView } from '../keyboard-extend';
|
|
12
12
|
import { pathsEqual } from '../path-math';
|
|
13
13
|
import { intraTableRectExtension } from '../table-rect-extend';
|
|
14
|
-
import {
|
|
15
|
-
import { asDomTextOffset } from '../../cursor/coordinate-spaces';
|
|
16
|
-
import { createRangeFromOffsets } from '../../cursor/content-offsets';
|
|
14
|
+
import { applySurfaceContentRange } from '../native-bridge';
|
|
17
15
|
export function createCrossBlockKeydown(ctx, mutCtx) {
|
|
18
16
|
return {
|
|
19
17
|
handleKeyDown: (e) => handleKeyDown(ctx, mutCtx, e),
|
|
@@ -168,7 +166,7 @@ async function handleCrossBlockEntry(ctx, e) {
|
|
|
168
166
|
e.preventDefault();
|
|
169
167
|
selection.incrementSelectAllCount();
|
|
170
168
|
if (selection.selectAllCount === 1) {
|
|
171
|
-
|
|
169
|
+
applySurfaceContentRange(el);
|
|
172
170
|
return true;
|
|
173
171
|
}
|
|
174
172
|
selectWholeDocument(selection, getDoc(), ctx.getBlockElByPath);
|
|
@@ -261,30 +259,6 @@ function kindOfPath(path, doc) {
|
|
|
261
259
|
// The root's 'document' kind is outside AnyBlockKind; dispatch treats it as an unknown kind.
|
|
262
260
|
return isBlockNode(node) ? node.kind : node.kind;
|
|
263
261
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Select the block's content for the first Ctrl+A press. With an ambient marker (a list item's
|
|
266
|
-
* `- `), anchor after it so type-replace doesn't corrupt the contenteditable="false" island.
|
|
267
|
-
*/
|
|
268
|
-
function selectFirstPressContent(el) {
|
|
269
|
-
const ambient = ambientSpanOf(el);
|
|
270
|
-
const ambientLen = ambient?.textContent?.length ?? 0;
|
|
271
|
-
const textLen = el.textContent?.length ?? 0;
|
|
272
|
-
if (ambient && textLen > ambientLen) {
|
|
273
|
-
if (!placeCaretAfterAmbientSpan(el))
|
|
274
|
-
return;
|
|
275
|
-
// textLen counts the full textContent (marker included) — a DomTextOffset by construction.
|
|
276
|
-
const endRange = createRangeFromOffsets(el, asDomTextOffset(textLen), asDomTextOffset(textLen));
|
|
277
|
-
if (endRange) {
|
|
278
|
-
window.getSelection()?.extend(endRange.endContainer, endRange.endOffset);
|
|
279
|
-
}
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
const range = document.createRange();
|
|
283
|
-
range.selectNodeContents(el);
|
|
284
|
-
const sel = window.getSelection();
|
|
285
|
-
sel?.removeAllRanges();
|
|
286
|
-
sel?.addRange(range);
|
|
287
|
-
}
|
|
288
262
|
/**
|
|
289
263
|
* Bring the active (focus-side) endpoint into view after an extend. A cell-coordinate focus
|
|
290
264
|
* addresses the table block by cell index, so reveal the deep cell path to mount the off-window
|
|
@@ -9,7 +9,7 @@ import { deleteSnapshot } from '../primitives';
|
|
|
9
9
|
import { focusCollapsedCaret } from '../native-bridge';
|
|
10
10
|
import { rangeDelete } from '../range-delete';
|
|
11
11
|
import { trackChildIds } from '../../tree-operations/structural-change';
|
|
12
|
-
import { isBlockNode, nodeAt } from '../../tree-operations/node-
|
|
12
|
+
import { isBlockNode, nodeAt } from '../../tree-operations/node-primitives';
|
|
13
13
|
import { pathsEqual } from '../path-math';
|
|
14
14
|
import { docPathFrom } from '../../cursor/coordinate-spaces';
|
|
15
15
|
import { getStateForNode } from '../../reactivity/state-registry';
|
|
@@ -9,13 +9,13 @@ import { normalizeLineEndings } from '../../core/lines';
|
|
|
9
9
|
import { performCrossBlockDelete } from './ops';
|
|
10
10
|
import { charOffsetOf } from '../primitives';
|
|
11
11
|
import { focusCollapsedCaret } from '../native-bridge';
|
|
12
|
+
import { blockCoveredWhole } from '../covered-block';
|
|
12
13
|
import { pasteDispatch } from '../../tree-operations/paste/dispatch';
|
|
13
14
|
import { applyPasteTransforms } from '../../tree-operations/paste/paste-transforms';
|
|
14
|
-
import {
|
|
15
|
-
import { blockNodeAt, isBlockNode, nodeAt } from '../../tree-operations/node-ops';
|
|
15
|
+
import { blockNodeAt, isBlockNode, nodeAt } from '../../tree-operations/node-primitives';
|
|
16
16
|
import { pathsEqual } from '../path-math';
|
|
17
17
|
import { replaceBlockAtParent } from '../../tree-operations/paste/replace-block-at-parent';
|
|
18
|
-
import {
|
|
18
|
+
import { parseReplacement } from '../../tree-operations/paste/replacement-parse';
|
|
19
19
|
import { emitClipboardError } from '../../editor-events';
|
|
20
20
|
export async function handleCrossBlockPaste(ctx, mutCtx, e, replacement) {
|
|
21
21
|
if (!ctx.selection.isCrossBlock)
|
|
@@ -32,10 +32,12 @@ export async function handleCrossBlockPaste(ctx, mutCtx, e, replacement) {
|
|
|
32
32
|
if (!pasted)
|
|
33
33
|
return true;
|
|
34
34
|
const doc = ctx.getDoc();
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// The range holds one block whole, in either addressing: replace the block at its parent
|
|
36
|
+
// position, single undo. A sub-rectangle inside a table only clears cells, leaving the table.
|
|
37
|
+
const covered = wholeTablePath(ctx.selection, doc) ??
|
|
38
|
+
blockCoveredWhole(doc, ctx.selection.anchor, ctx.selection.focus);
|
|
39
|
+
if (covered) {
|
|
40
|
+
await replaceCoveredBlockWithPaste(ctx, mutCtx, pasted, covered);
|
|
39
41
|
return true;
|
|
40
42
|
}
|
|
41
43
|
// Read before the delete collapses the selection: the only coordinate an error report on the
|
|
@@ -96,60 +98,54 @@ async function landCaretAfterPaste(ctx, caretPath, inlineCaretOffset) {
|
|
|
96
98
|
ctx.getBlockElByPath(caretPath)?.focus();
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
|
-
// ──
|
|
100
|
-
|
|
101
|
+
// ── Covered-block paste ────────────────────────────────────────────────────
|
|
102
|
+
/** The table a cell rectangle covers whole (Ctrl+A's 2nd press inside a cell), or null. */
|
|
103
|
+
function wholeTablePath(selection, doc) {
|
|
101
104
|
const anchor = selection.anchor;
|
|
102
105
|
const focus = selection.focus;
|
|
103
106
|
if (!anchor || !focus)
|
|
104
|
-
return
|
|
107
|
+
return null;
|
|
105
108
|
if (!pathsEqual(anchor.path, focus.path))
|
|
106
|
-
return
|
|
109
|
+
return null;
|
|
107
110
|
const node = nodeAt(doc, anchor.path);
|
|
108
111
|
if (!node || !isBlockNode(node) || node.kind !== 'table')
|
|
109
|
-
return
|
|
112
|
+
return null;
|
|
110
113
|
const cellCount = tableCellCount(node);
|
|
111
114
|
if (cellCount === 0)
|
|
112
|
-
return
|
|
115
|
+
return null;
|
|
113
116
|
// Same-path intra-table selection: cell offsets are context-established, so read directly.
|
|
114
117
|
const lo = Math.min(anchor.offset, focus.offset);
|
|
115
118
|
const hi = Math.max(anchor.offset, focus.offset);
|
|
116
|
-
return lo === 0 && hi === cellCount - 1;
|
|
119
|
+
return lo === 0 && hi === cellCount - 1 ? anchor.path.slice() : null;
|
|
117
120
|
}
|
|
118
121
|
/**
|
|
119
|
-
* Replace the
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
+
* Replace the covered block with the pasted content at its parent position. Routes through
|
|
123
|
+
* replaceBlockAtParent so the splice lands at the doc/enclosing-container scope rather than the
|
|
124
|
+
* row-level blockEdit TableRowBlock propagates. One snapshot covers the replace.
|
|
122
125
|
*/
|
|
123
|
-
async function
|
|
124
|
-
const tablePath = ctx.selection.anchor.path;
|
|
126
|
+
async function replaceCoveredBlockWithPaste(ctx, mutCtx, pasted, blockPath) {
|
|
125
127
|
const doc = ctx.getDoc();
|
|
128
|
+
const covered = blockNodeAt(doc, blockPath);
|
|
129
|
+
if (!covered)
|
|
130
|
+
return;
|
|
126
131
|
// This route never reaches pasteDispatch, so the paste transforms and the instance grammar
|
|
127
132
|
// ride here too; both rules live in the helper, applied at both sites.
|
|
128
|
-
const parsed =
|
|
129
|
-
|
|
130
|
-
scope: 'fragment'
|
|
131
|
-
});
|
|
132
|
-
if (parsed.children.length === 0)
|
|
133
|
-
return;
|
|
134
|
-
const tableNode = blockNodeAt(doc, tablePath);
|
|
135
|
-
if (!tableNode)
|
|
133
|
+
const parsed = parseReplacement(covered, applyPasteTransforms(pasted, ctx.activePlugins), ctx.grammar);
|
|
134
|
+
if (!parsed)
|
|
136
135
|
return;
|
|
137
|
-
const replacement = normalizeReplacementTrivia(tableNode, parsed.children);
|
|
138
|
-
for (const node of replacement)
|
|
139
|
-
ensureEditableContainers(node);
|
|
140
136
|
mutCtx.pushUndoSnapshot();
|
|
141
137
|
ctx.selection.collapse();
|
|
142
138
|
await replaceBlockAtParent({
|
|
143
139
|
doc,
|
|
144
|
-
blockPath
|
|
145
|
-
replacement,
|
|
140
|
+
blockPath,
|
|
141
|
+
replacement: parsed.replacement,
|
|
146
142
|
controller: ctx.pasteCoordinator,
|
|
147
143
|
undoEntry: 'join',
|
|
148
|
-
focusReplacementIndex: replacement.length - 1,
|
|
144
|
+
focusReplacementIndex: parsed.replacement.length - 1,
|
|
149
145
|
focusOffset: CURSOR_END,
|
|
150
|
-
source: 'cross-block-
|
|
146
|
+
source: 'cross-block-covered-block',
|
|
151
147
|
...(ctx.grammar ? { grammar: ctx.grammar } : {}),
|
|
152
|
-
// Nothing is reattached behind the clipboard here — the
|
|
148
|
+
// Nothing is reattached behind the clipboard here — the block's whole slot is the target —
|
|
153
149
|
// so the trailing blank rides in unfiltered (`paste/dispatch.ts` states the rule).
|
|
154
150
|
trailingSeparator: parsed.suffix
|
|
155
151
|
});
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* Delete the range, splice the character into the surviving leaf's raw, re-parse so a marker at
|
|
4
4
|
* offset 0 re-derives the kind (parity with the single-block type path). Routed through
|
|
5
5
|
* commitMultiScope so a kind change mints a fresh node, ids/refs stay synced, and the op is
|
|
6
|
-
* `updateContent`.
|
|
6
|
+
* `updateContent`. A range holding its block whole has no surviving leaf and takes the
|
|
7
|
+
* replace arm at the foot of the file instead.
|
|
7
8
|
*/
|
|
8
9
|
import type { CrossBlockDispatchContext } from './dispatch';
|
|
9
10
|
import type { CrossBlockMutationContext } from './ops';
|
|
10
|
-
export declare function handleCrossBlockTypeReplace(ctx: CrossBlockDispatchContext, mutCtx: CrossBlockMutationContext,
|
|
11
|
+
export declare function handleCrossBlockTypeReplace(ctx: CrossBlockDispatchContext, mutCtx: CrossBlockMutationContext, typed: string): Promise<void>;
|
|
@@ -3,37 +3,49 @@
|
|
|
3
3
|
* Delete the range, splice the character into the surviving leaf's raw, re-parse so a marker at
|
|
4
4
|
* offset 0 re-derives the kind (parity with the single-block type path). Routed through
|
|
5
5
|
* commitMultiScope so a kind change mints a fresh node, ids/refs stay synced, and the op is
|
|
6
|
-
* `updateContent`.
|
|
6
|
+
* `updateContent`. A range holding its block whole has no surviving leaf and takes the
|
|
7
|
+
* replace arm at the foot of the file instead.
|
|
7
8
|
*/
|
|
8
9
|
import { performCrossBlockDelete } from './ops';
|
|
9
10
|
import { charOffsetOf } from '../primitives';
|
|
10
|
-
import {
|
|
11
|
+
import { blockCoveredWhole } from '../covered-block';
|
|
12
|
+
import { CURSOR_END } from '../../block-component';
|
|
13
|
+
import { replaceBlockAtParent } from '../../tree-operations/paste/replace-block-at-parent';
|
|
14
|
+
import { parseReplacement } from '../../tree-operations/paste/replacement-parse';
|
|
15
|
+
import { blockNodeAt, normalizeBodyWrite, writeOwnRaw } from '../../tree-operations/node-primitives';
|
|
16
|
+
import { updateNodeContent, settledCaretTarget } from '../../tree-operations/content-write';
|
|
11
17
|
import { focusCollapsedCaret } from '../native-bridge';
|
|
12
|
-
import { ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedPath
|
|
18
|
+
import { ensureUnsharedChild, ensureUnsharedNode, ensureUnsharedPath } from '../../tree-operations/unshare';
|
|
19
|
+
import { rebuildUnsharedChain } from '../../tree-operations/chain-rebuild';
|
|
13
20
|
import { stampStructuralChange } from '../../tree-operations/structural-change';
|
|
14
21
|
import { getStateForNode } from '../../reactivity/state-registry';
|
|
15
22
|
import { docPathFrom } from '../../cursor/coordinate-spaces';
|
|
16
23
|
import { devWarn } from '../../dev-warn';
|
|
17
|
-
export async function handleCrossBlockTypeReplace(ctx, mutCtx,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
export async function handleCrossBlockTypeReplace(ctx, mutCtx, typed) {
|
|
25
|
+
// A range holding its block whole leaves no leaf to splice into, so the character replaces the
|
|
26
|
+
// block in its own slot. An empty insertion has nothing to stand there and takes the delete.
|
|
27
|
+
const covered = typed
|
|
28
|
+
? blockCoveredWhole(ctx.getDoc(), ctx.selection.anchor, ctx.selection.focus)
|
|
29
|
+
: null;
|
|
30
|
+
if (covered) {
|
|
31
|
+
await replaceCoveredBlockWithText(ctx, mutCtx, typed, covered);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
22
34
|
const caret = await performCrossBlockDelete(mutCtx, { skipCaretRestore: true });
|
|
23
35
|
if (!caret)
|
|
24
|
-
return
|
|
36
|
+
return;
|
|
25
37
|
// All caret placements below target caret.path's top-level block; mount it once here so each
|
|
26
38
|
// (the post-tick landing included) finds a live element.
|
|
27
39
|
await ctx.revealPath(caret.path);
|
|
28
40
|
if (!typed) {
|
|
29
41
|
focusCollapsedCaret(ctx.getBlockElByPath, caret);
|
|
30
|
-
return
|
|
42
|
+
return;
|
|
31
43
|
}
|
|
32
44
|
const doc = ctx.getDoc();
|
|
33
45
|
const targetNode = blockNodeAt(doc, caret.path);
|
|
34
46
|
if (!targetNode) {
|
|
35
47
|
focusCollapsedCaret(ctx.getBlockElByPath, caret);
|
|
36
|
-
return
|
|
48
|
+
return;
|
|
37
49
|
}
|
|
38
50
|
// `updateContent`, not `input`: symmetric with the single-block path's KIND-CHANGING branch
|
|
39
51
|
// (block-edit.ts updateBlockContent). Only that path's kind-stable branch emits the debounced
|
|
@@ -42,7 +54,7 @@ export async function handleCrossBlockTypeReplace(ctx, mutCtx, e) {
|
|
|
42
54
|
const scope = resolveTypedCharScope(ctx, caret.path);
|
|
43
55
|
if (!scope) {
|
|
44
56
|
focusCollapsedCaret(ctx.getBlockElByPath, caret);
|
|
45
|
-
return
|
|
57
|
+
return;
|
|
46
58
|
}
|
|
47
59
|
// resolveTypedCharScope returns the leaf's IMMEDIATE parent: every mounted container registers
|
|
48
60
|
// a BlockListState, and the document root stands in for a top-level leaf. The guard below is
|
|
@@ -97,7 +109,34 @@ export async function handleCrossBlockTypeReplace(ctx, mutCtx, e) {
|
|
|
97
109
|
focusCollapsedCaret(ctx.getBlockElByPath, { path, offset: target.offset });
|
|
98
110
|
}
|
|
99
111
|
});
|
|
100
|
-
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Replace the covered block with the parse of the typed character, at the block's parent
|
|
115
|
+
* position: the same door the covered-block paste takes, so both gestures splice at one scope
|
|
116
|
+
* and land one undo entry. Re-parsed rather than spliced, so a marker typed over the block
|
|
117
|
+
* derives its kind exactly as the single-block type path does.
|
|
118
|
+
*/
|
|
119
|
+
async function replaceCoveredBlockWithText(ctx, mutCtx, typed, blockPath) {
|
|
120
|
+
const doc = ctx.getDoc();
|
|
121
|
+
const covered = blockNodeAt(doc, blockPath);
|
|
122
|
+
if (!covered)
|
|
123
|
+
return;
|
|
124
|
+
const parsed = parseReplacement(covered, typed, ctx.grammar);
|
|
125
|
+
if (!parsed)
|
|
126
|
+
return;
|
|
127
|
+
mutCtx.pushUndoSnapshot();
|
|
128
|
+
ctx.selection.collapse();
|
|
129
|
+
await replaceBlockAtParent({
|
|
130
|
+
doc,
|
|
131
|
+
blockPath,
|
|
132
|
+
replacement: parsed.replacement,
|
|
133
|
+
controller: ctx.pasteCoordinator,
|
|
134
|
+
undoEntry: 'join',
|
|
135
|
+
focusReplacementIndex: parsed.replacement.length - 1,
|
|
136
|
+
focusOffset: CURSOR_END,
|
|
137
|
+
source: 'cross-block-covered-block',
|
|
138
|
+
...(ctx.grammar ? { grammar: ctx.grammar } : {})
|
|
139
|
+
});
|
|
101
140
|
}
|
|
102
141
|
/** The scope's children, re-read after the commit: the ceremony replaces the node it published. */
|
|
103
142
|
function scopeChildrenOf(ctx, scopePath) {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { type BlockComponent } from '../block-component';
|
|
9
9
|
import { type GapStopScope } from './gap-caret';
|
|
10
|
+
import type { SelectionEndpoint } from './primitives';
|
|
10
11
|
export interface DeadSpaceCaretDeps {
|
|
11
12
|
getBlockComponent(path: number[]): BlockComponent | null;
|
|
12
13
|
/**
|
|
@@ -35,5 +36,17 @@ export interface DeadSpaceCaret {
|
|
|
35
36
|
* was claimed; a point past a windowed-out tail claims it and lands after the reveal.
|
|
36
37
|
*/
|
|
37
38
|
placeAtPoint(root: HTMLElement, x: number, y: number): boolean;
|
|
39
|
+
/** Whether a press target is the editor's dead space (the root, or a block list inside it). */
|
|
40
|
+
isDeadSpaceTarget(root: HTMLElement, target: EventTarget | null): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* The endpoint a drag STARTING in dead space (or on a block's rendered face) anchors at: the
|
|
43
|
+
* landing a click there would take, without taking it. A character surface anchors at that
|
|
44
|
+
* offset; a rendered leaf (an equation) anchors as a WHOLE block, so the range takes it entire
|
|
45
|
+
* whichever way the drag goes. A coordinate-addressed kind (a table) anchors nothing.
|
|
46
|
+
*/
|
|
47
|
+
anchorAtPoint(root: HTMLElement, x: number, y: number): SelectionEndpoint | null;
|
|
48
|
+
/** The path of the block a press names, the point clamped into the nearest band: what the
|
|
49
|
+
* click ladder selects in for a press on the margin or a block's own box. */
|
|
50
|
+
blockPathNearPoint(root: HTMLElement, x: number, y: number): number[] | null;
|
|
38
51
|
}
|
|
39
52
|
export declare function createDeadSpaceCaret(deps: DeadSpaceCaretDeps): DeadSpaceCaret;
|