@voithos-labs/aragonite 0.10.3 → 0.10.7
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 +2 -2
- package/dist/a11y-strings.d.ts +12 -5
- package/dist/a11y-strings.js +61 -5
- package/dist/action-contracts.d.ts +242 -155
- package/dist/action-contracts.js +2 -2
- package/dist/active-editor.d.ts +3 -3
- package/dist/active-editor.js +8 -9
- package/dist/ambient/ambient-dom.d.ts +2 -5
- package/dist/ambient/ambient-dom.js +16 -62
- package/dist/assert.js +6 -5
- package/dist/block-component.d.ts +135 -109
- package/dist/block-component.js +30 -22
- package/dist/block-id.d.ts +6 -10
- package/dist/block-id.js +25 -14
- package/dist/bounded-memo.d.ts +5 -5
- package/dist/bounded-memo.js +6 -6
- package/dist/components/BlockDragHandle.svelte +13 -20
- package/dist/components/BlockHost.svelte +56 -134
- package/dist/components/BlockList.svelte +22 -19
- package/dist/components/DecorationOverlay.svelte +9 -10
- package/dist/components/DecorationOverlay.svelte.d.ts +2 -2
- package/dist/components/Editor.svelte +619 -1089
- package/dist/components/Editor.svelte.d.ts +9 -20
- package/dist/components/GapCaret.svelte +26 -59
- package/dist/components/SelectionOverlay.svelte +36 -67
- package/dist/components/SelectionOverlay.svelte.d.ts +6 -4
- package/dist/components/TailInsert.svelte +8 -55
- package/dist/components/TailInsert.svelte.d.ts +4 -6
- package/dist/components/block-content-selector.d.ts +11 -11
- package/dist/components/block-content-selector.js +11 -11
- package/dist/components/block-el-lookup.d.ts +6 -0
- package/dist/components/block-el-lookup.js +27 -0
- package/dist/components/blocks/BlockquoteBlock.svelte +3 -3
- package/dist/components/blocks/ThematicBreakBlock.svelte +22 -63
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +0 -2
- package/dist/components/blocks/code/CodeBlock.svelte +255 -276
- package/dist/components/blocks/code/CodeBlock.svelte.d.ts +2 -3
- package/dist/components/blocks/code/CodeBlockRail.svelte +103 -100
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +9 -4
- package/dist/components/blocks/code/code-beforeinput.d.ts +1 -1
- package/dist/components/blocks/code/code-bootstrap.d.ts +3 -5
- package/dist/components/blocks/code/code-bootstrap.js +35 -28
- package/dist/components/blocks/code/code-context-actions.js +5 -4
- package/dist/components/blocks/code/code-enter.js +2 -2
- package/dist/components/blocks/code/code-fence-boundary.d.ts +29 -46
- package/dist/components/blocks/code/code-fence-boundary.js +40 -66
- package/dist/components/blocks/code/code-fence-exit.d.ts +6 -11
- package/dist/components/blocks/code/code-fence-exit.js +24 -25
- package/dist/components/blocks/code/code-indent.d.ts +2 -2
- package/dist/components/blocks/code/code-indent.js +4 -7
- package/dist/components/blocks/code/code-languages.d.ts +16 -12
- package/dist/components/blocks/code/code-languages.js +58 -32
- package/dist/components/blocks/code/code-paste-surface.d.ts +3 -2
- package/dist/components/blocks/code/code-paste-surface.js +9 -18
- package/dist/components/blocks/code/code-renderer.d.ts +21 -5
- package/dist/components/blocks/code/code-renderer.js +90 -125
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +5 -8
- package/dist/components/blocks/directive/activate-directives.d.ts +4 -4
- package/dist/components/blocks/directive/activate-directives.js +18 -13
- package/dist/components/blocks/editable-leaf.d.ts +50 -74
- package/dist/components/blocks/editable-leaf.js +144 -202
- package/dist/components/blocks/editable-surface.d.ts +96 -100
- package/dist/components/blocks/editable-surface.js +101 -109
- package/dist/components/blocks/list/ListBlock.svelte +46 -90
- package/dist/components/blocks/list/ListItemBlock.svelte +99 -144
- package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +3 -0
- package/dist/components/blocks/list/task-checkbox.d.ts +5 -2
- package/dist/components/blocks/list/task-checkbox.js +12 -7
- package/dist/components/blocks/plain-text-backend.d.ts +4 -20
- package/dist/components/blocks/plain-text-backend.js +9 -43
- package/dist/components/blocks/surface-wiring.svelte.d.ts +8 -9
- package/dist/components/blocks/surface-wiring.svelte.js +15 -34
- package/dist/components/blocks/table/TableActionMenu.svelte +19 -18
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +3 -1
- package/dist/components/blocks/table/TableBlock.svelte +100 -165
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +3 -3
- package/dist/components/blocks/table/TableCellBlock.svelte +307 -385
- package/dist/components/blocks/table/TableCellBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableRowBlock.svelte +60 -104
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +4 -9
- package/dist/components/blocks/table/cell-keydown-plan.js +8 -12
- package/dist/components/blocks/table/cell-pointer.d.ts +18 -23
- package/dist/components/blocks/table/cell-pointer.js +49 -71
- package/dist/components/blocks/table/cell-render.d.ts +24 -29
- package/dist/components/blocks/table/cell-render.js +29 -31
- package/dist/components/blocks/table/selected-cells.d.ts +7 -7
- package/dist/components/blocks/table/selected-cells.js +9 -13
- package/dist/components/blocks/table/table-caret-at-point.d.ts +4 -4
- package/dist/components/blocks/table/table-caret-at-point.js +5 -5
- package/dist/components/blocks/table/table-cell-paste.d.ts +5 -11
- package/dist/components/blocks/table/table-cell-paste.js +27 -43
- package/dist/components/blocks/table/table-drag-hit-test.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +4 -8
- package/dist/components/blocks/table/table-menu-model.js +6 -10
- package/dist/components/blocks/table/table-navigation.js +2 -2
- package/dist/components/blocks/text/TextEditableBlock.svelte +392 -310
- package/dist/components/blocks/text/TextEditableBlock.svelte.d.ts +2 -1
- package/dist/components/blocks/text/auto-pair-record.d.ts +23 -0
- package/dist/components/blocks/text/auto-pair-record.js +51 -0
- package/dist/components/blocks/text/click-snap-guard.d.ts +9 -4
- package/dist/components/blocks/text/click-snap-guard.js +16 -5
- package/dist/components/blocks/text/composition-seat.d.ts +17 -14
- package/dist/components/blocks/text/composition-seat.js +11 -11
- package/dist/components/blocks/text/construct-edge-delete.d.ts +20 -20
- package/dist/components/blocks/text/construct-edge-delete.js +50 -65
- package/dist/components/blocks/text/construct-reveal.d.ts +16 -21
- package/dist/components/blocks/text/construct-reveal.js +13 -14
- package/dist/components/blocks/text/delimiter-autopair.d.ts +51 -34
- package/dist/components/blocks/text/delimiter-autopair.js +87 -103
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +44 -46
- package/dist/components/blocks/text/edge-policy-dispatch.js +164 -180
- package/dist/components/blocks/text/edge-seat.d.ts +17 -26
- package/dist/components/blocks/text/edge-seat.js +43 -70
- package/dist/components/blocks/text/link-at-point.d.ts +9 -9
- package/dist/components/blocks/text/link-at-point.js +12 -15
- package/dist/components/blocks/text/live-join-seam.d.ts +6 -9
- package/dist/components/blocks/text/live-join-seam.js +75 -112
- package/dist/components/blocks/text/live-selection-edit.d.ts +14 -43
- package/dist/components/blocks/text/live-selection-edit.js +32 -105
- package/dist/components/blocks/text/live-split-rebalance.d.ts +9 -13
- package/dist/components/blocks/text/live-split-rebalance.js +50 -67
- package/dist/components/blocks/text/marker-completion.d.ts +7 -12
- package/dist/components/blocks/text/marker-completion.js +17 -10
- package/dist/components/blocks/text/pending-mark-insert.d.ts +11 -18
- package/dist/components/blocks/text/pending-mark-insert.js +30 -49
- package/dist/components/blocks/text/screen-diff.d.ts +5 -15
- package/dist/components/blocks/text/screen-diff.js +5 -19
- package/dist/components/blocks/text/text-clipboard.d.ts +23 -32
- package/dist/components/blocks/text/text-clipboard.js +27 -32
- package/dist/components/blocks/text/text-keydown.d.ts +20 -30
- package/dist/components/blocks/text/text-keydown.js +54 -51
- package/dist/components/blocks/text/text-render.d.ts +25 -35
- package/dist/components/blocks/text/text-render.js +82 -61
- package/dist/components/blocks/text/widget-adjacency.d.ts +9 -9
- package/dist/components/blocks/text/widget-adjacency.js +17 -17
- package/dist/components/blocks/text/widget-interaction.d.ts +67 -43
- package/dist/components/blocks/text/widget-interaction.js +243 -208
- package/dist/components/blocks/widget-portal.d.ts +20 -29
- package/dist/components/blocks/widget-portal.js +16 -14
- package/dist/components/built-in-blocks.d.ts +3 -4
- package/dist/components/built-in-blocks.js +17 -18
- package/dist/components/drag-handle.d.ts +13 -30
- package/dist/components/drag-handle.js +25 -63
- package/dist/components/editor-built-ins.d.ts +1 -0
- package/dist/components/editor-built-ins.js +16 -0
- package/dist/components/editor-root-clipboard.d.ts +6 -6
- package/dist/components/editor-root-clipboard.js +16 -27
- package/dist/components/editor-root-document-swap.d.ts +49 -0
- package/dist/components/editor-root-document-swap.js +54 -0
- package/dist/components/editor-root-focus.d.ts +5 -4
- package/dist/components/editor-root-focus.js +51 -22
- package/dist/components/editor-root-focused-surface.d.ts +35 -0
- package/dist/components/editor-root-focused-surface.js +65 -0
- package/dist/components/editor-root-geometry.d.ts +15 -24
- package/dist/components/editor-root-geometry.js +27 -31
- package/dist/components/editor-root-gestures.d.ts +39 -0
- package/dist/components/editor-root-gestures.js +189 -0
- package/dist/components/editor-root-keydown.d.ts +10 -25
- package/dist/components/editor-root-keydown.js +19 -43
- package/dist/components/editor-root-listeners.d.ts +22 -31
- package/dist/components/editor-root-listeners.js +49 -46
- package/dist/components/editor-root-menus.d.ts +51 -0
- package/dist/components/editor-root-menus.js +147 -0
- package/dist/components/editor-root-mode-flip.d.ts +16 -11
- package/dist/components/editor-root-mode-flip.js +36 -34
- package/dist/components/editor-root-scroll-host.d.ts +22 -0
- package/dist/components/editor-root-scroll-host.js +34 -0
- package/dist/components/editor-root-test-surface.d.ts +48 -0
- package/dist/components/editor-root-test-surface.js +6 -0
- package/dist/components/image/ImageOverlayHost.svelte +44 -35
- package/dist/components/image/ImageOverlayHost.svelte.d.ts +5 -6
- package/dist/components/image/ImageProperties.svelte +37 -41
- package/dist/components/image/ImageProperties.svelte.d.ts +9 -4
- package/dist/components/image/ImageResizeHandles.svelte +26 -30
- package/dist/components/image/image-crop.d.ts +5 -11
- package/dist/components/image/image-crop.js +5 -11
- package/dist/components/image/image-edit-commit.d.ts +19 -16
- package/dist/components/image/image-edit-commit.js +74 -38
- package/dist/components/image/image-resize.d.ts +2 -5
- package/dist/components/image/image-resize.js +3 -6
- package/dist/components/image/image-widget-editing.d.ts +3 -3
- package/dist/components/image/image-widget-editing.js +9 -9
- package/dist/components/image/widget-dom.d.ts +2 -3
- package/dist/components/image/widget-dom.js +19 -24
- package/dist/components/image/widget-selection-state.svelte.d.ts +10 -11
- package/dist/components/image/widget-selection-state.svelte.js +26 -19
- package/dist/components/kind-cue.svelte.d.ts +21 -0
- package/dist/components/kind-cue.svelte.js +35 -0
- package/dist/components/link-card/LinkCard.svelte +115 -64
- package/dist/components/link-card/LinkCard.svelte.d.ts +7 -5
- package/dist/components/link-card/LinkCardHost.svelte +42 -33
- package/dist/components/link-card/LinkCardHost.svelte.d.ts +10 -8
- package/dist/components/link-card/link-card-commit.d.ts +16 -19
- package/dist/components/link-card/link-card-commit.js +21 -30
- package/dist/components/link-card/link-card-entry.d.ts +13 -23
- package/dist/components/link-card/link-card-entry.js +16 -24
- package/dist/components/link-card/link-card-state.svelte.d.ts +17 -21
- package/dist/components/link-card/link-card-state.svelte.js +2 -2
- package/dist/components/lrd-map-gate.d.ts +4 -11
- package/dist/components/lrd-map-gate.js +5 -11
- package/dist/components/menu/BlockMenu.svelte +20 -81
- package/dist/components/menu/BlockMenu.svelte.d.ts +11 -9
- package/dist/components/menu/InlineMenuHost.svelte +191 -0
- package/dist/components/menu/InlineMenuHost.svelte.d.ts +13 -0
- package/dist/components/menu/MenuIcon.svelte +3 -103
- package/dist/components/menu/MenuIcon.svelte.d.ts +2 -44
- package/dist/components/menu/SelectionToolbar.svelte +380 -0
- package/dist/components/menu/SelectionToolbar.svelte.d.ts +14 -0
- package/dist/components/menu/clipboard-actions.d.ts +3 -4
- package/dist/components/menu/clipboard-actions.js +3 -4
- package/dist/components/menu/default-context-actions.d.ts +4 -13
- package/dist/components/menu/default-context-actions.js +49 -67
- package/dist/components/menu/flyout-placement.d.ts +2 -2
- package/dist/components/menu/flyout-placement.js +2 -2
- package/dist/components/menu/menu-presence.svelte.d.ts +12 -0
- package/dist/components/menu/menu-presence.svelte.js +19 -0
- package/dist/components/paste-image-arm.d.ts +6 -11
- package/dist/components/paste-image-arm.js +11 -18
- package/dist/components/portal.d.ts +2 -5
- package/dist/components/portal.js +4 -7
- package/dist/core/directive/activate.d.ts +4 -5
- package/dist/core/directive/activate.js +8 -9
- package/dist/core/directive/container-opener.js +13 -12
- package/dist/core/directive/grammar.d.ts +2 -5
- package/dist/core/directive/grammar.js +10 -10
- package/dist/core/directive/kinds.d.ts +4 -7
- package/dist/core/directive/kinds.js +14 -16
- package/dist/core/directive/registry.d.ts +12 -9
- package/dist/core/directive/registry.js +19 -20
- package/dist/core/directive/text-recognizer.d.ts +3 -2
- package/dist/core/directive/text-recognizer.js +5 -9
- package/dist/core/escapable.d.ts +1 -5
- package/dist/core/escapable.js +1 -5
- package/dist/core/inline/backticks.d.ts +3 -4
- package/dist/core/inline/backticks.js +3 -4
- package/dist/core/inline/character-refs.js +2 -1
- package/dist/core/inline/destination-bytes.d.ts +6 -3
- package/dist/core/inline/destination-bytes.js +50 -8
- package/dist/core/inline/entity-widget.d.ts +4 -3
- package/dist/core/inline/entity-widget.js +6 -6
- package/dist/core/inline/format-toggle.d.ts +22 -28
- package/dist/core/inline/format-toggle.js +97 -129
- package/dist/core/inline/html-entities.d.ts +2 -3
- package/dist/core/inline/html-entities.js +2 -3
- package/dist/core/inline/html-tag-grammar.js +6 -2
- package/dist/core/inline/image-dimensions.d.ts +2 -0
- package/dist/core/inline/image-dimensions.js +19 -2
- package/dist/core/inline/image-source-bytes.d.ts +11 -0
- package/dist/core/inline/image-source-bytes.js +71 -0
- package/dist/core/inline/index.d.ts +28 -21
- package/dist/core/inline/index.js +56 -34
- package/dist/core/inline/inline-cache.d.ts +12 -15
- package/dist/core/inline/inline-cache.js +11 -14
- package/dist/core/inline/inline-widgets.d.ts +55 -68
- package/dist/core/inline/inline-widgets.js +55 -58
- package/dist/core/inline/link-destination.d.ts +31 -0
- package/dist/core/inline/link-destination.js +129 -0
- package/dist/core/inline/link-reference-resolver.d.ts +8 -6
- package/dist/core/inline/link-reference-resolver.js +10 -6
- package/dist/core/inline/link-source-bytes.d.ts +31 -0
- package/dist/{components/blocks/text → core/inline}/link-source-bytes.js +43 -54
- package/dist/core/inline/live-edit/read-back.d.ts +29 -0
- package/dist/core/inline/live-edit/read-back.js +50 -0
- package/dist/core/inline/picture.d.ts +10 -0
- package/dist/core/inline/picture.js +40 -0
- package/dist/core/inline/scan/autolinks.d.ts +7 -8
- package/dist/core/inline/scan/autolinks.js +80 -59
- package/dist/core/inline/scan/brackets.d.ts +2 -2
- package/dist/core/inline/scan/brackets.js +6 -103
- package/dist/core/inline/scan/code-spans.d.ts +4 -0
- package/dist/core/inline/scan/code-spans.js +8 -0
- package/dist/core/inline/scan/emphasis.js +10 -14
- package/dist/core/inline/scan/index.d.ts +3 -1
- package/dist/core/inline/scan/index.js +32 -33
- package/dist/core/inline/scan/plugin-syntax.d.ts +28 -36
- package/dist/core/inline/scan/plugin-syntax.js +75 -89
- package/dist/core/inline/scan/scan-state.d.ts +2 -0
- package/dist/core/inline/scan/scan-state.js +1 -0
- package/dist/core/inline/scan/simple-nodes.d.ts +2 -5
- package/dist/core/inline/scan/simple-nodes.js +2 -5
- package/dist/core/inline/scan/triggers.d.ts +17 -0
- package/dist/core/inline/scan/triggers.js +26 -0
- package/dist/core/inline/scan/url.js +8 -10
- package/dist/core/inline/transparency.d.ts +2 -1
- package/dist/core/inline/transparency.js +33 -17
- package/dist/core/inline/visibility.d.ts +30 -49
- package/dist/core/inline/visibility.js +31 -49
- package/dist/core/inline/walk.d.ts +4 -10
- package/dist/core/inline/walk.js +4 -10
- package/dist/core/inline-render.d.ts +22 -27
- package/dist/core/inline-render.js +51 -60
- package/dist/core/lines.d.ts +67 -26
- package/dist/core/lines.js +164 -33
- package/dist/core/metadata-parity.d.ts +9 -0
- package/dist/core/metadata-parity.js +25 -0
- package/dist/core/node-views.d.ts +7 -6
- package/dist/core/node-views.js +5 -5
- package/dist/core/nodes.d.ts +34 -44
- package/dist/core/nodes.js +4 -12
- package/dist/core/parser.d.ts +27 -34
- package/dist/core/parser.js +53 -67
- package/dist/core/parsers/blockquote.d.ts +5 -8
- package/dist/core/parsers/blockquote.js +11 -10
- package/dist/core/parsers/built-in-openers.d.ts +2 -5
- package/dist/core/parsers/built-in-openers.js +5 -8
- package/dist/core/parsers/fence-syntax.d.ts +36 -7
- package/dist/core/parsers/fence-syntax.js +61 -11
- package/dist/core/parsers/fenced-code.d.ts +16 -0
- package/dist/core/parsers/fenced-code.js +16 -15
- package/dist/core/parsers/heading.d.ts +19 -2
- package/dist/core/parsers/heading.js +50 -2
- package/dist/core/parsers/html-block.d.ts +3 -6
- package/dist/core/parsers/html-block.js +10 -11
- package/dist/core/parsers/indented-code.d.ts +1 -1
- package/dist/core/parsers/indented-code.js +2 -8
- package/dist/core/parsers/link-reference.d.ts +5 -3
- package/dist/core/parsers/link-reference.js +116 -120
- package/dist/core/parsers/list.d.ts +12 -5
- package/dist/core/parsers/list.js +53 -36
- package/dist/core/parsers/paragraph.d.ts +3 -3
- package/dist/core/parsers/paragraph.js +14 -5
- package/dist/core/parsers/table-completion.d.ts +2 -5
- package/dist/core/parsers/table-completion.js +7 -10
- package/dist/core/parsers/table.d.ts +8 -8
- package/dist/core/parsers/table.js +23 -26
- package/dist/core/parsers/thematic-break.js +15 -14
- package/dist/core/paths.d.ts +13 -0
- package/dist/core/paths.js +31 -0
- package/dist/core/terminator-escalation.d.ts +0 -7
- package/dist/core/terminator-escalation.js +5 -8
- package/dist/core/url-policy.d.ts +2 -5
- package/dist/core/url-policy.js +8 -12
- package/dist/cursor/caret-memory.d.ts +34 -0
- package/dist/cursor/caret-memory.js +82 -0
- package/dist/cursor/coordinate-spaces.d.ts +26 -19
- package/dist/cursor/coordinate-spaces.js +32 -20
- package/dist/cursor/dom-walk.d.ts +7 -13
- package/dist/cursor/dom-walk.js +7 -13
- package/dist/cursor/edge-affinity.d.ts +7 -35
- package/dist/cursor/edge-affinity.js +13 -44
- package/dist/cursor/focused-caret.d.ts +6 -8
- package/dist/cursor/focused-caret.js +7 -16
- package/dist/cursor/height-model.d.ts +3 -3
- package/dist/cursor/height-model.js +5 -5
- package/dist/cursor/height-oracle.d.ts +10 -7
- package/dist/cursor/height-oracle.js +26 -75
- package/dist/cursor/observe-resize.d.ts +13 -0
- package/dist/cursor/observe-resize.js +43 -0
- package/dist/cursor/overlay-rects.d.ts +2 -5
- package/dist/cursor/overlay-rects.js +2 -5
- package/dist/cursor/overlay-remeasure.d.ts +6 -7
- package/dist/cursor/overlay-remeasure.js +10 -14
- package/dist/cursor/pending-marks.d.ts +7 -9
- package/dist/cursor/pending-marks.js +6 -27
- package/dist/cursor/point-offset.d.ts +16 -10
- package/dist/cursor/point-offset.js +60 -25
- package/dist/cursor/reveal-source.d.ts +7 -11
- package/dist/cursor/reveal-source.js +10 -18
- package/dist/cursor/scroll-ancestors.d.ts +9 -20
- package/dist/cursor/scroll-ancestors.js +14 -26
- package/dist/cursor/scroll-owner.d.ts +92 -0
- package/dist/cursor/scroll-owner.js +283 -0
- package/dist/cursor/scrollport.d.ts +16 -8
- package/dist/cursor/scrollport.js +28 -5
- package/dist/cursor/sticky-column.d.ts +6 -29
- package/dist/cursor/sticky-column.js +6 -49
- package/dist/cursor/sticky-measure.d.ts +14 -9
- package/dist/cursor/sticky-measure.js +99 -49
- package/dist/cursor/surface-backend.d.ts +41 -0
- package/dist/cursor/surface-backend.js +78 -0
- package/dist/cursor/typography-estimates.d.ts +3 -3
- package/dist/cursor/typography-estimates.js +3 -3
- package/dist/cursor/visual-lines.d.ts +25 -13
- package/dist/cursor/visual-lines.js +72 -28
- package/dist/cursor/widget-edge-snap.d.ts +23 -0
- package/dist/cursor/widget-edge-snap.js +39 -0
- package/dist/cursor/widget-offset.d.ts +111 -102
- package/dist/cursor/widget-offset.js +352 -177
- package/dist/debug/diagnostics-report.d.ts +6 -6
- package/dist/debug/diagnostics-report.js +5 -5
- package/dist/debug/dump-tree.js +2 -2
- package/dist/debug/editor-diagnostics.d.ts +15 -0
- package/dist/debug/editor-diagnostics.js +33 -0
- package/dist/debug/inspect.d.ts +2 -2
- package/dist/debug/inspect.js +4 -4
- package/dist/debug/interaction-trace.d.ts +14 -15
- package/dist/debug/interaction-trace.js +19 -20
- package/dist/debug/operations-log.d.ts +1 -1
- package/dist/debug/operations-log.js +1 -1
- package/dist/decorations/buckets.d.ts +6 -12
- package/dist/decorations/buckets.js +4 -11
- package/dist/decorations/decoration-state.svelte.d.ts +2 -3
- package/dist/decorations/decoration-state.svelte.js +24 -26
- package/dist/decorations/island-dom.d.ts +10 -18
- package/dist/decorations/island-dom.js +25 -50
- package/dist/decorations/reserved-attrs.d.ts +6 -8
- package/dist/decorations/reserved-attrs.js +31 -17
- package/dist/decorations/types.d.ts +6 -7
- package/dist/decorations/types.js +2 -1
- package/dist/decorations/use-block-decorations.svelte.d.ts +19 -0
- package/dist/decorations/use-block-decorations.svelte.js +71 -0
- package/dist/decorations/widget-dom.d.ts +3 -3
- package/dist/decorations/widget-dom.js +5 -5
- package/dist/dev-warn.d.ts +5 -2
- package/dist/dev-warn.js +16 -4
- package/dist/editor-actions/ancestry-folds.d.ts +9 -14
- package/dist/editor-actions/ancestry-folds.js +13 -18
- package/dist/editor-actions/block-edit-core.d.ts +49 -22
- package/dist/editor-actions/block-edit-core.js +279 -151
- package/dist/editor-actions/block-edit-scope.d.ts +63 -50
- package/dist/editor-actions/block-edit-scope.js +100 -48
- package/dist/editor-actions/block-edit.d.ts +2 -3
- package/dist/editor-actions/block-edit.js +14 -90
- package/dist/editor-actions/commit/history.d.ts +4 -4
- package/dist/editor-actions/commit/history.js +33 -34
- package/dist/editor-actions/commit/reading-write-gate.d.ts +18 -0
- package/dist/editor-actions/commit/reading-write-gate.js +56 -0
- package/dist/editor-actions/commit/text-batch.d.ts +7 -11
- package/dist/editor-actions/commit/text-batch.js +6 -7
- package/dist/editor-actions/commit/undo-controller.d.ts +6 -4
- package/dist/editor-actions/commit/undo-controller.js +346 -216
- package/dist/editor-actions/container-block-component.d.ts +32 -51
- package/dist/editor-actions/container-block-component.js +66 -92
- package/dist/editor-actions/container-edit.d.ts +4 -2
- package/dist/editor-actions/container-edit.js +14 -44
- package/dist/editor-actions/container-exit-overrides.d.ts +6 -4
- package/dist/editor-actions/container-exit-overrides.js +19 -21
- package/dist/editor-actions/deps.d.ts +25 -32
- package/dist/editor-actions/enter-completion.d.ts +12 -15
- package/dist/editor-actions/enter-completion.js +49 -51
- package/dist/editor-actions/focus/focus-dispatch.d.ts +24 -25
- package/dist/editor-actions/focus/focus-dispatch.js +33 -52
- package/dist/editor-actions/focus/focus-landing.d.ts +11 -5
- package/dist/editor-actions/focus/focus-landing.js +29 -14
- package/dist/editor-actions/focus/focus.d.ts +3 -2
- package/dist/editor-actions/focus/focus.js +42 -59
- package/dist/editor-actions/index.d.ts +4 -3
- package/dist/editor-actions/index.js +5 -4
- package/dist/editor-actions/inline-range-commit.d.ts +18 -16
- package/dist/editor-actions/inline-range-commit.js +45 -54
- package/dist/editor-actions/leaf-write.d.ts +11 -0
- package/dist/editor-actions/leaf-write.js +102 -0
- package/dist/editor-actions/list-context.d.ts +9 -10
- package/dist/editor-actions/list-context.js +115 -110
- package/dist/editor-actions/list-overrides.d.ts +13 -5
- package/dist/editor-actions/list-overrides.js +36 -12
- package/dist/editor-actions/merge-fallback.d.ts +10 -16
- package/dist/editor-actions/merge-fallback.js +11 -22
- package/dist/editor-actions/nested/container-actions.d.ts +37 -0
- package/dist/editor-actions/nested/container-actions.js +42 -0
- package/dist/editor-actions/nested/emptied-container.d.ts +5 -0
- package/dist/editor-actions/nested/emptied-container.js +5 -0
- package/dist/editor-actions/nested/nested-actions.d.ts +22 -25
- package/dist/editor-actions/nested/nested-actions.js +17 -15
- package/dist/editor-actions/nested/nested-block-edit.d.ts +3 -4
- package/dist/editor-actions/nested/nested-block-edit.js +40 -134
- package/dist/editor-actions/nested/nested-focus.d.ts +2 -3
- package/dist/editor-actions/nested/nested-focus.js +33 -21
- package/dist/editor-actions/paste-coordinator.d.ts +3 -2
- package/dist/editor-actions/paste-coordinator.js +17 -13
- package/dist/editor-actions/plugin/chrome-leaf.d.ts +11 -9
- package/dist/editor-actions/plugin/chrome-leaf.js +14 -13
- package/dist/editor-actions/plugin/container.d.ts +55 -76
- package/dist/editor-actions/plugin/container.js +105 -186
- package/dist/editor-actions/plugin/directive-container.d.ts +4 -4
- package/dist/editor-actions/plugin/directive-container.js +7 -8
- package/dist/editor-actions/reorder-action.d.ts +7 -12
- package/dist/editor-actions/reorder-action.js +32 -48
- package/dist/editor-actions/reorder-drag.d.ts +12 -8
- package/dist/editor-actions/reorder-drag.js +45 -41
- package/dist/editor-actions/replacement-focus.d.ts +13 -21
- package/dist/editor-actions/replacement-focus.js +32 -33
- package/dist/editor-actions/search-replace.js +71 -67
- package/dist/editor-actions/stored-caret.d.ts +7 -0
- package/dist/editor-actions/stored-caret.js +10 -0
- package/dist/editor-actions/table-context.d.ts +9 -11
- package/dist/editor-actions/table-context.js +70 -93
- package/dist/editor-actions/unwrap-strategies.d.ts +4 -5
- package/dist/editor-actions/unwrap-strategies.js +52 -63
- package/dist/editor-actions/whole-block-focus-surface.d.ts +19 -28
- package/dist/editor-actions/whole-block-focus-surface.js +43 -53
- package/dist/editor-events.d.ts +25 -29
- package/dist/editor-events.js +17 -28
- package/dist/editor-keys.d.ts +96 -104
- package/dist/editor-keys.js +11 -24
- package/dist/editor-props.d.ts +91 -71
- package/dist/editor-rects.d.ts +23 -34
- package/dist/editor-rects.js +9 -77
- package/dist/env.d.ts +6 -4
- package/dist/env.js +8 -4
- package/dist/index.d.ts +6 -2
- package/dist/index.js +5 -2
- package/dist/inline-menu/inline-menu-session.d.ts +37 -0
- package/dist/inline-menu/inline-menu-session.js +98 -0
- package/dist/inline-menu/inline-menu-state.svelte.d.ts +63 -0
- package/dist/inline-menu/inline-menu-state.svelte.js +409 -0
- package/dist/inline-menu/types.d.ts +77 -0
- package/dist/inline-menu/types.js +7 -0
- package/dist/invariants/child-id-parity.d.ts +23 -0
- package/dist/invariants/child-id-parity.js +39 -0
- package/dist/invariants/commit-paths.d.ts +4 -4
- package/dist/invariants/commit-paths.js +4 -4
- package/dist/invariants/commit-scope.d.ts +4 -4
- package/dist/invariants/commit-scope.js +9 -8
- package/dist/invariants/context-keys.d.ts +2 -5
- package/dist/invariants/context-keys.js +3 -6
- package/dist/invariants/descriptor.d.ts +2 -6
- package/dist/invariants/descriptor.js +2 -6
- package/dist/invariants/inline-transitions.d.ts +9 -16
- package/dist/invariants/inline-transitions.js +10 -18
- package/dist/invariants/install.d.ts +18 -27
- package/dist/invariants/install.js +29 -30
- package/dist/invariants/keeps-a-block.d.ts +7 -0
- package/dist/invariants/keeps-a-block.js +14 -0
- package/dist/invariants/landable-caret.d.ts +5 -7
- package/dist/invariants/landable-caret.js +8 -11
- package/dist/invariants/landing-focus-scroll.d.ts +7 -0
- package/dist/invariants/landing-focus-scroll.js +13 -0
- package/dist/invariants/landing-value.d.ts +13 -0
- package/dist/invariants/landing-value.js +27 -0
- package/dist/invariants/marker-css-parity.d.ts +5 -6
- package/dist/invariants/marker-css-parity.js +11 -12
- package/dist/invariants/measures-in-own-list.d.ts +6 -0
- package/dist/invariants/measures-in-own-list.js +13 -0
- package/dist/invariants/node-shape.d.ts +18 -37
- package/dist/invariants/node-shape.js +123 -79
- package/dist/invariants/open-tail.d.ts +7 -0
- package/dist/invariants/open-tail.js +45 -0
- package/dist/invariants/placement-ends-widget.d.ts +8 -0
- package/dist/invariants/placement-ends-widget.js +13 -0
- package/dist/invariants/registry.d.ts +27 -66
- package/dist/invariants/registry.js +61 -137
- package/dist/invariants/render-fidelity.d.ts +4 -4
- package/dist/invariants/render-fidelity.js +5 -5
- package/dist/invariants/selection-endpoints.d.ts +6 -7
- package/dist/invariants/selection-endpoints.js +18 -16
- package/dist/invariants/single-node-sink.d.ts +4 -5
- package/dist/invariants/single-node-sink.js +4 -5
- package/dist/invariants/snapshot-integrity.d.ts +5 -6
- package/dist/invariants/snapshot-integrity.js +1 -1
- package/dist/invariants/structural-descriptor.d.ts +6 -15
- package/dist/invariants/structural-descriptor.js +6 -15
- package/dist/menu-icons.d.ts +47 -0
- package/dist/menu-icons.js +113 -0
- package/dist/perf/instruments.d.ts +20 -5
- package/dist/perf/instruments.js +35 -15
- package/dist/perf/use-mount-gauge.svelte.js +4 -4
- package/dist/plugin.d.ts +38 -8
- package/dist/plugin.js +102 -74
- package/dist/plugins/admonitions/AdmonitionBlock.svelte +14 -11
- package/dist/plugins/admonitions/admonition-kind.js +12 -10
- package/dist/plugins/admonitions/convert-document.d.ts +5 -5
- package/dist/plugins/admonitions/convert-document.js +8 -7
- package/dist/plugins/admonitions/gh-alert.d.ts +8 -1
- package/dist/plugins/admonitions/gh-alert.js +21 -22
- package/dist/plugins/admonitions/github-alert-kind.d.ts +5 -7
- package/dist/plugins/admonitions/github-alert-kind.js +28 -37
- package/dist/plugins/admonitions/index.js +2 -2
- package/dist/plugins/admonitions/kinds.d.ts +1 -1
- package/dist/plugins/admonitions/register.js +11 -2
- package/dist/plugins/details/DetailsBlock.svelte +20 -20
- package/dist/plugins/details/details-disclosure.svelte.d.ts +5 -6
- package/dist/plugins/details/details-disclosure.svelte.js +6 -7
- package/dist/plugins/details/details-kind.d.ts +6 -7
- package/dist/plugins/details/details-kind.js +44 -59
- package/dist/plugins/emoji/emoji-recognizer.d.ts +4 -7
- package/dist/plugins/emoji/emoji-recognizer.js +7 -14
- package/dist/plugins/footnotes/FootnoteDefinition.svelte +33 -16
- package/dist/plugins/footnotes/FootnoteReference.svelte +39 -19
- package/dist/plugins/footnotes/constants.d.ts +2 -0
- package/dist/plugins/footnotes/constants.js +3 -0
- package/dist/plugins/footnotes/footnote-definition.d.ts +8 -6
- package/dist/plugins/footnotes/footnote-definition.js +69 -45
- package/dist/plugins/footnotes/footnote-lookup.d.ts +3 -3
- package/dist/plugins/footnotes/footnote-lookup.js +14 -26
- package/dist/plugins/footnotes/footnote-numbering.d.ts +11 -11
- package/dist/plugins/footnotes/footnote-numbering.js +22 -31
- package/dist/plugins/footnotes/footnote-reference.d.ts +4 -4
- package/dist/plugins/footnotes/footnote-reference.js +8 -13
- package/dist/plugins/footnotes/index.js +2 -3
- package/dist/plugins/highlight-occurrences/highlight-occurrences-plugin.d.ts +5 -8
- package/dist/plugins/highlight-occurrences/highlight-occurrences-plugin.js +8 -3
- package/dist/plugins/highlight-occurrences/occurrence-source.d.ts +10 -12
- package/dist/plugins/highlight-occurrences/occurrence-source.js +21 -18
- package/dist/plugins/highlight-occurrences/occurrences.d.ts +11 -11
- package/dist/plugins/highlight-occurrences/occurrences.js +14 -36
- package/dist/plugins/highlight-occurrences/word-char.d.ts +1 -0
- package/dist/plugins/highlight-occurrences/word-char.js +4 -0
- package/dist/plugins/latex/BlockMath.svelte +58 -75
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -4
- package/dist/plugins/latex/MathInline.svelte +10 -9
- package/dist/plugins/latex/flanking.d.ts +1 -0
- package/dist/plugins/latex/flanking.js +3 -0
- package/dist/plugins/latex/index.d.ts +1 -1
- package/dist/plugins/latex/latex-kind.d.ts +4 -4
- package/dist/plugins/latex/latex-kind.js +113 -85
- package/dist/plugins/latex/math-completion.d.ts +1 -1
- package/dist/plugins/latex/math-completion.js +5 -8
- package/dist/plugins/latex/math-layout.d.ts +2 -8
- package/dist/plugins/latex/math-layout.js +0 -9
- package/dist/plugins/latex/math-renderer.d.ts +17 -20
- package/dist/plugins/latex/math-renderer.js +21 -38
- package/dist/plugins/latex/math-source.d.ts +4 -8
- package/dist/plugins/latex/math-source.js +36 -75
- package/dist/plugins/latex/register.d.ts +10 -11
- package/dist/plugins/latex/register.js +24 -10
- package/dist/plugins/latex/renderer.d.ts +9 -10
- package/dist/plugins/latex/renderer.js +9 -24
- package/dist/plugins/mermaid/MermaidBlock.svelte +63 -47
- package/dist/plugins/mermaid/index.js +1 -1
- package/dist/plugins/mermaid/mermaid-kind.d.ts +4 -7
- package/dist/plugins/mermaid/mermaid-kind.js +30 -47
- package/dist/plugins/mermaid/mermaid-renderer.d.ts +9 -16
- package/dist/plugins/mermaid/mermaid-renderer.js +12 -31
- package/dist/plugins/mermaid/register.d.ts +3 -3
- package/dist/plugins/mermaid/register.js +5 -5
- package/dist/plugins/mermaid/renderer.d.ts +5 -5
- package/dist/plugins/mermaid/renderer.js +8 -11
- package/dist/plugins/parrot/ParrotBlock.svelte +18 -7
- package/dist/plugins/parrot/ParrotBlock.svelte.d.ts +2 -2
- package/dist/plugins/parrot/parrot-plugin.js +3 -3
- package/dist/plugins/slash-commands/filter.d.ts +23 -0
- package/dist/plugins/slash-commands/filter.js +31 -0
- package/dist/plugins/slash-commands/index.d.ts +2 -0
- package/dist/plugins/slash-commands/index.js +2 -0
- package/dist/plugins/slash-commands/slash-commands-plugin.d.ts +12 -0
- package/dist/plugins/slash-commands/slash-commands-plugin.js +40 -0
- package/dist/plugins/slash-commands/slash-source.d.ts +36 -0
- package/dist/plugins/slash-commands/slash-source.js +108 -0
- package/dist/plugins/toc/TocBlock.svelte +23 -22
- package/dist/plugins/toc/TocBlock.svelte.d.ts +2 -3
- package/dist/plugins/toc/heading-outline.d.ts +7 -12
- package/dist/plugins/toc/heading-outline.js +22 -36
- package/dist/plugins/toc/navigation-queue.d.ts +2 -4
- package/dist/plugins/toc/toc-plugin.d.ts +5 -5
- package/dist/plugins/toc/toc-plugin.js +20 -13
- package/dist/presentation-mode.d.ts +15 -23
- package/dist/presentation-mode.js +19 -25
- package/dist/reactivity/block-list-state.svelte.d.ts +9 -11
- package/dist/reactivity/block-list-state.svelte.js +8 -9
- package/dist/reactivity/block-window.svelte.d.ts +12 -6
- package/dist/reactivity/block-window.svelte.js +44 -17
- package/dist/reactivity/child-list.d.ts +34 -0
- package/dist/reactivity/child-list.js +48 -0
- package/dist/reactivity/content-version.svelte.d.ts +4 -7
- package/dist/reactivity/content-version.svelte.js +3 -5
- package/dist/reactivity/hold-across.d.ts +39 -0
- package/dist/reactivity/hold-across.js +57 -0
- package/dist/reactivity/layout-state.svelte.d.ts +27 -0
- package/dist/reactivity/layout-state.svelte.js +51 -0
- package/dist/reactivity/list-tree.d.ts +39 -0
- package/dist/reactivity/list-tree.js +132 -0
- package/dist/reactivity/list-windowing.svelte.d.ts +50 -71
- package/dist/reactivity/list-windowing.svelte.js +143 -292
- package/dist/reactivity/measure-batch.d.ts +6 -8
- package/dist/reactivity/measure-batch.js +5 -6
- package/dist/reactivity/publish-ref.svelte.d.ts +16 -41
- package/dist/reactivity/publish-ref.svelte.js +21 -85
- package/dist/reactivity/scope-geometry.d.ts +6 -13
- package/dist/reactivity/scope-geometry.js +7 -14
- package/dist/reactivity/state-registry.d.ts +5 -5
- package/dist/reactivity/state-registry.js +11 -15
- package/dist/reactivity/use-container-windowing.svelte.d.ts +13 -26
- package/dist/reactivity/use-container-windowing.svelte.js +64 -74
- package/dist/reactivity/use-measured-child.svelte.d.ts +10 -0
- package/dist/reactivity/use-measured-child.svelte.js +41 -0
- package/dist/reactivity/use-window-floor.svelte.d.ts +2 -0
- package/dist/reactivity/use-window-floor.svelte.js +27 -0
- package/dist/reactivity/window-slice.d.ts +4 -4
- package/dist/reactivity/window-slice.js +0 -2
- package/dist/renderer-slot.d.ts +48 -0
- package/dist/renderer-slot.js +85 -0
- package/dist/scan-index.d.ts +4 -4
- package/dist/scan-index.js +4 -4
- package/dist/schema/block-commands.d.ts +69 -54
- package/dist/schema/block-commands.js +91 -104
- package/dist/schema/block-completions.d.ts +16 -19
- package/dist/schema/block-completions.js +25 -32
- package/dist/schema/block-component-registry.d.ts +13 -14
- package/dist/schema/block-component-registry.js +19 -17
- package/dist/schema/block-kind-descriptor.d.ts +234 -168
- package/dist/schema/block-kind-descriptor.js +151 -66
- package/dist/schema/block-openers.d.ts +48 -39
- package/dist/schema/block-openers.js +73 -83
- package/dist/schema/built-in-descriptors.d.ts +3 -5
- package/dist/schema/built-in-descriptors.js +96 -80
- package/dist/schema/child-spans.d.ts +12 -10
- package/dist/schema/child-spans.js +68 -34
- package/dist/schema/closure.d.ts +18 -16
- package/dist/schema/closure.js +24 -14
- package/dist/schema/command-id.d.ts +6 -8
- package/dist/schema/command-id.js +20 -25
- package/dist/schema/commands.d.ts +79 -81
- package/dist/schema/commands.js +156 -116
- package/dist/schema/container-raw.d.ts +51 -11
- package/dist/schema/container-raw.js +143 -14
- package/dist/schema/container-rebuilders.d.ts +17 -15
- package/dist/schema/container-rebuilders.js +30 -22
- package/dist/schema/context-actions.d.ts +25 -13
- package/dist/schema/context-actions.js +32 -17
- package/dist/schema/define-plugin-block.d.ts +8 -6
- package/dist/schema/define-plugin-block.js +6 -4
- package/dist/schema/fenced-code-raw.d.ts +30 -17
- package/dist/schema/fenced-code-raw.js +155 -107
- package/dist/schema/global-commands.d.ts +6 -6
- package/dist/schema/global-commands.js +14 -22
- package/dist/schema/height-estimates.d.ts +31 -0
- package/dist/schema/height-estimates.js +64 -0
- package/dist/schema/inline-construct-policy.d.ts +42 -55
- package/dist/schema/inline-construct-policy.js +36 -35
- package/dist/schema/insert-catalogue.d.ts +30 -0
- package/dist/schema/insert-catalogue.js +95 -0
- package/dist/schema/keybinding-overrides.d.ts +9 -11
- package/dist/schema/keybinding-overrides.js +1 -1
- package/dist/schema/keybindings.d.ts +15 -7
- package/dist/schema/keybindings.js +21 -9
- package/dist/schema/merge-rules.d.ts +5 -7
- package/dist/schema/merge-rules.js +7 -9
- package/dist/schema/opener-priorities.d.ts +4 -4
- package/dist/schema/opener-priorities.js +4 -4
- package/dist/schema/operations.d.ts +9 -11
- package/dist/schema/operations.js +3 -3
- package/dist/schema/page-role.d.ts +14 -0
- package/dist/schema/page-role.js +19 -0
- package/dist/schema/plugin-activation.d.ts +4 -6
- package/dist/schema/plugin-activation.js +12 -10
- package/dist/schema/plugin-editor-context.d.ts +25 -10
- package/dist/schema/plugin-editor-context.js +40 -9
- package/dist/schema/plugin-install.d.ts +49 -17
- package/dist/schema/plugin-install.js +51 -25
- package/dist/schema/plugin-kind.d.ts +12 -8
- package/dist/schema/plugin-kind.js +35 -42
- package/dist/schema/plugin-name.js +3 -3
- package/dist/schema/plugin-registry.d.ts +54 -0
- package/dist/schema/plugin-registry.js +70 -0
- package/dist/schema/reading.d.ts +23 -0
- package/dist/schema/reading.js +7 -0
- package/dist/schema/register-once.d.ts +3 -5
- package/dist/schema/register-once.js +7 -16
- package/dist/schema/registration-checks.d.ts +12 -16
- package/dist/schema/registration-checks.js +35 -51
- package/dist/schema/registration-pairs.d.ts +54 -0
- package/dist/schema/registration-pairs.js +48 -0
- package/dist/schema/registration-pending.d.ts +8 -12
- package/dist/schema/registration-pending.js +9 -9
- package/dist/schema/registry-reset.d.ts +8 -4
- package/dist/schema/registry-reset.js +13 -26
- package/dist/schema/registry-view.d.ts +25 -13
- package/dist/schema/registry-view.js +26 -20
- package/dist/schema/reserved-chords.d.ts +9 -10
- package/dist/schema/reserved-chords.js +73 -78
- package/dist/schema/reserved-chrome.d.ts +12 -11
- package/dist/schema/reserved-chrome.js +15 -11
- package/dist/schema/setext-raw.d.ts +13 -0
- package/dist/schema/setext-raw.js +33 -0
- package/dist/schema/stored-as.d.ts +20 -0
- package/dist/schema/stored-as.js +6 -0
- package/dist/schema/table-cell-raw.d.ts +18 -7
- package/dist/schema/table-cell-raw.js +28 -7
- package/dist/schema/whole-block-unit.d.ts +5 -4
- package/dist/schema/whole-block-unit.js +5 -4
- package/dist/search/document-scan.d.ts +3 -3
- package/dist/search/document-scan.js +2 -2
- package/dist/search/matcher.d.ts +1 -1
- package/dist/search/regex-executor.d.ts +5 -6
- package/dist/search/regex-executor.js +6 -8
- package/dist/search/replace.d.ts +2 -5
- package/dist/search/replace.js +2 -5
- package/dist/search/search-state.svelte.d.ts +5 -5
- package/dist/search/search-state.svelte.js +12 -16
- package/dist/selection/autoscroll.d.ts +6 -9
- package/dist/selection/block-hit-test.d.ts +16 -24
- package/dist/selection/block-hit-test.js +29 -22
- package/dist/selection/caret-doors.d.ts +15 -14
- package/dist/selection/caret-doors.js +37 -26
- package/dist/selection/caret-landing.d.ts +62 -0
- package/dist/selection/caret-landing.js +157 -0
- package/dist/selection/caret-restore.d.ts +17 -10
- package/dist/selection/caret-restore.js +10 -22
- package/dist/selection/caret-target.d.ts +30 -0
- package/dist/selection/caret-target.js +84 -0
- package/dist/selection/char-endpoint-snap.d.ts +6 -10
- package/dist/selection/char-endpoint-snap.js +8 -14
- package/dist/selection/clipboard-text.d.ts +5 -12
- package/dist/selection/clipboard-text.js +69 -172
- package/dist/selection/covered-block.d.ts +6 -8
- package/dist/selection/covered-block.js +8 -19
- package/dist/selection/cross-block/clipboard.d.ts +3 -2
- package/dist/selection/cross-block/clipboard.js +13 -4
- package/dist/selection/cross-block/dispatch.d.ts +47 -43
- package/dist/selection/cross-block/dispatch.js +12 -23
- package/dist/selection/cross-block/format-range.d.ts +18 -21
- package/dist/selection/cross-block/format-range.js +104 -103
- package/dist/selection/cross-block/format-toggle.d.ts +10 -15
- package/dist/selection/cross-block/format-toggle.js +28 -33
- package/dist/selection/cross-block/keydown.d.ts +4 -4
- package/dist/selection/cross-block/keydown.js +93 -152
- package/dist/selection/cross-block/ops.d.ts +19 -34
- package/dist/selection/cross-block/ops.js +110 -95
- package/dist/selection/cross-block/paste.d.ts +2 -3
- package/dist/selection/cross-block/paste.js +61 -104
- package/dist/selection/cross-block/pointer.d.ts +17 -15
- package/dist/selection/cross-block/pointer.js +74 -25
- package/dist/selection/cross-block/type-replace.d.ts +4 -6
- package/dist/selection/cross-block/type-replace.js +53 -146
- package/dist/selection/dead-space-caret.d.ts +20 -28
- package/dist/selection/dead-space-caret.js +79 -87
- package/dist/selection/drag-pointer.d.ts +22 -5
- package/dist/selection/drag-pointer.js +63 -31
- package/dist/selection/gap-caret.d.ts +17 -28
- package/dist/selection/gap-caret.js +14 -26
- package/dist/selection/grid-selection.d.ts +12 -0
- package/dist/selection/grid-selection.js +22 -0
- package/dist/selection/keyboard-extend.d.ts +27 -39
- package/dist/selection/keyboard-extend.js +94 -116
- package/dist/selection/multi-click.d.ts +37 -0
- package/dist/selection/multi-click.js +197 -0
- package/dist/selection/native-bridge.d.ts +30 -37
- package/dist/selection/native-bridge.js +80 -100
- package/dist/selection/nearest-block.d.ts +17 -12
- package/dist/selection/nearest-block.js +37 -18
- package/dist/selection/path-lookup.d.ts +31 -14
- package/dist/selection/path-lookup.js +110 -20
- package/dist/selection/path-math.d.ts +9 -11
- package/dist/selection/path-math.js +5 -7
- package/dist/selection/pointer-gesture.d.ts +9 -0
- package/dist/selection/pointer-gesture.js +11 -0
- package/dist/selection/pointer-session.d.ts +10 -16
- package/dist/selection/pointer-session.js +6 -7
- package/dist/selection/primitives.d.ts +64 -33
- package/dist/selection/primitives.js +61 -53
- package/dist/selection/range-coverage.d.ts +94 -0
- package/dist/selection/range-coverage.js +365 -0
- package/dist/selection/range-delete-ceremony.d.ts +28 -74
- package/dist/selection/range-delete-ceremony.js +100 -141
- package/dist/selection/range-delete-chrome.d.ts +20 -33
- package/dist/selection/range-delete-chrome.js +53 -78
- package/dist/selection/range-delete-table-coverage.d.ts +11 -25
- package/dist/selection/range-delete-table-coverage.js +42 -112
- package/dist/selection/range-delete-table.d.ts +14 -12
- package/dist/selection/range-delete-table.js +106 -310
- package/dist/selection/range-delete.d.ts +18 -19
- package/dist/selection/range-delete.js +83 -123
- package/dist/selection/round-trip-restore.d.ts +18 -0
- package/dist/selection/round-trip-restore.js +19 -0
- package/dist/selection/selection-announcer.d.ts +17 -0
- package/dist/selection/selection-announcer.js +25 -0
- package/dist/selection/selection-description.d.ts +4 -3
- package/dist/selection/selection-description.js +5 -4
- package/dist/selection/selection-drop.d.ts +52 -0
- package/dist/selection/selection-drop.js +317 -0
- package/dist/selection/selection-restore.d.ts +20 -33
- package/dist/selection/selection-restore.js +21 -44
- package/dist/selection/selection-state.svelte.d.ts +50 -42
- package/dist/selection/selection-state.svelte.js +144 -98
- package/dist/selection/shared-keydown.d.ts +22 -27
- package/dist/selection/shared-keydown.js +25 -45
- package/dist/selection/table-endpoint-snap.d.ts +17 -41
- package/dist/selection/table-endpoint-snap.js +54 -80
- package/dist/selection/table-rect-extend.d.ts +3 -6
- package/dist/selection/table-rect-extend.js +19 -14
- package/dist/styles/editor-theme.css +75 -53
- package/dist/styles/editor.css +248 -139
- package/dist/testing/conformance-core.d.ts +50 -18
- package/dist/testing/conformance-core.js +88 -50
- package/dist/testing/container-conformance.d.ts +48 -84
- package/dist/testing/container-conformance.js +159 -285
- package/dist/testing/headless-actions.d.ts +34 -19
- package/dist/testing/headless-actions.js +116 -65
- package/dist/testing/headless-block-list.svelte.d.ts +16 -0
- package/dist/testing/headless-block-list.svelte.js +22 -0
- package/dist/testing/inline-conformance.d.ts +15 -22
- package/dist/testing/inline-conformance.js +131 -148
- package/dist/testing/kind-conformance.d.ts +19 -24
- package/dist/testing/kind-conformance.js +204 -117
- package/dist/testing/kit-reading.d.ts +7 -0
- package/dist/testing/kit-reading.js +16 -0
- package/dist/testing/mount-dom-stubs.d.ts +4 -5
- package/dist/testing/mount-dom-stubs.js +8 -5
- package/dist/testing/parse-convergence.d.ts +10 -10
- package/dist/testing/parse-convergence.js +14 -49
- package/dist/testing.d.ts +13 -9
- package/dist/testing.js +28 -38
- package/dist/tree-operations/blockquote.d.ts +8 -14
- package/dist/tree-operations/blockquote.js +25 -45
- package/dist/tree-operations/chain-rebuild.d.ts +34 -25
- package/dist/tree-operations/chain-rebuild.js +115 -52
- package/dist/tree-operations/children.d.ts +5 -9
- package/dist/tree-operations/children.js +7 -11
- package/dist/tree-operations/cleanup.d.ts +6 -7
- package/dist/tree-operations/cleanup.js +14 -11
- package/dist/tree-operations/clone.js +5 -4
- package/dist/tree-operations/container-lift.d.ts +10 -5
- package/dist/tree-operations/container-lift.js +20 -14
- package/dist/tree-operations/container-offsets.d.ts +22 -0
- package/dist/tree-operations/container-offsets.js +89 -0
- package/dist/tree-operations/content-write.d.ts +52 -29
- package/dist/tree-operations/content-write.js +155 -110
- package/dist/tree-operations/index.d.ts +4 -4
- package/dist/tree-operations/index.js +4 -4
- package/dist/tree-operations/keep-one-block.d.ts +10 -0
- package/dist/tree-operations/keep-one-block.js +19 -0
- package/dist/tree-operations/leaf-range.d.ts +33 -0
- package/dist/tree-operations/leaf-range.js +67 -0
- package/dist/tree-operations/list/empty-check.d.ts +2 -3
- package/dist/tree-operations/list/empty-check.js +4 -4
- package/dist/tree-operations/list/exit-replacement.d.ts +4 -6
- package/dist/tree-operations/list/exit-replacement.js +4 -9
- package/dist/tree-operations/list/item-partition.d.ts +2 -2
- package/dist/tree-operations/list/item-partition.js +2 -2
- package/dist/tree-operations/list/list-builders.d.ts +18 -16
- package/dist/tree-operations/list/list-builders.js +59 -30
- package/dist/tree-operations/list/ordered-markers.d.ts +7 -16
- package/dist/tree-operations/list/ordered-markers.js +7 -16
- package/dist/tree-operations/list/reconcile-task.d.ts +16 -7
- package/dist/tree-operations/list/reconcile-task.js +80 -23
- package/dist/tree-operations/list/sublist-separator.d.ts +7 -7
- package/dist/tree-operations/list/sublist-separator.js +20 -27
- package/dist/tree-operations/list/task-paragraph.d.ts +25 -0
- package/dist/tree-operations/list/task-paragraph.js +54 -0
- package/dist/tree-operations/list/unwrap-merge.d.ts +8 -16
- package/dist/tree-operations/list/unwrap-merge.js +44 -79
- package/dist/tree-operations/node-ops.d.ts +30 -52
- package/dist/tree-operations/node-ops.js +152 -218
- package/dist/tree-operations/node-primitives.d.ts +44 -31
- package/dist/tree-operations/node-primitives.js +84 -60
- package/dist/tree-operations/open-tail.d.ts +25 -0
- package/dist/tree-operations/open-tail.js +100 -0
- package/dist/tree-operations/parse-block.d.ts +15 -2
- package/dist/tree-operations/parse-block.js +19 -4
- package/dist/tree-operations/paste/apply.d.ts +7 -8
- package/dist/tree-operations/paste/apply.js +33 -63
- package/dist/tree-operations/paste/body-write.d.ts +8 -6
- package/dist/tree-operations/paste/body-write.js +13 -14
- package/dist/tree-operations/paste/container-match.d.ts +7 -10
- package/dist/tree-operations/paste/container-match.js +76 -58
- package/dist/tree-operations/paste/container-paste.d.ts +3 -3
- package/dist/tree-operations/paste/container-paste.js +3 -3
- package/dist/tree-operations/paste/dispatch.d.ts +26 -29
- package/dist/tree-operations/paste/dispatch.js +68 -53
- package/dist/tree-operations/paste/focus-target.d.ts +11 -14
- package/dist/tree-operations/paste/focus-target.js +13 -18
- package/dist/tree-operations/paste/hooks.d.ts +7 -12
- package/dist/tree-operations/paste/hooks.js +49 -46
- package/dist/tree-operations/paste/line-ending.d.ts +11 -0
- package/dist/tree-operations/paste/line-ending.js +32 -0
- package/dist/tree-operations/paste/list-absorb.d.ts +8 -11
- package/dist/tree-operations/paste/list-absorb.js +17 -19
- package/dist/tree-operations/paste/list-break-out.d.ts +12 -11
- package/dist/tree-operations/paste/list-break-out.js +24 -32
- package/dist/tree-operations/paste/parent-scope.d.ts +8 -12
- package/dist/tree-operations/paste/parent-scope.js +9 -16
- package/dist/tree-operations/paste/paste-deps.d.ts +14 -9
- package/dist/tree-operations/paste/paste-replacement.d.ts +20 -3
- package/dist/tree-operations/paste/paste-replacement.js +73 -42
- package/dist/tree-operations/paste/paste-transforms.d.ts +5 -7
- package/dist/tree-operations/paste/paste-transforms.js +18 -27
- package/dist/tree-operations/paste/replacement-parse.d.ts +19 -0
- package/dist/tree-operations/paste/replacement-parse.js +24 -0
- package/dist/tree-operations/paste/strategy.d.ts +1 -1
- package/dist/tree-operations/paste/strategy.js +1 -1
- package/dist/tree-operations/paste/table-slice.js +15 -10
- package/dist/tree-operations/paste-surfaces.d.ts +18 -29
- package/dist/tree-operations/paste-surfaces.js +14 -10
- package/dist/tree-operations/path-mutate.d.ts +7 -8
- package/dist/tree-operations/path-mutate.js +4 -4
- package/dist/tree-operations/reorder-unit.d.ts +4 -5
- package/dist/tree-operations/reorder-unit.js +5 -6
- package/dist/tree-operations/reorder.d.ts +7 -9
- package/dist/tree-operations/reorder.js +39 -40
- package/dist/tree-operations/settle.d.ts +42 -53
- package/dist/tree-operations/settle.js +241 -162
- package/dist/tree-operations/sharing.d.ts +5 -5
- package/dist/tree-operations/splice-many.d.ts +5 -5
- package/dist/tree-operations/splice-many.js +5 -5
- package/dist/tree-operations/stored-as.d.ts +13 -0
- package/dist/tree-operations/stored-as.js +48 -0
- package/dist/tree-operations/structural-change.d.ts +11 -15
- package/dist/tree-operations/structural-change.js +10 -14
- package/dist/tree-operations/structural-suffix.d.ts +22 -0
- package/dist/tree-operations/structural-suffix.js +61 -0
- package/dist/tree-operations/sub-table-copy.d.ts +7 -0
- package/dist/tree-operations/sub-table-copy.js +40 -36
- package/dist/tree-operations/table-grid-clipboard.d.ts +2 -7
- package/dist/tree-operations/table-grid-clipboard.js +11 -36
- package/dist/tree-operations/table-mutations.d.ts +7 -3
- package/dist/tree-operations/table-mutations.js +45 -9
- package/dist/tree-operations/unshare.d.ts +11 -15
- package/dist/tree-operations/unshare.js +15 -17
- package/dist/undo/manager.js +1 -1
- package/dist/undo/types.d.ts +2 -2
- package/docs/guide/consumer-guide.md +329 -205
- package/docs/guide/directives.md +7 -7
- package/docs/guide/plugin-api.md +267 -183
- package/docs/guide/plugin-guide.md +341 -235
- package/docs/guide/plugin-testing.md +96 -82
- package/package.json +17 -21
- package/dist/ambient/ambient-cursor.d.ts +0 -41
- package/dist/ambient/ambient-cursor.js +0 -120
- package/dist/components/blocks/code/code-paste.d.ts +0 -24
- package/dist/components/blocks/code/code-paste.js +0 -21
- package/dist/components/blocks/table/cell-clipboard.d.ts +0 -33
- package/dist/components/blocks/table/cell-clipboard.js +0 -67
- package/dist/components/blocks/text/hidden-suffix.d.ts +0 -10
- package/dist/components/blocks/text/hidden-suffix.js +0 -15
- package/dist/components/blocks/text/link-source-bytes.d.ts +0 -37
- package/dist/components/image/image-source-bytes.d.ts +0 -15
- package/dist/components/image/image-source-bytes.js +0 -80
- package/dist/cursor/content-offsets.d.ts +0 -29
- package/dist/cursor/content-offsets.js +0 -151
- package/dist/cursor/reveal-anchor.d.ts +0 -30
- package/dist/cursor/reveal-anchor.js +0 -31
- package/dist/invariants/split-landing.d.ts +0 -8
- package/dist/invariants/split-landing.js +0 -15
- package/dist/selection/double-click-trim.d.ts +0 -17
- package/dist/selection/double-click-trim.js +0 -57
- package/dist/tree-operations/list/terminator.d.ts +0 -17
- package/dist/tree-operations/list/terminator.js +0 -60
- package/dist/tree-operations/paste/replace-block-at-parent.d.ts +0 -29
- package/dist/tree-operations/paste/replace-block-at-parent.js +0 -75
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
import '../styles/editor.css';
|
|
4
4
|
import type { BlockComponent } from '../block-component';
|
|
5
5
|
import type { AnyBlockKind, Document } from '../core/nodes';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
EditorProps,
|
|
8
|
+
EditorInstance,
|
|
9
|
+
EditorDiagnostics,
|
|
10
|
+
InsertMarkdownOptions
|
|
11
|
+
} from '../editor-props';
|
|
7
12
|
import type { EditorEvents } from '../editor-events';
|
|
8
13
|
import {
|
|
9
14
|
BLOCK_EDIT_KEY,
|
|
@@ -16,57 +21,39 @@
|
|
|
16
21
|
type BlockElLookup,
|
|
17
22
|
type DocumentGetter,
|
|
18
23
|
type EditorDoc,
|
|
19
|
-
type LinkReferenceResolverRef,
|
|
20
24
|
type EditorPolicies,
|
|
21
25
|
type EditorServices,
|
|
22
26
|
type PluginEditorLookup,
|
|
23
27
|
type ResolveImageUrl,
|
|
24
28
|
type ResolveLinkUrl
|
|
25
29
|
} from '../editor-keys';
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
clippingAncestors,
|
|
34
|
-
userScrollportFor,
|
|
35
|
-
type UserScrollport
|
|
36
|
-
} from '../cursor/scroll-ancestors';
|
|
37
|
-
import { createScrollport, type Scrollport } from '../cursor/scrollport';
|
|
38
|
-
import { createDeadSpaceCaret } from '../selection/dead-space-caret';
|
|
39
|
-
import { resetForPointerDown } from '../selection/cross-block/pointer';
|
|
40
|
-
import { installDragListener } from '../selection/drag-pointer';
|
|
30
|
+
import { createCaretMemory } from '../cursor/caret-memory';
|
|
31
|
+
import { docPathFrom } from '../cursor/coordinate-spaces';
|
|
32
|
+
import { createAutoPairRecord } from './blocks/text/auto-pair-record';
|
|
33
|
+
import { createScrollOwner } from '../cursor/scroll-owner';
|
|
34
|
+
import { createScrollHostResolution } from './editor-root-scroll-host';
|
|
35
|
+
import { installSelectionDrop, type DropCaretRect } from '../selection/selection-drop';
|
|
41
36
|
import { createContentVersion } from '../reactivity/content-version.svelte';
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
37
|
+
import { useRootWindowing } from '../reactivity/use-container-windowing.svelte';
|
|
38
|
+
import { createListTree } from '../reactivity/list-tree';
|
|
39
|
+
import { createLayoutState } from '../reactivity/layout-state.svelte';
|
|
40
|
+
import { refSlotsOver, replaceRefs } from '../reactivity/publish-ref.svelte';
|
|
41
|
+
import { componentAt, type ChildList } from '../reactivity/child-list';
|
|
44
42
|
import { createSelectionState } from '../selection/selection-state.svelte';
|
|
43
|
+
import { coverRange, rangeCoverage } from '../selection/range-coverage';
|
|
45
44
|
import { createSelectionDescription } from '../selection/selection-description';
|
|
46
|
-
import {
|
|
47
|
-
BLOCK_ACTIONS_LABEL,
|
|
48
|
-
BLOCK_MENU_LABEL,
|
|
49
|
-
EDITOR_LABEL,
|
|
50
|
-
movedBlockToPosition
|
|
51
|
-
} from '../a11y-strings';
|
|
45
|
+
import { EDITOR_LABEL } from '../a11y-strings';
|
|
52
46
|
import TailInsert from './TailInsert.svelte';
|
|
53
|
-
import BlockMenu
|
|
54
|
-
|
|
55
|
-
insertMenuEntries,
|
|
56
|
-
insertSnippets,
|
|
57
|
-
type MenuEntry
|
|
58
|
-
} from './menu/BlockMenu.svelte';
|
|
59
|
-
import { runClipboardAction, type ClipboardAction } from './menu/clipboard-actions';
|
|
60
|
-
import { blockContextActionsFor, type BlockContextAction } from '../schema/context-actions';
|
|
61
|
-
import { isProseBackground, registerDefaultContextActions } from './menu/default-context-actions';
|
|
47
|
+
import BlockMenu from './menu/BlockMenu.svelte';
|
|
48
|
+
import { createMenuPresence } from './menu/menu-presence.svelte';
|
|
62
49
|
import type { EditorSelection } from '../selection/primitives';
|
|
63
50
|
import { createWidgetSelectionState } from './image/widget-selection-state.svelte';
|
|
64
|
-
import {
|
|
51
|
+
import { imageAtTarget } from './image/image-edit-commit';
|
|
52
|
+
import type { SelectedWidgetHandle } from '../selection/primitives';
|
|
65
53
|
import { assignIds } from '../block-id';
|
|
66
|
-
import {
|
|
67
|
-
import { blockNodeAt
|
|
54
|
+
import { createDocumentSwap, initDocument } from './editor-root-document-swap';
|
|
55
|
+
import { blockNodeAt } from '../tree-operations/node-primitives';
|
|
68
56
|
import { serialize } from '../core/serializer';
|
|
69
|
-
import { parse } from '../core/parser';
|
|
70
57
|
import { defaultLinkActivation } from '../core/url-policy';
|
|
71
58
|
import { advanceSignatureEpoch, lrdMapCouldChange } from './lrd-map-gate';
|
|
72
59
|
import {
|
|
@@ -83,21 +70,14 @@
|
|
|
83
70
|
import { createDecorationEngine } from '../decorations/decoration-state.svelte';
|
|
84
71
|
import type { DecorationRegistry } from '../decorations/types';
|
|
85
72
|
import { createEditorRects, type EditorRects } from '../editor-rects';
|
|
73
|
+
import { createCaretLanding } from '../selection/caret-landing';
|
|
86
74
|
import { installReorderDrag } from '../editor-actions/reorder-drag';
|
|
87
75
|
import { createPasteCoordinator } from '../editor-actions/paste-coordinator';
|
|
88
76
|
import { createOperationsLog } from '../debug/operations-log';
|
|
89
|
-
import {
|
|
90
|
-
import { buildDiagnosticsReport } from '../debug/diagnostics-report';
|
|
91
|
-
import {
|
|
92
|
-
enableInteractionTrace,
|
|
93
|
-
disableInteractionTrace,
|
|
94
|
-
isInteractionTraceEnabled,
|
|
95
|
-
interactionTraceSnapshot
|
|
96
|
-
} from '../debug/interaction-trace';
|
|
77
|
+
import { createEditorDiagnostics } from '../debug/editor-diagnostics';
|
|
97
78
|
import { readCurrentSelection } from '../selection/native-bridge';
|
|
98
|
-
import {
|
|
99
|
-
import {
|
|
100
|
-
import { createCaretRestore } from '../selection/caret-restore';
|
|
79
|
+
import { createCaretRestore, type CaretRestoreDeps } from '../selection/caret-restore';
|
|
80
|
+
import { createRoundTripRestore } from '../selection/round-trip-restore';
|
|
101
81
|
import { createCrossBlockHandlers } from '../selection/cross-block/dispatch';
|
|
102
82
|
import { createCrossBlockCommands } from '../selection/cross-block/format-toggle';
|
|
103
83
|
import { normalizeKeybindingOverrides } from '../schema/keybinding-overrides';
|
|
@@ -105,17 +85,24 @@
|
|
|
105
85
|
import { createEditorRootClipboard } from './editor-root-clipboard';
|
|
106
86
|
import { createModeFlip } from './editor-root-mode-flip';
|
|
107
87
|
import { createFocusAttribution } from './editor-root-focus';
|
|
88
|
+
import { createRootGestures } from './editor-root-gestures';
|
|
89
|
+
import { createRootMenus, type BlockMenuModel } from './editor-root-menus';
|
|
90
|
+
import { createFocusedSurface } from './editor-root-focused-surface';
|
|
91
|
+
import type { EditorTestSurface } from './editor-root-test-surface';
|
|
108
92
|
import {
|
|
109
93
|
installHeaderSlotCompensation,
|
|
110
94
|
installTypeScaleProbe,
|
|
111
95
|
installViewportHeightWatcher,
|
|
112
96
|
installWidthWatcher
|
|
113
97
|
} from './editor-root-geometry';
|
|
98
|
+
import { createSelectionAnnouncer } from '../selection/selection-announcer';
|
|
99
|
+
import { createKindCue } from './kind-cue.svelte';
|
|
114
100
|
import {
|
|
115
|
-
installDoubleClickWordSelect,
|
|
116
101
|
installEditorBlurAnnouncer,
|
|
117
102
|
installModActiveTracker,
|
|
103
|
+
installRevealAnchorRelease,
|
|
118
104
|
installSelectionChangeBridge,
|
|
105
|
+
installUndoStepEnd,
|
|
119
106
|
onRoot,
|
|
120
107
|
removeAll
|
|
121
108
|
} from './editor-root-listeners';
|
|
@@ -132,37 +119,38 @@
|
|
|
132
119
|
isCommandActiveById,
|
|
133
120
|
runCommandById,
|
|
134
121
|
type CommandDispatchContext,
|
|
135
|
-
type CommandErrorSink
|
|
136
|
-
type KindCommandTarget
|
|
122
|
+
type CommandErrorSink
|
|
137
123
|
} from '../schema/block-commands';
|
|
138
124
|
import type { AnyCommandId } from '../schema/command-id';
|
|
139
125
|
import { installPlugins, normalizePluginEntries } from '../schema/plugin-install';
|
|
140
|
-
import {
|
|
141
|
-
activationFor,
|
|
142
|
-
everyInstalledPlugin,
|
|
143
|
-
kindEnablementFor
|
|
144
|
-
} from '../schema/plugin-activation';
|
|
126
|
+
import { activationFor, everyInstalledPlugin } from '../schema/plugin-activation';
|
|
145
127
|
import { createEditorPluginContexts, mintEditorId } from '../schema/plugin-editor-context';
|
|
146
|
-
import {
|
|
128
|
+
import { insertCatalogue, type InsertEntry } from '../schema/insert-catalogue';
|
|
129
|
+
import { createRegistryView, type KindEnablement } from '../schema/registry-view';
|
|
130
|
+
import type { Reading } from '../schema/reading';
|
|
131
|
+
import { hidesDelimitersAtCaret, type PresentationMode } from '../presentation-mode';
|
|
147
132
|
import BlockList from './BlockList.svelte';
|
|
148
133
|
import SearchBar from './SearchBar.svelte';
|
|
134
|
+
import SelectionToolbar from './menu/SelectionToolbar.svelte';
|
|
149
135
|
import ImageOverlayHost from './image/ImageOverlayHost.svelte';
|
|
150
136
|
import LinkCardHost from './link-card/LinkCardHost.svelte';
|
|
137
|
+
import InlineMenuHost from './menu/InlineMenuHost.svelte';
|
|
138
|
+
import { createInlineMenuState } from '../inline-menu/inline-menu-state.svelte';
|
|
139
|
+
import type { InlineMenuRegistry } from '../inline-menu/types';
|
|
140
|
+
import { createInlineRangeCommit } from '../editor-actions/inline-range-commit';
|
|
141
|
+
import { replaceBlockRaw } from '../editor-actions/block-edit-core';
|
|
151
142
|
import { createLinkCardState } from './link-card/link-card-state.svelte';
|
|
152
|
-
import { LINK_ELEMENT_SELECTOR, resolveLinkAtPoint } from './blocks/text/link-at-point';
|
|
153
143
|
import { runStartupInvariantChecks } from '../invariants/install';
|
|
154
144
|
import { assertInvariant } from '../assert';
|
|
155
145
|
import { checkMarkerCssParity } from '../invariants/marker-css-parity';
|
|
156
|
-
import {
|
|
157
|
-
import {
|
|
146
|
+
import { registerEditorBuiltIns } from './editor-built-ins';
|
|
147
|
+
import { blockContentElAt } from './block-el-lookup';
|
|
158
148
|
|
|
159
|
-
|
|
160
|
-
bootstrapCodeLanguages();
|
|
161
|
-
registerDefaultContextActions();
|
|
149
|
+
registerEditorBuiltIns();
|
|
162
150
|
runStartupInvariantChecks();
|
|
163
151
|
|
|
164
|
-
// `__registryEnablement` is
|
|
165
|
-
// public EditorProps
|
|
152
|
+
// `__registryEnablement` is for tests only; the intersection type keeps it off the
|
|
153
|
+
// public EditorProps.
|
|
166
154
|
let {
|
|
167
155
|
source = '',
|
|
168
156
|
resolveImageUrl,
|
|
@@ -176,77 +164,46 @@
|
|
|
176
164
|
blockDragHandles = true,
|
|
177
165
|
searchBar = true,
|
|
178
166
|
searchBarAnchor,
|
|
167
|
+
selectionToolbar = true,
|
|
179
168
|
keybindings,
|
|
180
169
|
theme = 'dark',
|
|
181
170
|
presentationMode = 'source',
|
|
182
171
|
scrollMode = 'self',
|
|
183
172
|
plugins,
|
|
173
|
+
syntax,
|
|
184
174
|
__registryEnablement
|
|
185
175
|
}: EditorProps & { __registryEnablement?: KindEnablement } = $props();
|
|
186
176
|
|
|
187
|
-
//
|
|
188
|
-
// windowing scopes' derived would make `scrollMode` a dependency of the hottest path.
|
|
177
|
+
// Read once: `scrollMode` is fixed at mount, and a live read would join windowing's hottest path.
|
|
189
178
|
// svelte-ignore state_referenced_locally
|
|
190
179
|
const hostScroll = scrollMode === 'host';
|
|
191
180
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
function resolveHost(): void {
|
|
199
|
-
if (hostResolved || !editorEl) return;
|
|
200
|
-
resolvedScrollHost = userScrollportFor(editorEl);
|
|
201
|
-
resolvedClipBounds = clippingAncestors(editorEl);
|
|
202
|
-
hostResolved = true;
|
|
203
|
-
}
|
|
204
|
-
/** What a drag autoscrolls: the root in self mode, the nearest scrollable ancestor
|
|
205
|
-
* in host mode. Null only before the root mounts. */
|
|
206
|
-
function getScrollHost(): UserScrollport | null {
|
|
207
|
-
if (!hostScroll) return editorEl ?? null;
|
|
208
|
-
resolveHost();
|
|
209
|
-
return resolvedScrollHost;
|
|
210
|
-
}
|
|
211
|
-
/** Every clipping ancestor; their intersection with the viewport is what a reveal
|
|
212
|
-
* must land inside. */
|
|
213
|
-
function getClipBounds(): HTMLElement[] {
|
|
214
|
-
if (!hostScroll) return [];
|
|
215
|
-
resolveHost();
|
|
216
|
-
return resolvedClipBounds;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// The scrollport every windowing scope measures and writes, over the SAME scroller the
|
|
220
|
-
// autoscroll seam resolves — so the mode picks the target and nothing downstream branches
|
|
221
|
-
// on it. Memoized with that resolution.
|
|
222
|
-
let scrollport: Scrollport | null = null;
|
|
223
|
-
function getScrollport(): Scrollport | null {
|
|
224
|
-
if (!scrollport) {
|
|
225
|
-
const target = getScrollHost();
|
|
226
|
-
if (target) scrollport = createScrollport(target);
|
|
227
|
-
}
|
|
228
|
-
return scrollport;
|
|
229
|
-
}
|
|
181
|
+
const { getScrollHost, getClipBounds } = createScrollHostResolution({
|
|
182
|
+
get editorEl() {
|
|
183
|
+
return editorEl;
|
|
184
|
+
},
|
|
185
|
+
hostScroll
|
|
186
|
+
});
|
|
230
187
|
|
|
231
|
-
//
|
|
232
|
-
// for the seed grammar. Set-once by contract — a later prop change is ignored.
|
|
188
|
+
// Installed before the first parse of `source`; a later change to `plugins` is ignored.
|
|
233
189
|
// svelte-ignore state_referenced_locally
|
|
234
190
|
const pluginEntries = plugins?.length ? normalizePluginEntries(plugins) : undefined;
|
|
235
191
|
if (pluginEntries) installPlugins(pluginEntries.plugins);
|
|
236
192
|
|
|
237
|
-
// The
|
|
238
|
-
//
|
|
193
|
+
// The editor activates exactly the plugins it lists; with no `plugins` prop, every installed
|
|
194
|
+
// plugin stays active.
|
|
239
195
|
const activePlugins = pluginEntries
|
|
240
196
|
? activationFor(pluginEntries.plugins.map((p) => p.name))
|
|
241
197
|
: everyInstalledPlugin;
|
|
242
198
|
|
|
243
199
|
const overridesMap = $derived(normalizeKeybindingOverrides(keybindings));
|
|
244
200
|
|
|
245
|
-
// The one mode every
|
|
246
|
-
// events), and the seam an effective-vs-requested divergence would land in.
|
|
201
|
+
// The one mode every reader reports, and the one place effective and requested could differ.
|
|
247
202
|
const effectiveMode = $derived(presentationMode);
|
|
248
|
-
//
|
|
249
|
-
//
|
|
203
|
+
// Set only while a mode switch commits the outgoing mode's pending edits, so they land in the
|
|
204
|
+
// mode they were typed in.
|
|
205
|
+
let outgoingMode = $state<PresentationMode | null>(null);
|
|
206
|
+
// Replace is an edit, so reading mode never offers it (the commit refuses the write anyway).
|
|
250
207
|
const canReplace = $derived(effectiveMode !== 'reading');
|
|
251
208
|
|
|
252
209
|
const resolveImageUrlImpl: ResolveImageUrl = (u) => (resolveImageUrl ? resolveImageUrl(u) : u);
|
|
@@ -258,95 +215,84 @@
|
|
|
258
215
|
|
|
259
216
|
// ── State ───────────────────────────────────────────────────────────
|
|
260
217
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
// Only the empty source parses to zero blocks — a blank line is a block of its
|
|
269
|
-
// own — so there is no authored ending to inherit and LF is the whole answer.
|
|
270
|
-
d.children.push(emptyParagraph('', '\n'));
|
|
271
|
-
}
|
|
272
|
-
for (const child of d.children) {
|
|
273
|
-
ensureEditableContainers(child);
|
|
274
|
-
}
|
|
275
|
-
const refMap = buildLinkReferenceMap(d.children);
|
|
276
|
-
return { doc: d, resolver: refMap.resolve, signature: refMap.signature };
|
|
277
|
-
}
|
|
218
|
+
// This editor's view of the global registries; `__registryEnablement` narrows it in tests.
|
|
219
|
+
// svelte-ignore state_referenced_locally
|
|
220
|
+
const registryView = createRegistryView({
|
|
221
|
+
plugins: pluginEntries ? activePlugins : undefined,
|
|
222
|
+
isEnabled: __registryEnablement,
|
|
223
|
+
syntax
|
|
224
|
+
});
|
|
278
225
|
|
|
279
|
-
//
|
|
280
|
-
// cannot be $derived: snapshot at mount, re-sync via the $effect below.
|
|
226
|
+
// Plain state, not $derived: structural edits write `doc` and `blockIds` directly.
|
|
281
227
|
// svelte-ignore state_referenced_locally
|
|
282
|
-
const initial = initDocument(source);
|
|
283
|
-
let doc = $state
|
|
228
|
+
const initial = initDocument(source, registryView.grammar);
|
|
229
|
+
let doc: Document = $state(initial.doc);
|
|
284
230
|
// svelte-ignore state_referenced_locally
|
|
285
231
|
let blockIds = $state<string[]>(assignIds(doc.children));
|
|
286
|
-
// Bumped by every
|
|
287
|
-
// decoration engine's `editEpoch`
|
|
232
|
+
// Bumped by every path that writes bytes. Inline widgets derive on it directly, and the
|
|
233
|
+
// decoration engine's `editEpoch` follows it a tick later.
|
|
288
234
|
const contentVersion = createContentVersion();
|
|
289
235
|
let currentResolver = $state<LinkReferenceResolver>(initial.resolver);
|
|
290
236
|
let currentSignature = $state<string>(initial.signature);
|
|
291
237
|
// Reference-bearing render memos key on this instead of the whole (~MB) signature.
|
|
292
238
|
let signatureEpoch = $state<number>(0);
|
|
293
|
-
/**
|
|
294
|
-
* through their deps
|
|
295
|
-
const
|
|
296
|
-
|
|
239
|
+
/** This editor's one reading context, shared by the block components (through context) and
|
|
240
|
+
* the action bundles (through their deps), so a post-commit rebuild reaches both. */
|
|
241
|
+
const reading: Reading = {
|
|
242
|
+
grammar: registryView.grammar,
|
|
243
|
+
get resolver(): LinkReferenceResolver {
|
|
297
244
|
return currentResolver;
|
|
298
245
|
},
|
|
299
|
-
get
|
|
246
|
+
get resolverSignature(): string {
|
|
300
247
|
return currentSignature;
|
|
301
248
|
},
|
|
302
|
-
get
|
|
249
|
+
get resolverEpoch(): number {
|
|
303
250
|
return signatureEpoch;
|
|
304
|
-
}
|
|
251
|
+
},
|
|
252
|
+
mode: () => outgoingMode ?? effectiveMode,
|
|
253
|
+
hidesDelimitersAtCaret: () => hidesDelimitersAtCaret(outgoingMode ?? effectiveMode)
|
|
305
254
|
};
|
|
306
|
-
//
|
|
255
|
+
// The root list's child component refs, plain rather than `$state` (see `refSlotsOver`).
|
|
307
256
|
const blockRefs: (BlockComponent | undefined)[] = [];
|
|
308
257
|
const blockRefSlots = refSlotsOver(blockRefs);
|
|
309
258
|
let editorEl: HTMLDivElement | undefined = $state();
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
// anchor already inherits.
|
|
259
|
+
// A portaled search bar inside a themed host skips the theme class, whose defaults would
|
|
260
|
+
// shadow the host's tokens.
|
|
313
261
|
const inThemedScope = $derived(!!editorEl?.closest('.aragonite-editor-theme'));
|
|
314
262
|
let headerEl: HTMLDivElement | undefined = $state();
|
|
315
263
|
let typeScaleProbeEl: HTMLDivElement | undefined = $state();
|
|
264
|
+
let widthProbeEl: HTMLDivElement | undefined = $state();
|
|
316
265
|
const undoManager = createUndoManager();
|
|
317
266
|
const sharing = createSharingState();
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
// exact lifetime, so every door that invalidates the arrival side drops them by construction.
|
|
321
|
-
const pendingMarks = createPendingMarksState();
|
|
322
|
-
const edgeAffinity = createEdgeAffinityState({ onInvalidate: pendingMarks.reset });
|
|
323
|
-
const revealAnchor = createRevealAnchorState();
|
|
267
|
+
const caretMemory = createCaretMemory();
|
|
268
|
+
const autoPairs = createAutoPairRecord();
|
|
324
269
|
const operationsLog = createOperationsLog();
|
|
325
270
|
const events = createEditorEvents();
|
|
326
|
-
// getSelection is function-hoisted below
|
|
271
|
+
// getSelection is function-hoisted below, so every read here is a fresh snapshot.
|
|
272
|
+
const selectionAnnouncer = createSelectionAnnouncer({
|
|
273
|
+
read: () => getSelection(),
|
|
274
|
+
emit: (selection) => events.emit('selectionChange', selection)
|
|
275
|
+
});
|
|
327
276
|
const selectionState = createSelectionState({
|
|
328
|
-
onChange: () => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
// both live, and every dispatch keyed on "a widget is selected" would answer for
|
|
332
|
-
// the document-wide selection the user is looking at.
|
|
333
|
-
if (selectionState.isCrossBlock) widgetSelection.clear();
|
|
334
|
-
events.emit('selectionChange', getSelection());
|
|
277
|
+
onChange: ({ placementOnly }) => {
|
|
278
|
+
if (placementOnly) selectionAnnouncer.announceIfMoved();
|
|
279
|
+
else selectionAnnouncer.announce();
|
|
335
280
|
},
|
|
336
|
-
getDoc: () => doc
|
|
281
|
+
getDoc: () => doc,
|
|
282
|
+
// Read off the live document, since an image's own commits move its end byte.
|
|
283
|
+
widgetSpan: (target) => imageAtTarget(doc, target, reading)
|
|
337
284
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
// it fires bails on the empty range before it reaches the channel.
|
|
344
|
-
selectionState.batch(() => {
|
|
345
|
-
selectionState.clear();
|
|
346
|
-
selectionState.announceSelection();
|
|
347
|
-
});
|
|
348
|
-
}
|
|
285
|
+
// With no live range this reads no document bytes, so a keystroke at a caret costs nothing here.
|
|
286
|
+
const coverage = $derived.by(() => {
|
|
287
|
+
const { anchor, focus } = selectionState;
|
|
288
|
+
if (!selectionState.isCustomRendered || !anchor || !focus) return null;
|
|
289
|
+
return rangeCoverage(doc, coverRange(doc, anchor, focus));
|
|
349
290
|
});
|
|
291
|
+
const widgetSelection = createWidgetSelectionState(selectionState);
|
|
292
|
+
const selectedWidget: SelectedWidgetHandle = {
|
|
293
|
+
range: () => selectionState.widgetRange(),
|
|
294
|
+
clear: widgetSelection.clear
|
|
295
|
+
};
|
|
350
296
|
|
|
351
297
|
let selectionDescription = $derived(
|
|
352
298
|
selectionState.isCrossBlock && selectionState.anchor && selectionState.focus
|
|
@@ -354,14 +300,21 @@
|
|
|
354
300
|
: ''
|
|
355
301
|
);
|
|
356
302
|
|
|
357
|
-
//
|
|
358
|
-
// the
|
|
359
|
-
let
|
|
303
|
+
// The edit live region, its own: sharing `selectionDescription`'s would drop a move's
|
|
304
|
+
// announcement. Only the undo controller speaks here, from a commit that wrote.
|
|
305
|
+
let editAnnouncement = $state('');
|
|
306
|
+
const announceEdit = async (message: string) => {
|
|
307
|
+
// Clear first: Svelte skips the DOM write on a ===-equal assignment, which drops
|
|
308
|
+
// the second of two identical announcements.
|
|
309
|
+
editAnnouncement = '';
|
|
310
|
+
await tick();
|
|
311
|
+
editAnnouncement = message;
|
|
312
|
+
};
|
|
360
313
|
|
|
361
|
-
//
|
|
362
|
-
// component.
|
|
314
|
+
// One element each for the whole editor, so dragging costs nothing per mounted block.
|
|
363
315
|
let reorderGhost = $state<{ clientX: number; clientY: number; label: string } | null>(null);
|
|
364
316
|
let reorderLine = $state<{ left: number; top: number; width: number } | null>(null);
|
|
317
|
+
let dropCaret = $state<DropCaretRect | null>(null);
|
|
365
318
|
|
|
366
319
|
$effect(() => {
|
|
367
320
|
const dispose = events.on('edit', (e) => {
|
|
@@ -370,9 +323,8 @@
|
|
|
370
323
|
path: e.path,
|
|
371
324
|
detail: ('detail' in e ? e.detail : undefined) ?? {}
|
|
372
325
|
});
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
// a real signature change: one per edit re-renders every block that read it.
|
|
326
|
+
// A fresh resolver only on a real signature change: one per edit would re-render
|
|
327
|
+
// every block that read it.
|
|
376
328
|
if (lrdMapCouldChange(doc, e)) {
|
|
377
329
|
const newMap = buildLinkReferenceMap(doc.children);
|
|
378
330
|
const next = advanceSignatureEpoch(currentSignature, signatureEpoch, newMap.signature);
|
|
@@ -386,10 +338,8 @@
|
|
|
386
338
|
return () => dispose();
|
|
387
339
|
});
|
|
388
340
|
|
|
389
|
-
// The one
|
|
390
|
-
//
|
|
391
|
-
// pause behind the bytes; the tick keeps a source off a half-applied tree, and the
|
|
392
|
-
// no-source skip keeps an undecorated editor at zero keystroke work (perf:check).
|
|
341
|
+
// The one place `editEpoch` is bumped, off the content version because the `edit` event
|
|
342
|
+
// batches a typing burst; the tick keeps decoration sources off a half-applied tree.
|
|
393
343
|
let notifiedVersion = contentVersion.read();
|
|
394
344
|
$effect(() => {
|
|
395
345
|
const version = contentVersion.read();
|
|
@@ -398,265 +348,73 @@
|
|
|
398
348
|
if (decorationEngine.sourceCount > 0) void tick().then(() => decorationEngine.notifyEdit());
|
|
399
349
|
});
|
|
400
350
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
let lastSource = source;
|
|
409
|
-
$effect(() => {
|
|
410
|
-
if (source !== lastSource) {
|
|
411
|
-
// A pending typing batch addresses the OUTGOING document, so it flushes
|
|
412
|
-
// while its path still resolves; left armed, the timer fires note A's path
|
|
413
|
-
// against note B.
|
|
414
|
-
controller.flushDebouncedCheckpoint();
|
|
415
|
-
lastSource = source;
|
|
416
|
-
const reset = initDocument(source);
|
|
417
|
-
doc = reset.doc;
|
|
418
|
-
contentVersion.bump();
|
|
351
|
+
const documentSwap = createDocumentSwap({
|
|
352
|
+
grammar: registryView.grammar,
|
|
353
|
+
// Built below; a swap runs post-init, so the closures read past the TDZ.
|
|
354
|
+
flushDebouncedCheckpoint: () => controller.flushDebouncedCheckpoint(),
|
|
355
|
+
noteTreeSwap: () => caretLanding.noteTreeSwap(),
|
|
356
|
+
adoptDocument: (next) => {
|
|
357
|
+
doc = next;
|
|
419
358
|
blockIds = assignIds(doc.children);
|
|
359
|
+
},
|
|
360
|
+
bumpContentVersion: contentVersion.bump,
|
|
361
|
+
clearBlockRefs: () => {
|
|
420
362
|
blockRefs.length = 0;
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
// The resolver refreshes unconditionally — the old one closes over the
|
|
436
|
-
// swapped-out doc — but the epoch bumps only on a differing LRD signature.
|
|
437
|
-
const next = advanceSignatureEpoch(currentSignature, signatureEpoch, reset.signature);
|
|
438
|
-
currentResolver = reset.resolver;
|
|
363
|
+
},
|
|
364
|
+
get layout() {
|
|
365
|
+
return layout;
|
|
366
|
+
},
|
|
367
|
+
undoManager,
|
|
368
|
+
caretMemory,
|
|
369
|
+
closeMenus,
|
|
370
|
+
widgetSelection,
|
|
371
|
+
selection: selectionState,
|
|
372
|
+
// The counter bumps only when the link-reference signature differs; the resolver
|
|
373
|
+
// refreshes regardless.
|
|
374
|
+
adoptLinkReferences: (resolver, signature) => {
|
|
375
|
+
const next = advanceSignatureEpoch(currentSignature, signatureEpoch, signature);
|
|
376
|
+
currentResolver = resolver;
|
|
439
377
|
currentSignature = next.signature;
|
|
440
378
|
signatureEpoch = next.epoch;
|
|
441
|
-
}
|
|
379
|
+
},
|
|
380
|
+
events
|
|
442
381
|
});
|
|
443
382
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
383
|
+
// The equality check is required: `docs/design/editor.md` § Reactive state plumbing.
|
|
384
|
+
// svelte-ignore state_referenced_locally
|
|
385
|
+
let lastSource = source;
|
|
386
|
+
$effect(() => {
|
|
387
|
+
if (source === lastSource) return;
|
|
388
|
+
lastSource = source;
|
|
389
|
+
documentSwap.swapTo(source);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
/** Whether `node` is in the host's header; every "is this editor content" check asks here.
|
|
393
|
+
* Focus tracking uses `contains` instead, since the header counts as part of the editor. */
|
|
450
394
|
function isHostChrome(node: Node | null): boolean {
|
|
451
395
|
return !!node && !!headerEl && headerEl.contains(node);
|
|
452
396
|
}
|
|
453
397
|
|
|
454
|
-
// ── Dead-space caret ────────────────────────────────────────────────
|
|
455
|
-
|
|
456
|
-
// A click in the root's padding or below the last block places a caret rather than
|
|
457
|
-
// doing nothing. `getBlockComponent` is hoisted; the reset closure defers its reads.
|
|
458
|
-
const deadSpaceCaret = createDeadSpaceCaret({
|
|
459
|
-
getBlockComponent,
|
|
460
|
-
resetSelectionForClick: () =>
|
|
461
|
-
resetForPointerDown(selectionState, stickyColumn, edgeAffinity, false),
|
|
462
|
-
gapScope: {
|
|
463
|
-
getDoc: () => doc,
|
|
464
|
-
selection: selectionState,
|
|
465
|
-
getPresentationMode: () => effectiveMode
|
|
466
|
-
},
|
|
467
|
-
lastBlockIndex: () => doc.children.length - 1,
|
|
468
|
-
revealBlock: (index) => revealPath([index])
|
|
469
|
-
});
|
|
470
|
-
|
|
471
398
|
// ── Block menu ──────────────────────────────────────────────────────
|
|
472
399
|
|
|
473
|
-
// Opened by
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
anchor: () => { x: number; y: number };
|
|
480
|
-
items: MenuEntry[];
|
|
481
|
-
label: string;
|
|
482
|
-
pick: (id: string) => void;
|
|
483
|
-
} | null>(null);
|
|
484
|
-
|
|
485
|
-
// Open/close transitions only, never the mount, so a subscriber's first news is a real menu.
|
|
400
|
+
// Opened by a right-click on prose with nothing selected; over a selection the host's
|
|
401
|
+
// formatting popover stays in charge, and tables run their own cell menu.
|
|
402
|
+
let blockMenu = $state<BlockMenuModel | null>(null);
|
|
403
|
+
|
|
404
|
+
// Emits on open/close transitions only, so a subscriber's first event is a real menu.
|
|
405
|
+
const menuPresence = createMenuPresence();
|
|
486
406
|
let menuWasOpen = false;
|
|
487
407
|
$effect(() => {
|
|
488
|
-
const open =
|
|
408
|
+
const open = menuPresence.isOpen;
|
|
489
409
|
if (open === menuWasOpen) return;
|
|
490
410
|
menuWasOpen = open;
|
|
491
411
|
events.emit('menuChange', open);
|
|
492
412
|
});
|
|
493
413
|
|
|
494
|
-
|
|
495
|
-
const rect = el.getBoundingClientRect();
|
|
496
|
-
const dx = point.x - rect.left;
|
|
497
|
-
const dy = point.y - rect.top;
|
|
498
|
-
return () => {
|
|
499
|
-
const now = el.getBoundingClientRect();
|
|
500
|
-
return { x: now.left + dx, y: now.top + dy };
|
|
501
|
-
};
|
|
502
|
-
}
|
|
414
|
+
// ── Link card ───────────────────────────────────────────────────────
|
|
503
415
|
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
// the browser's own menu (spelling, the platform's clipboard), as does a selection, whose
|
|
507
|
-
// affordance is the host's formatting popover. The margin shows nothing at all.
|
|
508
|
-
function onRootContextMenu(e: MouseEvent): void {
|
|
509
|
-
if (e.defaultPrevented || effectiveMode === 'reading' || !editorEl) return;
|
|
510
|
-
const target = e.target instanceof Element ? e.target : null;
|
|
511
|
-
if (!target || isHostChrome(target)) return;
|
|
512
|
-
e.preventDefault();
|
|
513
|
-
// The keyboard's contextmenu event (Windows fires it on the ContextMenu key's release)
|
|
514
|
-
// lands on the item the keydown's own menu already focused: that menu is the answer.
|
|
515
|
-
if (target.closest('.md-menu')) return;
|
|
516
|
-
const host = target.closest<HTMLElement>('.block-host[data-block-path]');
|
|
517
|
-
const path = pathOf(host);
|
|
518
|
-
if (!host || !path) return;
|
|
519
|
-
const point = { x: e.clientX, y: e.clientY };
|
|
520
|
-
const native = window.getSelection();
|
|
521
|
-
const selected = !!native && !native.isCollapsed && editorEl.contains(native.anchorNode);
|
|
522
|
-
// A selection's menu is the clipboard's, over the selection as it stands. So is prose's
|
|
523
|
-
// (the page's background), at a caret placed where the press landed; a nested block (a
|
|
524
|
-
// fence inside a list item) takes the same for now.
|
|
525
|
-
const node = path.length === 1 ? doc.children[path[0]] : undefined;
|
|
526
|
-
if (selected || !node || isProseBackground(node)) {
|
|
527
|
-
if (!selected) placeCaretAtPoint(e.clientX, e.clientY);
|
|
528
|
-
// Top-level prose is where a sibling block makes sense; a nested block or a selection
|
|
529
|
-
// gets the clipboard alone.
|
|
530
|
-
const insertAfter = !selected && node && isProseBackground(node) ? path[0] : null;
|
|
531
|
-
openClipboardMenu(point, host, insertAfter);
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
const index = path[0];
|
|
535
|
-
const actions = blockContextActionsFor(node, path);
|
|
536
|
-
if (actions.length === 0) return;
|
|
537
|
-
const ctx = {
|
|
538
|
-
node,
|
|
539
|
-
path,
|
|
540
|
-
deleteBlock: async () => {
|
|
541
|
-
await blockEdit.deleteBlock(index);
|
|
542
|
-
},
|
|
543
|
-
replaceRaw: async (raw: string) => {
|
|
544
|
-
await blockEdit.updateBlockContent(index, raw);
|
|
545
|
-
}
|
|
546
|
-
};
|
|
547
|
-
blockMenu = {
|
|
548
|
-
...point,
|
|
549
|
-
anchor: anchorOn(host, point),
|
|
550
|
-
items: actions.map(({ id, label, icon, danger }) => ({
|
|
551
|
-
id,
|
|
552
|
-
label,
|
|
553
|
-
icon: icon as MenuEntry['icon'],
|
|
554
|
-
danger
|
|
555
|
-
})),
|
|
556
|
-
label: BLOCK_ACTIONS_LABEL,
|
|
557
|
-
pick: (id) => {
|
|
558
|
-
blockMenu = null;
|
|
559
|
-
const action = actions.find((a: BlockContextAction) => a.id === id);
|
|
560
|
-
if (action) void action.run(ctx);
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// The editing surface a paste goes to: whatever editable holds focus inside the root.
|
|
566
|
-
function focusedEditable(): HTMLElement | null {
|
|
567
|
-
const active = document.activeElement;
|
|
568
|
-
return active instanceof HTMLElement && editorEl?.contains(active) && active.isContentEditable
|
|
569
|
-
? active
|
|
570
|
-
: null;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function clipboardRows(): MenuEntry[] {
|
|
574
|
-
const selected = !!focusedEditable() && !(window.getSelection()?.isCollapsed ?? true);
|
|
575
|
-
return [
|
|
576
|
-
{ id: 'clip.cut', label: 'Cut', icon: 'scissors', disabled: !selected },
|
|
577
|
-
{ id: 'clip.copy', label: 'Copy', icon: 'copy', disabled: !selected },
|
|
578
|
-
{ id: 'clip.paste', label: 'Paste', icon: 'clipboard' },
|
|
579
|
-
{ id: 'clip.paste-plain', label: 'Paste as plain text', icon: 'type' }
|
|
580
|
-
];
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function runClipboardRow(id: string): boolean {
|
|
584
|
-
if (!id.startsWith('clip.')) return false;
|
|
585
|
-
void runClipboardAction(id.slice('clip.'.length) as ClipboardAction, focusedEditable());
|
|
586
|
-
return true;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
/** `insertAfter` names the top-level block an "Insert block" flyout mints an empty sibling
|
|
590
|
-
* after; null leaves the menu to the clipboard rows alone. */
|
|
591
|
-
function openClipboardMenu(
|
|
592
|
-
point: { x: number; y: number },
|
|
593
|
-
anchorEl: Element,
|
|
594
|
-
insertAfter: number | null = null
|
|
595
|
-
): void {
|
|
596
|
-
const insert: MenuEntry[] =
|
|
597
|
-
insertAfter === null
|
|
598
|
-
? []
|
|
599
|
-
: [
|
|
600
|
-
{ id: 'sep', label: '', divider: true },
|
|
601
|
-
{ id: 'insert', label: 'Insert block', icon: 'plus', children: insertFlyoutEntries() }
|
|
602
|
-
];
|
|
603
|
-
blockMenu = {
|
|
604
|
-
...point,
|
|
605
|
-
anchor: anchorOn(anchorEl, point),
|
|
606
|
-
items: [...clipboardRows(), ...insert],
|
|
607
|
-
label: BLOCK_ACTIONS_LABEL,
|
|
608
|
-
pick: (id) => {
|
|
609
|
-
blockMenu = null;
|
|
610
|
-
if (runClipboardRow(id)) return;
|
|
611
|
-
const md = insertSnippets().get(id);
|
|
612
|
-
if (md && insertAfter !== null) void insertBlockAfter(insertAfter, md);
|
|
613
|
-
}
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
// The same two steps the tail's `+` takes: mint the empty paragraph, which lands the caret in
|
|
618
|
-
// it, then hand the snippet to the surface that now holds focus.
|
|
619
|
-
async function insertBlockAfter(index: number, md: string): Promise<void> {
|
|
620
|
-
await blockEdit.insertParagraph(index + 1, '');
|
|
621
|
-
insertMarkdown(md);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
function pathOf(host: HTMLElement | null): number[] | null {
|
|
625
|
-
const raw = host?.dataset.blockPath;
|
|
626
|
-
if (!raw) return null;
|
|
627
|
-
try {
|
|
628
|
-
const parsed: unknown = JSON.parse(raw);
|
|
629
|
-
return Array.isArray(parsed) && parsed.every((n) => typeof n === 'number') ? parsed : null;
|
|
630
|
-
} catch {
|
|
631
|
-
return null;
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
async function onTailPlus(button: HTMLElement): Promise<void> {
|
|
636
|
-
await blockEdit.insertParagraph(doc.children.length, '');
|
|
637
|
-
const rect = button.getBoundingClientRect();
|
|
638
|
-
const point = { x: rect.left, y: rect.bottom + 4 };
|
|
639
|
-
const snippets = insertSnippets();
|
|
640
|
-
blockMenu = {
|
|
641
|
-
...point,
|
|
642
|
-
anchor: anchorOn(button, point),
|
|
643
|
-
items: [...insertMenuEntries(), { id: 'sep', label: '', divider: true }, ...clipboardRows()],
|
|
644
|
-
label: BLOCK_MENU_LABEL,
|
|
645
|
-
pick: (id) => {
|
|
646
|
-
blockMenu = null;
|
|
647
|
-
if (runClipboardRow(id)) return;
|
|
648
|
-
const md = snippets.get(id);
|
|
649
|
-
if (md) insertMarkdown(md);
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
// ── Link card door ──────────────────────────────────────────────────
|
|
655
|
-
|
|
656
|
-
// The caret snapshot and the entry guards ride the STATE, not the callers, so entry path N+1
|
|
657
|
-
// cannot forget them. All three decline a cross-block range absolutely; on a same-block one
|
|
658
|
-
// they split by gesture: an unasked-for click must not interrupt it, the create gesture wraps
|
|
659
|
-
// it, and the chord has already resolved it against the construct it opens.
|
|
416
|
+
// The caret snapshot and entry checks live on the link card state so no entry path skips
|
|
417
|
+
// them. Each refuses a cross-block range; a click also refuses a selection, which create needs.
|
|
660
418
|
const linkCard = createLinkCardState({
|
|
661
419
|
onOpen: () => linkCardCaret.saveCurrent(),
|
|
662
420
|
canOpen: () => !selectionState.isCrossBlock && window.getSelection()?.isCollapsed !== false,
|
|
@@ -665,29 +423,21 @@
|
|
|
665
423
|
!selectionState.isCrossBlock && window.getSelection()?.isCollapsed === false
|
|
666
424
|
});
|
|
667
425
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
if (!path) return false;
|
|
673
|
-
const block = nodeAt(doc, path);
|
|
674
|
-
if (block === null || !isBlockNode(block)) return false;
|
|
675
|
-
const contentEl = getBlockElByPath(path);
|
|
676
|
-
if (!contentEl) return false;
|
|
677
|
-
const hit = resolveLinkAtPoint({ contentEl, block, path, linkRef: linkRefView });
|
|
678
|
-
if (!hit) return false;
|
|
679
|
-
return linkCard.open(hit.target);
|
|
426
|
+
function closeMenus(): void {
|
|
427
|
+
blockMenu = null;
|
|
428
|
+
inlineMenu.close();
|
|
429
|
+
linkCard.close();
|
|
680
430
|
}
|
|
681
431
|
|
|
682
|
-
//
|
|
432
|
+
// A menu opened in one mode offers that mode's edits, so a mode change closes every menu.
|
|
683
433
|
$effect(() => {
|
|
684
|
-
|
|
434
|
+
void effectiveMode;
|
|
435
|
+
untrack(closeMenus);
|
|
685
436
|
});
|
|
686
437
|
|
|
687
|
-
// ── Hidden-run class
|
|
438
|
+
// ── Hidden-run class check ──────────────────────────────────────────
|
|
688
439
|
|
|
689
|
-
// Once per mode change, the
|
|
690
|
-
// predicate cannot afford, so the two class vocabularies cannot drift silently.
|
|
440
|
+
// Once per mode change, so the getComputedStyle cost stays off the keystroke path.
|
|
691
441
|
$effect(() => {
|
|
692
442
|
void effectiveMode;
|
|
693
443
|
if (!editorEl) return;
|
|
@@ -695,163 +445,44 @@
|
|
|
695
445
|
assertInvariant('marker-css-parity', () => checkMarkerCssParity(root));
|
|
696
446
|
});
|
|
697
447
|
|
|
698
|
-
// ── Root
|
|
699
|
-
|
|
700
|
-
$effect(() => {
|
|
701
|
-
if (!editorEl) return;
|
|
702
|
-
const root = editorEl;
|
|
703
|
-
const handleClick = (e: MouseEvent) => {
|
|
704
|
-
const target = e.target as Element | null;
|
|
705
|
-
// Ahead of the anchor arm: a blocked-scheme link renders as a SPAN, and it is exactly
|
|
706
|
-
// the link a user opens the card to fix. Mod-click still activates, below.
|
|
707
|
-
if (effectiveMode === 'live' && !e.ctrlKey && !e.metaKey) {
|
|
708
|
-
const linkEl = target?.closest(LINK_ELEMENT_SELECTOR);
|
|
709
|
-
if (linkEl && !isHostChrome(linkEl) && openLinkCard(linkEl)) {
|
|
710
|
-
e.preventDefault();
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
const anchor = target?.closest('a[href]') as HTMLAnchorElement | null;
|
|
715
|
-
// The helper claims only clicks whose target IS the root, so nothing the
|
|
716
|
-
// editor renders is touched. A margin drag's release arrives as a root click too (the
|
|
717
|
-
// press and release targets meet at the root); with a range just painted, it is no
|
|
718
|
-
// click to answer.
|
|
719
|
-
if (!anchor) {
|
|
720
|
-
const pressed = marginDrag;
|
|
721
|
-
const dragged =
|
|
722
|
-
pressed &&
|
|
723
|
-
(Math.abs(e.clientX - marginDown.x) > 3 || Math.abs(e.clientY - marginDown.y) > 3);
|
|
724
|
-
marginDrag = false;
|
|
725
|
-
if (dragged) return;
|
|
726
|
-
if (deadSpaceCaret.handleClick(root, e)) return;
|
|
727
|
-
// A press the editor took on a block (a host's own padding beside a table, a rule, a
|
|
728
|
-
// folded equation's face) that did not move is a click on it: the click helper
|
|
729
|
-
// claims only dead space, so the same landing is resolved here.
|
|
730
|
-
if (pressed && !deadSpaceCaret.isDeadSpaceTarget(root, e.target)) {
|
|
731
|
-
if (placeCaretAtPoint(e.clientX, e.clientY)) return;
|
|
732
|
-
}
|
|
733
|
-
// Declined everywhere: a click on nothing still LEAVES what was being edited (a
|
|
734
|
-
// revealed equation folds on blur), since the margin press suppressed the blur the
|
|
735
|
-
// browser would have done.
|
|
736
|
-
if (pressed) blurEditingSurface(root);
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
// Host chrome follows the page's link behaviour, not plain-click-edits.
|
|
740
|
-
if (isHostChrome(anchor)) return;
|
|
741
|
-
const href = anchor.getAttribute('href');
|
|
742
|
-
if (!href) return;
|
|
743
|
-
// Reading mode has no caret for a plain click to place, so links behave as
|
|
744
|
-
// in a rendered document.
|
|
745
|
-
if (e.ctrlKey || e.metaKey || effectiveMode === 'reading') {
|
|
746
|
-
e.preventDefault();
|
|
747
|
-
activateLink(href, e);
|
|
748
|
-
} else {
|
|
749
|
-
// Suppress the browser's link navigation; cursor placement comes from
|
|
750
|
-
// mousedown and is unaffected.
|
|
751
|
-
e.preventDefault();
|
|
752
|
-
}
|
|
753
|
-
};
|
|
754
|
-
// Dead space, and the parts of a block that are neither editable nor controls: a rendered
|
|
755
|
-
// equation, a diagram, a card's rendered face. A press on any of them cannot grow a native
|
|
756
|
-
// selection, so the editor's drag runs from it.
|
|
757
|
-
// A whole-block input proxy is an editable in name only (it catches IME for a block with no
|
|
758
|
-
// text), so a press on it starts the editor's drag like a press on the block itself.
|
|
759
|
-
const NOT_A_DRAG_START =
|
|
760
|
-
'[contenteditable="true"]:not([data-whole-block-input]), button, input, textarea, select, ' +
|
|
761
|
-
'a, summary, [role="checkbox"], ' +
|
|
762
|
-
'.code-rail, .table-add-zone, .editor-tail, .md-menu, .block-drag-handle';
|
|
763
|
-
const dragStartsHere = (rootEl: HTMLElement, target: EventTarget | null): boolean => {
|
|
764
|
-
if (deadSpaceCaret.isDeadSpaceTarget(rootEl, target)) return true;
|
|
765
|
-
if (!(target instanceof Element) || !rootEl.contains(target)) return false;
|
|
766
|
-
return target.closest(NOT_A_DRAG_START) === null;
|
|
767
|
-
};
|
|
768
|
-
// A drag that STARTS in the margin: the browser cannot grow a selection from a
|
|
769
|
-
// non-editable press into an editable block, so the editor runs the drag itself, anchored
|
|
770
|
-
// where a click there would land. The mousedown's default is suppressed so the browser
|
|
771
|
-
// starts no selection of its own to fight it; `click` still fires for the caret placement.
|
|
772
|
-
let marginDrag = false;
|
|
773
|
-
let marginDown = { x: 0, y: 0 };
|
|
774
|
-
const startMarginDrag = (e: PointerEvent) => {
|
|
775
|
-
marginDrag = false;
|
|
776
|
-
marginDown = { x: e.clientX, y: e.clientY };
|
|
777
|
-
if (e.button !== 0 || e.shiftKey || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
778
|
-
if (effectiveMode === 'reading' || !dragStartsHere(root, e.target)) return;
|
|
779
|
-
const anchor = deadSpaceCaret.anchorAtPoint(root, e.clientX, e.clientY);
|
|
780
|
-
if (!anchor) return;
|
|
781
|
-
marginDrag = true;
|
|
782
|
-
resetForPointerDown(selectionState, stickyColumn, edgeAffinity, false);
|
|
783
|
-
// A block that runs its own drag from a nearby press (a table's cell rectangle) takes
|
|
784
|
-
// it; the generic drag is for blocks that have none.
|
|
785
|
-
if (!('offset' in anchor)) {
|
|
786
|
-
const component = getBlockComponent(anchor.path);
|
|
787
|
-
if (component?.startDragAtPoint?.(e.clientX, e.clientY, e)) return;
|
|
788
|
-
}
|
|
789
|
-
installDragListener(
|
|
790
|
-
{
|
|
791
|
-
editorRoot: root,
|
|
792
|
-
scrollContainer: getScrollHost() ?? root,
|
|
793
|
-
selection: selectionState,
|
|
794
|
-
getBlockElByPath,
|
|
795
|
-
lifetimeSignal: lifetimeController.signal,
|
|
796
|
-
paintSameBlock: true
|
|
797
|
-
},
|
|
798
|
-
anchor,
|
|
799
|
-
e
|
|
800
|
-
);
|
|
801
|
-
};
|
|
802
|
-
return removeAll(
|
|
803
|
-
onRoot(root, 'click', handleClick),
|
|
804
|
-
installDoubleClickWordSelect(root),
|
|
805
|
-
onRoot(root, 'pointerdown', startMarginDrag),
|
|
806
|
-
onRoot(root, 'mousedown', (e: MouseEvent) => {
|
|
807
|
-
deadSpaceCaret.notePress(root, e);
|
|
808
|
-
if (marginDrag) e.preventDefault();
|
|
809
|
-
})
|
|
810
|
-
);
|
|
811
|
-
});
|
|
448
|
+
// ── Root listeners ──────────────────────────────────────────────────
|
|
812
449
|
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
// would self-release mid-settle. On the resolved PORT, not the root — the pin fights
|
|
816
|
-
// whoever scrolls the port, and in host mode that gesture lands outside the editor.
|
|
450
|
+
// On the resolved scroll container, not the root: in host mode the user scrolls outside the
|
|
451
|
+
// editor, and that scroll must release the block held in place.
|
|
817
452
|
$effect(() => {
|
|
818
453
|
if (!editorEl) return;
|
|
819
454
|
const target = getScrollHost();
|
|
820
455
|
if (!target) return;
|
|
821
|
-
|
|
822
|
-
return removeAll(
|
|
823
|
-
onRoot(target, 'keydown', release),
|
|
824
|
-
onRoot(target, 'pointerdown', release),
|
|
825
|
-
onRoot(target, 'wheel', release, { passive: true })
|
|
826
|
-
);
|
|
456
|
+
return installRevealAnchorRelease(target, scrollOwner.release);
|
|
827
457
|
});
|
|
828
458
|
|
|
829
459
|
$effect(() => {
|
|
830
460
|
if (!editorEl) return;
|
|
831
461
|
const root = editorEl;
|
|
832
|
-
// focusout bubbles, so reset only when focus leaves the editor
|
|
462
|
+
// focusout bubbles, so reset only when focus leaves the editor's content: for somewhere
|
|
463
|
+
// outside it, or for the host's header, which holds no caret of the editor's.
|
|
833
464
|
return onRoot(root, 'focusout', (e: FocusEvent) => {
|
|
834
465
|
const next = e.relatedTarget as Node | null;
|
|
835
|
-
if (next && root.contains(next)) return;
|
|
836
|
-
|
|
837
|
-
edgeAffinity.reset();
|
|
466
|
+
if (next && root.contains(next) && !isHostChrome(next)) return;
|
|
467
|
+
caretMemory.forget();
|
|
838
468
|
});
|
|
839
469
|
});
|
|
840
470
|
|
|
841
|
-
// Its own effect
|
|
842
|
-
// would make it wait for the bind and re-install on every root change.
|
|
471
|
+
// Its own effect, since it needs no root and would otherwise re-install on every root change.
|
|
843
472
|
$effect(() =>
|
|
844
473
|
onRoot(document, 'visibilitychange', () => {
|
|
845
474
|
if (document.visibilityState === 'hidden') {
|
|
846
|
-
|
|
847
|
-
edgeAffinity.reset();
|
|
475
|
+
caretMemory.forget();
|
|
848
476
|
}
|
|
849
477
|
})
|
|
850
478
|
);
|
|
851
479
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
480
|
+
$effect(() => {
|
|
481
|
+
if (!editorEl) return;
|
|
482
|
+
return installUndoStepEnd(editorEl, () => controller.endUndoStep());
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// A chord that reaches <body> goes to one editor: the only one, or the last one interacted with.
|
|
855
486
|
$effect(() => {
|
|
856
487
|
if (!editorEl) return;
|
|
857
488
|
const root = editorEl;
|
|
@@ -866,75 +497,45 @@
|
|
|
866
497
|
|
|
867
498
|
// ── Block element and component lookup ──────────────────────────────
|
|
868
499
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
500
|
+
const getBlockElByPath: BlockElLookup = (path) =>
|
|
501
|
+
editorEl ? blockContentElAt(editorEl, path) : null;
|
|
502
|
+
|
|
503
|
+
// The root list as a descent reads it; the window's calls go through arrows because
|
|
504
|
+
// `topWindowing` is declared further down.
|
|
505
|
+
const rootList: ChildList = {
|
|
506
|
+
count: () => doc.children.length,
|
|
507
|
+
refs: blockRefSlots,
|
|
508
|
+
windowing: {
|
|
509
|
+
revealChild: (index) => topWindowing.revealChild(index),
|
|
510
|
+
isInWindow: (index) => topWindowing.isInWindow(index)
|
|
877
511
|
}
|
|
878
|
-
if (path.length < 3) return null;
|
|
879
|
-
const tablePath = path.slice(0, -2);
|
|
880
|
-
const rowIdx = path[path.length - 2];
|
|
881
|
-
const colIdx = path[path.length - 1];
|
|
882
|
-
const tableWrapper = editorEl.querySelector(`[data-block-path='${JSON.stringify(tablePath)}']`);
|
|
883
|
-
if (!tableWrapper) return null;
|
|
884
|
-
const tableEl = tableWrapper.querySelector(':scope > [role="table"]');
|
|
885
|
-
if (!tableEl) return null;
|
|
886
|
-
const rowEl = tableEl.querySelector(`:scope > [data-table-row-idx='${rowIdx}']`);
|
|
887
|
-
if (!rowEl) return null;
|
|
888
|
-
const cells = rowEl.querySelectorAll(':scope > [role="cell"]');
|
|
889
|
-
return (cells[colIdx] as HTMLElement | undefined) ?? null;
|
|
890
512
|
};
|
|
891
513
|
|
|
892
|
-
// The
|
|
893
|
-
//
|
|
514
|
+
// The component already mounted at `path`, mounting nothing; shared by the rect API and the
|
|
515
|
+
// test hooks.
|
|
894
516
|
function getBlockComponent(path: number[]): BlockComponent | null {
|
|
895
|
-
|
|
896
|
-
const [first, ...rest] = path;
|
|
897
|
-
const ref = blockRefs[first];
|
|
898
|
-
if (!ref) return null;
|
|
899
|
-
if (rest.length === 0) return ref;
|
|
900
|
-
return ref.getBlockComponentByPath?.(rest) ?? null;
|
|
517
|
+
return componentAt(rootList, path);
|
|
901
518
|
}
|
|
902
519
|
|
|
903
520
|
// ── Action Bundles ──────────────────────────────────────────────────
|
|
904
521
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
await revealChildOrWait(top, {
|
|
914
|
-
slots: blockRefSlots,
|
|
915
|
-
childCount: doc.children.length,
|
|
916
|
-
revealChild: topWindowing.revealChild,
|
|
917
|
-
isInWindow: topWindowing.isInWindow
|
|
918
|
-
});
|
|
919
|
-
const ref = blockRefs[top];
|
|
920
|
-
if (!ref) return null;
|
|
921
|
-
if (path.length === 1) return ref;
|
|
922
|
-
return ref.revealByPath
|
|
923
|
-
? await ref.revealByPath(path.slice(1))
|
|
924
|
-
: (ref.getBlockComponentByPath?.(path.slice(1)) ?? null);
|
|
925
|
-
}
|
|
522
|
+
const scrollOwner = createScrollOwner({
|
|
523
|
+
getScrollHost,
|
|
524
|
+
editorCorrects: ownsScrollCorrection,
|
|
525
|
+
getBlockElByPath,
|
|
526
|
+
getEditorRoot: () => editorEl ?? null,
|
|
527
|
+
isHostScroll: () => hostScroll,
|
|
528
|
+
getClipBounds
|
|
529
|
+
});
|
|
926
530
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
pluginEntries ? kindEnablementFor(activePlugins) : undefined,
|
|
936
|
-
__registryEnablement
|
|
937
|
-
)
|
|
531
|
+
const caretLanding = createCaretLanding({
|
|
532
|
+
getDoc: () => doc,
|
|
533
|
+
root: rootList,
|
|
534
|
+
selectionState,
|
|
535
|
+
caretMemory,
|
|
536
|
+
getBlockElByPath,
|
|
537
|
+
getEditorRoot: () => editorEl ?? null,
|
|
538
|
+
scroll: scrollOwner
|
|
938
539
|
});
|
|
939
540
|
|
|
940
541
|
const editorActionsDeps: EditorActionsDeps = {
|
|
@@ -958,27 +559,21 @@
|
|
|
958
559
|
setBlockRefs: (v) => replaceRefs(blockRefs, v),
|
|
959
560
|
undoManager,
|
|
960
561
|
sharing,
|
|
961
|
-
|
|
962
|
-
edgeAffinity,
|
|
562
|
+
caretMemory,
|
|
963
563
|
selectionState,
|
|
964
564
|
getBlockElByPath,
|
|
965
|
-
|
|
565
|
+
caretLanding,
|
|
966
566
|
events,
|
|
967
|
-
|
|
968
|
-
getPresentationMode: () => effectiveMode,
|
|
969
|
-
get linkRef() {
|
|
970
|
-
return linkRefView;
|
|
971
|
-
}
|
|
567
|
+
reading
|
|
972
568
|
};
|
|
973
|
-
const { blockEdit, focus, history, containerEdit, controller } =
|
|
974
|
-
|
|
569
|
+
const { blockEdit, focus, history, containerEdit, controller } = createEditorActions(
|
|
570
|
+
editorActionsDeps,
|
|
571
|
+
announceEdit
|
|
572
|
+
);
|
|
975
573
|
|
|
976
|
-
// A getter, so block components read the live doc
|
|
977
|
-
// the snapshot they mounted with.
|
|
574
|
+
// A getter, so block components read the live doc rather than the one they mounted with.
|
|
978
575
|
const getDoc: DocumentGetter = () => doc;
|
|
979
576
|
|
|
980
|
-
// Ahead of the plugin contexts because the plugin door hands its registry into
|
|
981
|
-
// createEditorPluginContexts below.
|
|
982
577
|
const decorationEngine = createDecorationEngine({
|
|
983
578
|
getDoc,
|
|
984
579
|
onSourceError: (source, error) =>
|
|
@@ -986,26 +581,36 @@
|
|
|
986
581
|
});
|
|
987
582
|
const decorations: DecorationRegistry = { addSource: decorationEngine.addSource };
|
|
988
583
|
|
|
989
|
-
// Reuses revealPath/getBlockElByPath/getBlockComponent so nothing measures through
|
|
990
|
-
// a second closure.
|
|
991
584
|
const rects = createEditorRects({
|
|
992
585
|
getBlockElByPath,
|
|
993
|
-
|
|
994
|
-
revealPath,
|
|
586
|
+
getBlockComponent,
|
|
587
|
+
revealPath: (path) => caretLanding.mount(path, { openCollapsed: true }),
|
|
995
588
|
getEditorRoot: () => editorEl ?? null,
|
|
996
|
-
|
|
997
|
-
getClipBounds,
|
|
589
|
+
scroll: scrollOwner,
|
|
998
590
|
isCrossBlock: () => selectionState.isCrossBlock,
|
|
999
591
|
isHostChrome,
|
|
1000
|
-
|
|
1001
|
-
// A navigation holds its pin, unlike the consumer restore door: nothing follows
|
|
1002
|
-
// it that wants the viewport back, and the band should outlive a late decode.
|
|
1003
|
-
landCaretAt: landCaretAtOffset
|
|
592
|
+
landCaretAt: navigateCaret
|
|
1004
593
|
});
|
|
1005
594
|
|
|
1006
|
-
// After getDoc so it reuses that one live-doc closure: a second getDoc would be a
|
|
1007
|
-
// TDZ reference here, and the rule is one getter, never a captured value.
|
|
1008
595
|
const editorId = mintEditorId();
|
|
596
|
+
|
|
597
|
+
// The typed-trigger menus (`#tag`, `[[link`); a pick commits as one undo step, like the
|
|
598
|
+
// link card's edits.
|
|
599
|
+
const inlineRange = createInlineRangeCommit({ deps: editorActionsDeps, controller });
|
|
600
|
+
const inlineMenu = createInlineMenuState({
|
|
601
|
+
getDoc,
|
|
602
|
+
getSelection,
|
|
603
|
+
getMode: reading.mode,
|
|
604
|
+
events,
|
|
605
|
+
editorId,
|
|
606
|
+
reading,
|
|
607
|
+
commitRange: (path, start, end, bytes, caretAfter) =>
|
|
608
|
+
inlineRange.commitInlineRange(path, start, end, bytes, caretAfter, { landCaret: true }),
|
|
609
|
+
undoStep: (path, offset, run) => controller.undoStep({ path: docPathFrom(path), offset }, run)
|
|
610
|
+
});
|
|
611
|
+
const inlineMenus: InlineMenuRegistry = inlineMenu.registry;
|
|
612
|
+
$effect(() => () => inlineMenu.dispose());
|
|
613
|
+
|
|
1009
614
|
const pluginContexts = createEditorPluginContexts({
|
|
1010
615
|
editorId,
|
|
1011
616
|
getDoc,
|
|
@@ -1013,21 +618,24 @@
|
|
|
1013
618
|
optionsFor: (name) => pluginEntries?.optionsByName.get(name),
|
|
1014
619
|
decorations,
|
|
1015
620
|
rects,
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
621
|
+
inlineMenus,
|
|
622
|
+
getDocumentGeneration: documentSwap.generation,
|
|
623
|
+
// The one place the mode enters command dispatch, read back through `pluginEditor`.
|
|
624
|
+
getPresentationMode: reading.mode,
|
|
1019
625
|
getTheme: () => theme,
|
|
1020
|
-
activation: activePlugins
|
|
626
|
+
activation: activePlugins,
|
|
627
|
+
// Called at use, never here: both read state declared further down this component.
|
|
628
|
+
insertMarkdown: (md, options) => insertMarkdown(md, options),
|
|
629
|
+
runCommand: (commandId, arg) => runCommand(commandId, arg),
|
|
630
|
+
reading
|
|
1021
631
|
});
|
|
1022
632
|
|
|
1023
|
-
// One
|
|
1024
|
-
// handler, so leaf, cross-block and editor-root route identically.
|
|
633
|
+
// One lookup for every dispatch level, so block, cross-block and root route plugins alike.
|
|
1025
634
|
const pluginEditorLookup: PluginEditorLookup = (name) => pluginContexts.get(name);
|
|
1026
635
|
const commandErrorSink: CommandErrorSink = (report) => emitCommandError(events, report);
|
|
1027
636
|
|
|
1028
|
-
// onMount,
|
|
1029
|
-
//
|
|
1030
|
-
// first structural edit would dispose every subscription (casebook.md's re-init scar).
|
|
637
|
+
// onMount, not $effect: plugin callbacks read reactive state, and an effect would tear
|
|
638
|
+
// every subscription down on the first structural edit.
|
|
1031
639
|
onMount(() => {
|
|
1032
640
|
pluginContexts.attachAll(({ plugin, error }) =>
|
|
1033
641
|
events.emit('error', { origin: 'subscriber', error, context: { plugin } })
|
|
@@ -1038,8 +646,7 @@
|
|
|
1038
646
|
// Aborted on unmount; document-level listeners observe it to cancel mid-operation work.
|
|
1039
647
|
const lifetimeController = new AbortController();
|
|
1040
648
|
$effect(() => () => {
|
|
1041
|
-
//
|
|
1042
|
-
// subscribers the host still holds, for a document that is gone.
|
|
649
|
+
// A pending checkpoint timer would otherwise emit for a document that is gone.
|
|
1043
650
|
controller.flushDebouncedCheckpoint();
|
|
1044
651
|
lifetimeController.abort();
|
|
1045
652
|
});
|
|
@@ -1048,65 +655,79 @@
|
|
|
1048
655
|
// other's broken-state recompute.
|
|
1049
656
|
const brokenImageUrls = new Set<string>();
|
|
1050
657
|
|
|
1051
|
-
const pasteCoordinator = createPasteCoordinator(
|
|
658
|
+
const pasteCoordinator = createPasteCoordinator(editorActionsDeps, controller);
|
|
1052
659
|
|
|
1053
|
-
// The
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
660
|
+
// The find bar, the link card and a mode switch put their saved selection back through here.
|
|
661
|
+
const roundTripRestore = createRoundTripRestore({
|
|
662
|
+
selection: selectionState,
|
|
663
|
+
landing: caretLanding,
|
|
664
|
+
getBlockElByPath
|
|
665
|
+
});
|
|
1058
666
|
|
|
1059
|
-
|
|
1060
|
-
//
|
|
1061
|
-
const
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
canReplace ? searchReplace.replaceAll(matches, template) : Promise.resolve(0)
|
|
667
|
+
// The document caret while the search bar or link card holds focus; one each, so a card
|
|
668
|
+
// opened over the search bar cannot overwrite the caret the bar restores.
|
|
669
|
+
const caretRestoreDeps: CaretRestoreDeps = {
|
|
670
|
+
getEditorEl: () => editorEl ?? null,
|
|
671
|
+
read: () => getSelection(),
|
|
672
|
+
restore: (selection) => roundTripRestore(selection, { reveal: 'into-view' })
|
|
1066
673
|
};
|
|
674
|
+
const searchCaret = createCaretRestore(caretRestoreDeps);
|
|
675
|
+
const linkCardCaret = createCaretRestore(caretRestoreDeps);
|
|
676
|
+
|
|
677
|
+
const searchReplace = createSearchReplace(editorActionsDeps, controller);
|
|
678
|
+
// Find stays live in reading mode; replace is an edit and does nothing here.
|
|
1067
679
|
const searchState = createSearchState({
|
|
1068
680
|
getDoc,
|
|
1069
|
-
getDocumentGeneration:
|
|
681
|
+
getDocumentGeneration: documentSwap.generation,
|
|
1070
682
|
decorations,
|
|
1071
|
-
replace:
|
|
1072
|
-
//
|
|
1073
|
-
// default, which is what search wants), so the band's async image-decode churn
|
|
1074
|
-
// can't clamp the reveal off the match.
|
|
683
|
+
replace: searchReplace,
|
|
684
|
+
// The public scroll call holds the match in place, so a late image decode cannot move it.
|
|
1075
685
|
reveal: (p) => rects.scrollTo(p),
|
|
1076
|
-
onClose: searchCaret.restore
|
|
686
|
+
onClose: () => void searchCaret.restore()
|
|
1077
687
|
});
|
|
1078
|
-
// Lives here, not in SearchBar, so the root Ctrl+H and the bar's chevron share
|
|
1079
|
-
// source of truth.
|
|
688
|
+
// Lives here, not in SearchBar, so the root Ctrl+H and the bar's chevron share it.
|
|
1080
689
|
let replaceExpanded = $state(false);
|
|
1081
690
|
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
691
|
+
const reorder = createReorderAction(editorActionsDeps, controller);
|
|
692
|
+
|
|
693
|
+
// Cleared first like the reorder announcement, so two headings in a row announce twice.
|
|
694
|
+
let kindAnnouncement = $state('');
|
|
695
|
+
const kindCue = createKindCue({
|
|
696
|
+
getDoc,
|
|
697
|
+
getPresentationMode: reading.mode,
|
|
698
|
+
announce: async (label) => {
|
|
699
|
+
kindAnnouncement = '';
|
|
700
|
+
await tick();
|
|
701
|
+
kindAnnouncement = label;
|
|
702
|
+
}
|
|
1091
703
|
});
|
|
1092
704
|
|
|
1093
705
|
// ── Context provision ───────────────────────────────────────────────
|
|
1094
706
|
|
|
1095
|
-
// One per instance, shared by every dispatch site's gates: the chord arm, a leaf's rebound
|
|
1096
|
-
// chord and the `runCommand` door must reach the same cross-block road.
|
|
1097
707
|
const crossBlockCommands = createCrossBlockCommands({
|
|
1098
708
|
selection: selectionState,
|
|
1099
709
|
getDoc,
|
|
1100
|
-
getBlockElByPath,
|
|
1101
|
-
revealPath,
|
|
1102
710
|
controller,
|
|
1103
|
-
|
|
1104
|
-
grammar: registryView.grammar,
|
|
711
|
+
reading,
|
|
1105
712
|
getContentVersion: contentVersion.read
|
|
1106
713
|
});
|
|
1107
714
|
|
|
1108
|
-
//
|
|
1109
|
-
// overrides
|
|
715
|
+
// Every chord and `runCommand` dispatches against this one context, so all share one history,
|
|
716
|
+
// mode, overrides, range handling and error channel.
|
|
717
|
+
const commands: CommandDispatchContext = {
|
|
718
|
+
history,
|
|
719
|
+
pluginEditor: pluginEditorLookup,
|
|
720
|
+
activation: activePlugins,
|
|
721
|
+
getPresentationMode: reading.mode,
|
|
722
|
+
isCrossBlockRange: () => selectionState.isCrossBlock,
|
|
723
|
+
crossBlockCommands,
|
|
724
|
+
keybindingOverrides: () => overridesMap,
|
|
725
|
+
onCommandError: commandErrorSink,
|
|
726
|
+
reorder
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
// One context key per bundle so a container re-provides only what it overrides; history
|
|
730
|
+
// has a single provider (G1.4).
|
|
1110
731
|
setContext(BLOCK_EDIT_KEY, blockEdit);
|
|
1111
732
|
setContext(FOCUS_KEY, focus);
|
|
1112
733
|
setContext(HISTORY_KEY, history);
|
|
@@ -1116,39 +737,41 @@
|
|
|
1116
737
|
events,
|
|
1117
738
|
decorations: decorationEngine,
|
|
1118
739
|
selection: selectionState,
|
|
740
|
+
rangeCoverage: () => coverage,
|
|
1119
741
|
search: searchState,
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
revealAnchor,
|
|
742
|
+
caretMemory,
|
|
743
|
+
autoPairs,
|
|
744
|
+
scrollOwner,
|
|
1124
745
|
widgetSelection,
|
|
746
|
+
selectedWidget,
|
|
1125
747
|
linkCard,
|
|
748
|
+
inlineMenuCombobox: inlineMenu.comboboxFor,
|
|
1126
749
|
controller,
|
|
750
|
+
caretLanding,
|
|
1127
751
|
pasteCoordinator,
|
|
1128
752
|
reorder,
|
|
1129
|
-
reorderAnnounce: announceReorder,
|
|
1130
753
|
registryView,
|
|
1131
754
|
activePlugins,
|
|
1132
755
|
rects,
|
|
1133
|
-
|
|
756
|
+
commands,
|
|
757
|
+
menuPresence,
|
|
758
|
+
kindCue
|
|
1134
759
|
} satisfies EditorServices);
|
|
1135
760
|
|
|
1136
761
|
setContext(EDITOR_POLICIES_KEY, {
|
|
1137
762
|
resolveImageUrl: resolveImageUrlImpl,
|
|
1138
763
|
resolveLinkUrl: resolveLinkUrlImpl,
|
|
1139
764
|
imageLoadPolicy: () => imageLoadPolicy,
|
|
1140
|
-
// Reading mode
|
|
765
|
+
// Reading mode turns the drag handles off through the prop's own getter.
|
|
1141
766
|
blockDragHandles: () => blockDragHandles && effectiveMode !== 'reading',
|
|
1142
|
-
presentationMode:
|
|
767
|
+
presentationMode: reading.mode,
|
|
1143
768
|
theme: () => theme,
|
|
1144
769
|
keybindingOverrides: () => overridesMap,
|
|
1145
|
-
// An accessor, not the `onPasteImage,` shorthand
|
|
1146
|
-
// and svelte-check reports `state_referenced_locally`, which
|
|
1147
|
-
// `svelte/no-unused-svelte-ignore` won't let us suppress.
|
|
770
|
+
// An accessor, not the `onPasteImage,` shorthand, which would capture the prop's value.
|
|
1148
771
|
get onPasteImage() {
|
|
1149
772
|
return onPasteImage;
|
|
1150
773
|
},
|
|
1151
|
-
// Accessors
|
|
774
|
+
// Accessors, as above.
|
|
1152
775
|
get onRunCode() {
|
|
1153
776
|
return onRunCode;
|
|
1154
777
|
},
|
|
@@ -1160,26 +783,30 @@
|
|
|
1160
783
|
|
|
1161
784
|
// ── Root DOM effects ────────────────────────────────────────────────
|
|
1162
785
|
|
|
1163
|
-
//
|
|
1164
|
-
//
|
|
786
|
+
// `beforeFlip` runs while the outgoing mode still owns the DOM, `afterFlip` once every block
|
|
787
|
+
// has re-rendered; the caret is carried across.
|
|
1165
788
|
const modeFlip = createModeFlip({
|
|
1166
789
|
get editorEl() {
|
|
1167
790
|
return editorEl;
|
|
1168
791
|
},
|
|
1169
792
|
get mode() {
|
|
1170
|
-
return
|
|
793
|
+
return reading.mode();
|
|
1171
794
|
},
|
|
1172
795
|
selection: selectionState,
|
|
1173
796
|
getSelection,
|
|
797
|
+
announceSelection: selectionAnnouncer.announce,
|
|
1174
798
|
getBlockElByPath,
|
|
1175
799
|
isHostChrome,
|
|
1176
|
-
|
|
1177
|
-
// Built below; a
|
|
1178
|
-
get
|
|
1179
|
-
return
|
|
800
|
+
caretMemory,
|
|
801
|
+
// Built below; a mode switch runs after init, so the getter reads past the TDZ.
|
|
802
|
+
get layout() {
|
|
803
|
+
return layout;
|
|
1180
804
|
},
|
|
1181
805
|
events,
|
|
1182
|
-
restoreCaret: (path, offset) =>
|
|
806
|
+
restoreCaret: (path, offset) => roundTripRestore(caretAt(path, offset), { reveal: 'mount' }),
|
|
807
|
+
holdOutgoingMode: (mode) => {
|
|
808
|
+
outgoingMode = mode;
|
|
809
|
+
}
|
|
1183
810
|
});
|
|
1184
811
|
$effect.pre(() => {
|
|
1185
812
|
const mode = effectiveMode;
|
|
@@ -1189,8 +816,63 @@
|
|
|
1189
816
|
modeFlip.afterFlip(effectiveMode);
|
|
1190
817
|
});
|
|
1191
818
|
|
|
1192
|
-
//
|
|
1193
|
-
|
|
819
|
+
// ── Root gestures ───────────────────────────────────────────────────
|
|
820
|
+
|
|
821
|
+
const rootGestures = createRootGestures({
|
|
822
|
+
getDoc,
|
|
823
|
+
selection: selectionState,
|
|
824
|
+
caretMemory,
|
|
825
|
+
getBlockElByPath,
|
|
826
|
+
getBlockComponent,
|
|
827
|
+
land: caretLanding.land,
|
|
828
|
+
getScrollHost,
|
|
829
|
+
getLifetime: () => lifetimeController.signal,
|
|
830
|
+
isHostChrome,
|
|
831
|
+
activateLink,
|
|
832
|
+
linkCard,
|
|
833
|
+
reading,
|
|
834
|
+
widgetSelection
|
|
835
|
+
});
|
|
836
|
+
$effect(() => {
|
|
837
|
+
if (!editorEl) return;
|
|
838
|
+
const root = editorEl;
|
|
839
|
+
return removeAll(
|
|
840
|
+
rootGestures.install(root),
|
|
841
|
+
installSelectionDrop({
|
|
842
|
+
editorRoot: root,
|
|
843
|
+
getDoc,
|
|
844
|
+
controller,
|
|
845
|
+
coordinator: pasteCoordinator,
|
|
846
|
+
reading,
|
|
847
|
+
activePlugins,
|
|
848
|
+
events,
|
|
849
|
+
setDropCaret: (rect) => (dropCaret = rect),
|
|
850
|
+
isReadOnly: () => effectiveMode === 'reading'
|
|
851
|
+
})
|
|
852
|
+
);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
const rootMenus = createRootMenus({
|
|
856
|
+
get editorEl() {
|
|
857
|
+
return editorEl;
|
|
858
|
+
},
|
|
859
|
+
get mode() {
|
|
860
|
+
return reading.mode();
|
|
861
|
+
},
|
|
862
|
+
getDoc,
|
|
863
|
+
isHostChrome,
|
|
864
|
+
blockEdit,
|
|
865
|
+
replaceRaw: (index, raw) =>
|
|
866
|
+
replaceBlockRaw({ deps: editorActionsDeps, controller }, [index], raw),
|
|
867
|
+
placeCaretAtPoint,
|
|
868
|
+
insertMarkdown,
|
|
869
|
+
insertCatalogue: getInsertCatalogue,
|
|
870
|
+
activation: activePlugins,
|
|
871
|
+
reading,
|
|
872
|
+
setMenu: (menu) => (blockMenu = menu)
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
// Announced for plugins that paint their own colors and cannot see a theme change in CSS.
|
|
1194
876
|
// svelte-ignore state_referenced_locally
|
|
1195
877
|
let lastTheme = theme;
|
|
1196
878
|
$effect(() => {
|
|
@@ -1200,9 +882,8 @@
|
|
|
1200
882
|
events.emit('themeChange', next);
|
|
1201
883
|
});
|
|
1202
884
|
|
|
1203
|
-
//
|
|
1204
|
-
//
|
|
1205
|
-
// a state it declines to paint must not also lose the caret, or the screen shows neither.
|
|
885
|
+
// Hides the native caret and highlight while the overlay paints; keyed on the overlay's own
|
|
886
|
+
// test, so a range it does not paint keeps the native one.
|
|
1206
887
|
$effect(() => {
|
|
1207
888
|
if (!editorEl) return;
|
|
1208
889
|
if (selectionState.isCustomRendered) {
|
|
@@ -1224,6 +905,8 @@
|
|
|
1224
905
|
editorRoot: editorEl,
|
|
1225
906
|
getScrollHost,
|
|
1226
907
|
moveReorderUnit: reorder.moveReorderUnit,
|
|
908
|
+
getDoc,
|
|
909
|
+
reading,
|
|
1227
910
|
overlay: {
|
|
1228
911
|
setGhost: (g) => (reorderGhost = g),
|
|
1229
912
|
setLine: (l) => (reorderLine = l)
|
|
@@ -1238,7 +921,8 @@
|
|
|
1238
921
|
return installSelectionChangeBridge({
|
|
1239
922
|
root: editorEl,
|
|
1240
923
|
isHostChrome,
|
|
1241
|
-
|
|
924
|
+
announceIfMoved: selectionAnnouncer.announceIfMoved,
|
|
925
|
+
selection: selectionState
|
|
1242
926
|
});
|
|
1243
927
|
});
|
|
1244
928
|
|
|
@@ -1246,72 +930,53 @@
|
|
|
1246
930
|
if (!editorEl) return;
|
|
1247
931
|
return installEditorBlurAnnouncer({
|
|
1248
932
|
root: editorEl,
|
|
1249
|
-
|
|
933
|
+
announce: selectionAnnouncer.announce
|
|
1250
934
|
});
|
|
1251
935
|
});
|
|
1252
936
|
|
|
1253
937
|
// ── Editor-root keydown routing ──────────────────────────────────────
|
|
1254
|
-
//
|
|
1255
|
-
// When the caret's block windows out, focus drops to <body> and the per-block keydown handlers
|
|
1256
|
-
// go silent, so this editor-scope handler reuses the same cross-block composer with the root
|
|
1257
|
-
// and the focus path standing in for `getEl` and `getMyPath`.
|
|
938
|
+
// Handles keys for a caret whose block unmounted, when focus has dropped to <body>.
|
|
1258
939
|
const editorCrossBlock = createCrossBlockHandlers({
|
|
1259
940
|
getEl: () => editorEl ?? null,
|
|
1260
941
|
getMyPath: () => selectionState.focus?.path ?? [],
|
|
1261
|
-
getIndex: () => selectionState.focus?.path?.[0] ?? 0,
|
|
1262
942
|
selection: selectionState,
|
|
1263
943
|
getDoc,
|
|
1264
944
|
getBlockElByPath,
|
|
1265
|
-
|
|
945
|
+
caretLanding,
|
|
946
|
+
revealPath: (path) => caretLanding.mount(path),
|
|
1266
947
|
getEditorRoot: () => editorEl ?? null,
|
|
1267
948
|
getScrollHost,
|
|
949
|
+
scrollOwner,
|
|
1268
950
|
getEditorLifetime: () => lifetimeController.signal,
|
|
1269
|
-
|
|
1270
|
-
edgeAffinity,
|
|
951
|
+
caretMemory,
|
|
1271
952
|
blockEdit,
|
|
1272
953
|
controller,
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
getPresentationMode: () => effectiveMode,
|
|
1276
|
-
linkRef: linkRefView,
|
|
1277
|
-
onCommandError: commandErrorSink,
|
|
1278
|
-
crossBlockCommands,
|
|
954
|
+
reading,
|
|
955
|
+
commands,
|
|
1279
956
|
pasteCoordinator,
|
|
1280
|
-
getKeybindingOverrides: () => overridesMap,
|
|
1281
|
-
grammar: registryView.grammar,
|
|
1282
957
|
activePlugins,
|
|
1283
958
|
events,
|
|
1284
|
-
|
|
959
|
+
selectedWidget,
|
|
1285
960
|
afterReactivity: () => tick()
|
|
1286
961
|
});
|
|
1287
962
|
|
|
1288
|
-
//
|
|
1289
|
-
//
|
|
1290
|
-
// the page, so an unguarded handler let one Ctrl+Z revert two editors.
|
|
963
|
+
// Every handler checks for this instance: the document listener sees every editor's keys,
|
|
964
|
+
// and one Ctrl+Z must not revert two editors.
|
|
1291
965
|
const rootKeydown = createEditorRootKeydown({
|
|
1292
966
|
get searchBarEnabled() {
|
|
1293
967
|
return searchBar;
|
|
1294
968
|
},
|
|
1295
|
-
get mode() {
|
|
1296
|
-
return effectiveMode;
|
|
1297
|
-
},
|
|
1298
969
|
get canReplace() {
|
|
1299
970
|
return canReplace;
|
|
1300
971
|
},
|
|
1301
|
-
get keybindingOverrides() {
|
|
1302
|
-
return overridesMap;
|
|
1303
|
-
},
|
|
1304
972
|
get isCrossBlock() {
|
|
1305
973
|
return selectionState.isCrossBlock;
|
|
1306
974
|
},
|
|
1307
975
|
search: searchState,
|
|
1308
|
-
|
|
1309
|
-
pluginEditor: pluginEditorLookup,
|
|
1310
|
-
activation: activePlugins,
|
|
1311
|
-
onCommandError: commandErrorSink,
|
|
976
|
+
commands,
|
|
1312
977
|
crossBlock: editorCrossBlock,
|
|
1313
978
|
isHostChrome,
|
|
1314
|
-
|
|
979
|
+
saveSearchCaret: searchCaret.saveCurrent,
|
|
1315
980
|
setReplaceExpanded: (expanded) => (replaceExpanded = expanded)
|
|
1316
981
|
});
|
|
1317
982
|
|
|
@@ -1324,21 +989,18 @@
|
|
|
1324
989
|
});
|
|
1325
990
|
|
|
1326
991
|
// ── Editor-root clipboard routing ────────────────────────────────────
|
|
1327
|
-
//
|
|
1328
|
-
// The keydown sibling's counterpart: a Ctrl+C/X/V that Chromium retargeted to <body> because
|
|
1329
|
-
// the selection found no text position to park a caret in. Same containment — the arms claim
|
|
1330
|
-
// only events landing on THIS root, or on the body with this instance holding the chord claim.
|
|
992
|
+
// Copy, cut and paste that Chromium retargets to <body> when the selection has no text position.
|
|
1331
993
|
const rootClipboard = createEditorRootClipboard({
|
|
1332
994
|
selection: selectionState,
|
|
1333
995
|
getDoc,
|
|
1334
996
|
crossBlock: editorCrossBlock,
|
|
1335
|
-
//
|
|
997
|
+
// An accessor, so the handler reads the live prop.
|
|
1336
998
|
get onPasteImage() {
|
|
1337
999
|
return onPasteImage;
|
|
1338
1000
|
},
|
|
1339
1001
|
events,
|
|
1340
1002
|
getSelectedWidgetBlock: () => {
|
|
1341
|
-
const selected =
|
|
1003
|
+
const selected = selectionState.widget;
|
|
1342
1004
|
return selected ? getBlockComponent(selected.paragraphPath) : null;
|
|
1343
1005
|
}
|
|
1344
1006
|
});
|
|
@@ -1354,83 +1016,48 @@
|
|
|
1354
1016
|
);
|
|
1355
1017
|
});
|
|
1356
1018
|
|
|
1357
|
-
// ── Height
|
|
1019
|
+
// ── Height estimates ────────────────────────────────────────────────
|
|
1358
1020
|
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
// feature's hottest path, and `widthVersion` is already the rebuild signal.
|
|
1362
|
-
let typeScale = 1;
|
|
1363
|
-
|
|
1364
|
-
// Only the font-relative terms scale: block chrome is absolute padding and an
|
|
1365
|
-
// image's height is its own. Getters, so a scale change lands without rebuilding
|
|
1366
|
-
// the oracle or dropping its measured heights.
|
|
1367
|
-
const heightOracle = createHeightOracle({
|
|
1368
|
-
get lineHeight() {
|
|
1369
|
-
return HEIGHT_ESTIMATES.proseLineHeight * typeScale;
|
|
1370
|
-
},
|
|
1371
|
-
get codeLineHeight() {
|
|
1372
|
-
return HEIGHT_ESTIMATES.codeLineHeight * typeScale;
|
|
1373
|
-
},
|
|
1374
|
-
get avgCharWidth() {
|
|
1375
|
-
return HEIGHT_ESTIMATES.avgCharWidth * typeScale;
|
|
1376
|
-
},
|
|
1377
|
-
blockChrome: HEIGHT_ESTIMATES.blockChrome,
|
|
1378
|
-
imageBlockMinHeight: HEIGHT_ESTIMATES.imageBlockMinHeight
|
|
1379
|
-
});
|
|
1021
|
+
const layout = createLayoutState();
|
|
1022
|
+
const heightOracle = layout.heightOracle;
|
|
1380
1023
|
|
|
1381
1024
|
// ── Resize invalidation ─────────────────────────────────────────────
|
|
1382
1025
|
|
|
1383
|
-
// A
|
|
1384
|
-
//
|
|
1385
|
-
let widthVersion = $state(0);
|
|
1026
|
+
// A zero-tall element, not the root: in host mode the root's height follows the blocks the
|
|
1027
|
+
// rebuild changes, and a watched box resized mid-delivery is a ResizeObserver loop error.
|
|
1386
1028
|
$effect(() => {
|
|
1387
|
-
if (!
|
|
1388
|
-
return installWidthWatcher(
|
|
1389
|
-
heightOracle.dropMeasured();
|
|
1390
|
-
widthVersion++;
|
|
1391
|
-
});
|
|
1029
|
+
if (!widthProbeEl) return;
|
|
1030
|
+
return installWidthWatcher(widthProbeEl, layout.rebuildForNewGeometry);
|
|
1392
1031
|
});
|
|
1393
1032
|
|
|
1394
|
-
// The
|
|
1395
|
-
// scrollport's height, so a height-only resize otherwise leaves the newly-exposed band as
|
|
1396
|
-
// bare spacer until the next scroll. Its own counter, never `widthVersion` — that one
|
|
1397
|
-
// drops every measured height, which a resize re-wrapping no prose has not earned.
|
|
1398
|
-
let viewportHeightVersion = $state(0);
|
|
1033
|
+
// The scroll container's height sets how many blocks mount.
|
|
1399
1034
|
$effect(() => {
|
|
1400
1035
|
if (!editorEl) return;
|
|
1401
1036
|
const target = getScrollHost();
|
|
1402
1037
|
if (!target) return;
|
|
1403
|
-
return installViewportHeightWatcher(target,
|
|
1038
|
+
return installViewportHeightWatcher(target, layout.noteViewportHeight);
|
|
1404
1039
|
});
|
|
1405
1040
|
|
|
1406
1041
|
// ── Type scale ──────────────────────────────────────────────────────
|
|
1407
1042
|
|
|
1408
|
-
// A font-size
|
|
1409
|
-
// reports; the probe's scale rides the width signal, since a rebuild is what it needs.
|
|
1043
|
+
// A font-size change throws every height estimate off, and no other box reports it.
|
|
1410
1044
|
$effect(() => {
|
|
1411
1045
|
if (!typeScaleProbeEl) return;
|
|
1412
1046
|
return installTypeScaleProbe(typeScaleProbeEl, {
|
|
1413
|
-
getScale:
|
|
1414
|
-
onScale:
|
|
1415
|
-
typeScale = next;
|
|
1416
|
-
heightOracle.dropMeasured();
|
|
1417
|
-
widthVersion++;
|
|
1418
|
-
}
|
|
1047
|
+
getScale: layout.typeScale,
|
|
1048
|
+
onScale: layout.setTypeScale
|
|
1419
1049
|
});
|
|
1420
1050
|
});
|
|
1421
1051
|
|
|
1422
|
-
// ── Header
|
|
1052
|
+
// ── Header height compensation ──────────────────────────────────────
|
|
1423
1053
|
|
|
1424
1054
|
$effect(() => {
|
|
1425
1055
|
const el = headerEl;
|
|
1426
|
-
if (!el || !editorEl) return;
|
|
1427
|
-
const port = getScrollport();
|
|
1428
|
-
if (!port) return;
|
|
1056
|
+
if (!el || !editorEl || !rootScroll) return;
|
|
1429
1057
|
return installHeaderSlotCompensation({
|
|
1430
1058
|
el,
|
|
1431
|
-
port,
|
|
1432
|
-
|
|
1433
|
-
revealHoldsScroll: () => topWindowing.revealHoldsScroll()
|
|
1059
|
+
port: scrollOwner.port,
|
|
1060
|
+
compensate: rootScroll.compensate
|
|
1434
1061
|
});
|
|
1435
1062
|
});
|
|
1436
1063
|
|
|
@@ -1438,7 +1065,7 @@
|
|
|
1438
1065
|
|
|
1439
1066
|
const focusAttribution = createFocusAttribution({
|
|
1440
1067
|
get mode() {
|
|
1441
|
-
return
|
|
1068
|
+
return reading.mode();
|
|
1442
1069
|
}
|
|
1443
1070
|
});
|
|
1444
1071
|
$effect(() => {
|
|
@@ -1452,49 +1079,46 @@
|
|
|
1452
1079
|
|
|
1453
1080
|
// ── Top-level windowing ─────────────────────────────────────────────
|
|
1454
1081
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1082
|
+
const listTree = createListTree({
|
|
1083
|
+
getScrollTop: () => scrollOwner.port()?.scrollTop() ?? null,
|
|
1084
|
+
getFocusPath: focusAttribution.getFocusedPath
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// Set after the windowing signals it holds exist; the windowing hook below reads it back.
|
|
1457
1088
|
setContext(EDITOR_DOC_KEY, {
|
|
1458
1089
|
doc: getDoc,
|
|
1459
1090
|
contentVersion: contentVersion.read,
|
|
1460
|
-
|
|
1091
|
+
reading,
|
|
1461
1092
|
pluginEditor: pluginEditorLookup,
|
|
1462
1093
|
lifetime: lifetimeController.signal,
|
|
1463
1094
|
editorRoot: () => editorEl ?? null,
|
|
1464
1095
|
scrollHost: getScrollHost,
|
|
1465
|
-
scrollport:
|
|
1096
|
+
scrollport: scrollOwner.port,
|
|
1466
1097
|
blockElLookup: getBlockElByPath,
|
|
1467
1098
|
focusedPath: focusAttribution.getFocusedPath,
|
|
1468
1099
|
heightOracle,
|
|
1469
|
-
|
|
1470
|
-
widthVersion:
|
|
1471
|
-
viewportHeightVersion:
|
|
1100
|
+
listTree,
|
|
1101
|
+
widthVersion: layout.widthVersion,
|
|
1102
|
+
viewportHeightVersion: layout.viewportHeightVersion
|
|
1472
1103
|
} satisfies EditorDoc);
|
|
1473
1104
|
|
|
1474
|
-
//
|
|
1475
|
-
//
|
|
1476
|
-
|
|
1477
|
-
const topWindowing = useContainerWindowing({
|
|
1478
|
-
getIndex: () => 0, // ignored — root has no parent sink (reportSelfHeight is undefined)
|
|
1479
|
-
getParentPath: () => [],
|
|
1105
|
+
// The inner `.block-list`, not `editorEl`: it scrolls with content, so its top maps scrollTop
|
|
1106
|
+
// into list coordinates.
|
|
1107
|
+
const { windowing: topWindowing, scroll: rootScroll } = useRootWindowing({
|
|
1480
1108
|
getChildren: () => doc.children,
|
|
1481
1109
|
getChildIds: () => blockIds,
|
|
1482
|
-
getListEl: () => editorEl?.querySelector(':scope > .block-list') ?? null
|
|
1483
|
-
provideLeafChannel: true
|
|
1110
|
+
getListEl: () => editorEl?.querySelector(':scope > .block-list') ?? null
|
|
1484
1111
|
});
|
|
1485
1112
|
|
|
1486
|
-
// Plain `let
|
|
1487
|
-
//
|
|
1488
|
-
// avoid (VR-4). Nothing reactive reads it — the root's own attribute reads the derived,
|
|
1489
|
-
// so the flag lags the attribute one flush at a watermark crossing (measured harmless).
|
|
1113
|
+
// Plain `let`: the scroll correction reads it mid-measure, where the derived would force a
|
|
1114
|
+
// layout read (VR-4). It lags one flush when windowing toggles.
|
|
1490
1115
|
let rootWindowingActive = false;
|
|
1491
1116
|
$effect(() => {
|
|
1492
1117
|
rootWindowingActive = topWindowing.window.active;
|
|
1493
1118
|
});
|
|
1494
1119
|
|
|
1495
|
-
//
|
|
1496
|
-
//
|
|
1497
|
-
// both correcting would double-count. The `overflow-anchor` opt-out keys off the same fact.
|
|
1120
|
+
// Whether the editor corrects scroll on a height change; in host mode without windowing the
|
|
1121
|
+
// browser's scroll anchoring does it instead.
|
|
1498
1122
|
function ownsScrollCorrection(): boolean {
|
|
1499
1123
|
return !hostScroll || rootWindowingActive;
|
|
1500
1124
|
}
|
|
@@ -1505,150 +1129,73 @@
|
|
|
1505
1129
|
return serialize(doc);
|
|
1506
1130
|
}
|
|
1507
1131
|
|
|
1508
|
-
// The kind alone, never the node
|
|
1509
|
-
// it. See `editor-props.ts` for the contract.
|
|
1132
|
+
// The kind alone, never the node, so a host holds no handle into the tree.
|
|
1510
1133
|
export function getBlockKindAt(path: number[]): AnyBlockKind | null {
|
|
1511
1134
|
return blockNodeAt(doc, path)?.kind ?? null;
|
|
1512
1135
|
}
|
|
1513
1136
|
|
|
1514
|
-
/**
|
|
1515
|
-
* A frozen snapshot of the current selection, or null when nothing is focused.
|
|
1516
|
-
* Path arrays are copies, so mutating the result does not affect internal state.
|
|
1517
|
-
*/
|
|
1137
|
+
/** A snapshot of the current selection with copied paths, or null when nothing is focused. */
|
|
1518
1138
|
export function getSelection(): EditorSelection | null {
|
|
1519
1139
|
return readCurrentSelection(selectionState, blockRefs);
|
|
1520
1140
|
}
|
|
1521
1141
|
|
|
1522
|
-
/**
|
|
1523
|
-
* The one restore road: resolve + clamp, reveal, place. `reveal` picks which reveal. The two
|
|
1524
|
-
* scrolling ones differ in whether the pin outlives the call — a navigation holds, a consumer
|
|
1525
|
-
* restore hands the viewport back so a kept pin can't override the host's next scroll — and
|
|
1526
|
-
* `mount` is the history swap's bare mount, which promises no block IN VIEW (overscan keeps
|
|
1527
|
-
* blocks mounted past the fold) and in exchange writes no scrollport.
|
|
1528
|
-
*/
|
|
1529
|
-
function restoreThroughRevealRoad(
|
|
1530
|
-
selection: EditorSelection,
|
|
1531
|
-
reveal: 'hold' | 'release' | 'mount'
|
|
1532
|
-
): Promise<SelectionRestoreOutcome> {
|
|
1533
|
-
return restoreSelection(selection, {
|
|
1534
|
-
getDoc,
|
|
1535
|
-
selectionState,
|
|
1536
|
-
getBlockElByPath,
|
|
1537
|
-
revealTarget: async (path) =>
|
|
1538
|
-
reveal === 'mount'
|
|
1539
|
-
? (await revealPath(path)) !== null
|
|
1540
|
-
: rects.scrollTo(path, { block: 'nearest', hold: reveal === 'hold' })
|
|
1541
|
-
});
|
|
1542
|
-
}
|
|
1543
|
-
|
|
1544
1142
|
function caretAt(path: number[], offset = 0): EditorSelection {
|
|
1545
1143
|
return { anchor: { path, offset }, focus: { path, offset } };
|
|
1546
1144
|
}
|
|
1547
1145
|
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
/**
|
|
1555
|
-
* Restore a snapshot from {@link getSelection}, sharing the whole restore road with
|
|
1556
|
-
* the undo swap and plugin navigation so the three cannot diverge. True iff the
|
|
1557
|
-
* selection was placed AND its focus block is in view; a later programmatic reveal
|
|
1558
|
-
* mid-settle owns the viewport and makes this false, a user gesture does not.
|
|
1559
|
-
*/
|
|
1560
|
-
export async function setSelection(selection: EditorSelection): Promise<boolean> {
|
|
1561
|
-
return (await restoreThroughRevealRoad(selection, 'release')) === 'applied';
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
// The dead-space click's own landing walk, minus the press/target discrimination a host
|
|
1565
|
-
// caller has already done for itself. See `editor-props.ts` for the contract.
|
|
1566
|
-
export function placeCaretAtPoint(x: number, y: number): boolean {
|
|
1567
|
-
return editorEl ? deadSpaceCaret.placeAtPoint(editorEl, x, y) : false;
|
|
1146
|
+
// Read off the placed selection, not the requested one: a caret aimed into a hidden body lands
|
|
1147
|
+
// on its title row.
|
|
1148
|
+
function focusInView(): boolean {
|
|
1149
|
+
const focus = getSelection()?.focus;
|
|
1150
|
+
return !!focus && scrollOwner.isInView(selectionState.cellLandingFor(focus).path);
|
|
1568
1151
|
}
|
|
1569
1152
|
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
* sits inside its container's host, which must not receive what was aimed at the gap.
|
|
1574
|
-
*/
|
|
1575
|
-
function blurEditingSurface(root: HTMLElement): void {
|
|
1576
|
-
const active = document.activeElement;
|
|
1577
|
-
if (active instanceof HTMLElement && root.contains(active) && active !== root) active.blur();
|
|
1153
|
+
async function navigateCaret(path: number[], offset: number): Promise<boolean> {
|
|
1154
|
+
const outcome = await caretLanding.navigate({ path: docPathFrom(path), offset });
|
|
1155
|
+
return outcome === 'placed' && focusInView();
|
|
1578
1156
|
}
|
|
1579
1157
|
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
return findSurfacePathForElement(active);
|
|
1158
|
+
/** True only if the selection was placed and its focus block is in view; a programmatic
|
|
1159
|
+
* scroll before it finishes makes it false, a user scroll does not. */
|
|
1160
|
+
export async function setSelection(selection: EditorSelection): Promise<boolean> {
|
|
1161
|
+
return (await caretLanding.restore(selection)) === 'applied' && focusInView();
|
|
1585
1162
|
}
|
|
1586
1163
|
|
|
1587
|
-
//
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
export function insertMarkdown(md: string): boolean {
|
|
1591
|
-
const path = focusedSurfacePath();
|
|
1592
|
-
if (!path) return false;
|
|
1593
|
-
return getBlockComponent(path)?.insertMarkdown?.(md) ?? false;
|
|
1164
|
+
// The caret search a click on empty space runs, minus the event checks a host has done.
|
|
1165
|
+
export function placeCaretAtPoint(x: number, y: number): boolean {
|
|
1166
|
+
return editorEl ? rootGestures.placeCaretAtPoint(editorEl, x, y) : false;
|
|
1594
1167
|
}
|
|
1595
1168
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1169
|
+
// The entry points below only find the focused editable; the rules live in the editable.
|
|
1170
|
+
const focusedSurface = createFocusedSurface({
|
|
1171
|
+
get editorEl() {
|
|
1172
|
+
return editorEl;
|
|
1173
|
+
},
|
|
1174
|
+
selection: selectionState,
|
|
1175
|
+
getDoc,
|
|
1176
|
+
getBlockComponent,
|
|
1177
|
+
isReading: () => effectiveMode === 'reading',
|
|
1178
|
+
insertParagraph: (boundary, text) => blockEdit.insertParagraph(boundary, text),
|
|
1179
|
+
undoStep: (path, offset, run) => controller.undoStep({ path: docPathFrom(path), offset }, run),
|
|
1180
|
+
contentVersion: contentVersion.read
|
|
1181
|
+
});
|
|
1604
1182
|
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
function focusedCommandTarget(): KindCommandTarget | null {
|
|
1608
|
-
const path = focusedSurfacePath();
|
|
1609
|
-
if (!path) return null;
|
|
1610
|
-
const component = getBlockComponent(path);
|
|
1611
|
-
const node = blockNodeAt(doc, path);
|
|
1612
|
-
if (!component?.runCommand || !node) return null;
|
|
1613
|
-
return {
|
|
1614
|
-
kind: node.kind,
|
|
1615
|
-
runCommand: (id, arg) => component.runCommand!(id, arg),
|
|
1616
|
-
isCommandActive: component.isCommandActive
|
|
1617
|
-
? (id) => component.isCommandActive!(id)
|
|
1618
|
-
: undefined
|
|
1619
|
-
};
|
|
1183
|
+
export function insertMarkdown(md: string, options?: InsertMarkdownOptions): Promise<boolean> {
|
|
1184
|
+
return focusedSurface.insertMarkdown(md, options);
|
|
1620
1185
|
}
|
|
1621
1186
|
|
|
1622
|
-
// The door only resolves the focused surface; every rule (the reading gate, the cross-block
|
|
1623
|
-
// range decline, the arms themselves) lives below the seam. See `editor-props.ts`.
|
|
1624
1187
|
export function runCommand(commandId: string, arg?: unknown): boolean {
|
|
1625
|
-
return runCommandById(
|
|
1626
|
-
commandId as AnyCommandId,
|
|
1627
|
-
arg,
|
|
1628
|
-
focusedCommandTarget(),
|
|
1629
|
-
commandDispatchContext,
|
|
1630
|
-
commandErrorSink
|
|
1631
|
-
);
|
|
1188
|
+
return runCommandById(commandId as AnyCommandId, arg, focusedSurface.commandTarget(), commands);
|
|
1632
1189
|
}
|
|
1633
1190
|
|
|
1634
|
-
//
|
|
1635
|
-
// cannot drift. See `editor-props.ts` for the contract.
|
|
1191
|
+
// Asks through `runCommand`'s own dispatch, so a host greys out exactly what a click refuses.
|
|
1636
1192
|
export function canRunCommand(commandId: string): boolean {
|
|
1637
|
-
return canRunCommandById(
|
|
1638
|
-
commandId as AnyCommandId,
|
|
1639
|
-
focusedCommandTarget(),
|
|
1640
|
-
commandDispatchContext
|
|
1641
|
-
);
|
|
1193
|
+
return canRunCommandById(commandId as AnyCommandId, focusedSurface.commandTarget(), commands);
|
|
1642
1194
|
}
|
|
1643
1195
|
|
|
1644
|
-
//
|
|
1645
|
-
// pressed paint reads the same bytes the toggle would rewrite. See `editor-props.ts`.
|
|
1196
|
+
// The focused editable reports its toggle state from the bytes the toggle would rewrite.
|
|
1646
1197
|
export function isCommandActive(commandId: string): boolean {
|
|
1647
|
-
return isCommandActiveById(
|
|
1648
|
-
commandId as AnyCommandId,
|
|
1649
|
-
focusedCommandTarget(),
|
|
1650
|
-
commandDispatchContext
|
|
1651
|
-
);
|
|
1198
|
+
return isCommandActiveById(commandId as AnyCommandId, focusedSurface.commandTarget(), commands);
|
|
1652
1199
|
}
|
|
1653
1200
|
|
|
1654
1201
|
export function getEvents(): EditorEvents {
|
|
@@ -1663,12 +1210,19 @@
|
|
|
1663
1210
|
return decorations;
|
|
1664
1211
|
}
|
|
1665
1212
|
|
|
1213
|
+
export function getInlineMenus(): InlineMenuRegistry {
|
|
1214
|
+
return inlineMenus;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
export function getInsertCatalogue(): readonly InsertEntry[] {
|
|
1218
|
+
return insertCatalogue(activePlugins);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1666
1221
|
export function getRects(): EditorRects {
|
|
1667
1222
|
return rects;
|
|
1668
1223
|
}
|
|
1669
1224
|
|
|
1670
|
-
// Recomposed per call
|
|
1671
|
-
// process-global and mutate outside this component's reactive graph.
|
|
1225
|
+
// Recomposed per call: the registries are process-global and not reactive.
|
|
1672
1226
|
export function reservedChords(): ReadonlySet<string> {
|
|
1673
1227
|
return collectReservedChords({
|
|
1674
1228
|
searchBar,
|
|
@@ -1681,34 +1235,10 @@
|
|
|
1681
1235
|
return chordIsClaimed(event, reservedChords());
|
|
1682
1236
|
}
|
|
1683
1237
|
|
|
1684
|
-
|
|
1685
|
-
// SelectionState never holds.
|
|
1686
|
-
function selectionSummary(): string {
|
|
1687
|
-
const sel = getSelection();
|
|
1688
|
-
if (!sel) return '(no selection)';
|
|
1689
|
-
const fmt = (p: { path: number[]; offset: number }) => `[${p.path.join(',')}]@${p.offset}`;
|
|
1690
|
-
return `anchor=${fmt(sel.anchor)} focus=${fmt(sel.focus)}`;
|
|
1691
|
-
}
|
|
1238
|
+
const diagnostics = createEditorDiagnostics({ getSelection, getSource, operationsLog });
|
|
1692
1239
|
|
|
1693
1240
|
export function getDiagnostics(): EditorDiagnostics {
|
|
1694
|
-
return
|
|
1695
|
-
enableTrace: enableInteractionTrace,
|
|
1696
|
-
disableTrace: disableInteractionTrace,
|
|
1697
|
-
isTraceEnabled: isInteractionTraceEnabled,
|
|
1698
|
-
traceSnapshot: interactionTraceSnapshot,
|
|
1699
|
-
serializeDiagnostics: (opts) => {
|
|
1700
|
-
const includeSource = opts?.includeSource ?? false;
|
|
1701
|
-
return buildDiagnosticsReport({
|
|
1702
|
-
timestamp: new Date().toISOString(),
|
|
1703
|
-
trace: dumpInteractionTrace(interactionTraceSnapshot()),
|
|
1704
|
-
opsLog: dumpOperationsLog(operationsLog),
|
|
1705
|
-
selection: selectionSummary(),
|
|
1706
|
-
// Serialize only when opted in — the report stays document-free by default.
|
|
1707
|
-
source: includeSource ? getSource() : '',
|
|
1708
|
-
includeSource
|
|
1709
|
-
});
|
|
1710
|
-
}
|
|
1711
|
-
};
|
|
1241
|
+
return diagnostics;
|
|
1712
1242
|
}
|
|
1713
1243
|
|
|
1714
1244
|
// Compile-time conformance: the published handle can't drift from the exports.
|
|
@@ -1725,64 +1255,37 @@
|
|
|
1725
1255
|
getEvents,
|
|
1726
1256
|
getSearch,
|
|
1727
1257
|
getDecorations,
|
|
1258
|
+
getInlineMenus,
|
|
1259
|
+
getInsertCatalogue,
|
|
1728
1260
|
getRects,
|
|
1729
1261
|
getDiagnostics,
|
|
1730
1262
|
reservedChords,
|
|
1731
1263
|
claimsChord
|
|
1732
1264
|
} satisfies EditorInstance);
|
|
1733
1265
|
|
|
1734
|
-
// ── Test-only
|
|
1266
|
+
// ── Test-only hooks ─────────────────────────────────────────────────
|
|
1735
1267
|
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
function getUndoStack() {
|
|
1741
|
-
return undoManager.getStacks();
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
/** The live CST Document; treat it as read-only, since mutating it bypasses the
|
|
1745
|
-
* undo pipeline. */
|
|
1746
|
-
function getDocument() {
|
|
1747
|
-
return doc;
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
export const __test = {
|
|
1751
|
-
getDocument,
|
|
1752
|
-
// The swap door's only oracle: every other door is reachable headlessly, but the
|
|
1753
|
-
// `source` prop's reset lives in this component.
|
|
1268
|
+
export const __test: EditorTestSurface = {
|
|
1269
|
+
getDocument: () => doc,
|
|
1270
|
+
getGrammar: () => registryView.grammar,
|
|
1754
1271
|
getContentVersion: contentVersion.read,
|
|
1755
1272
|
getBlockComponent,
|
|
1756
|
-
getUndoStack,
|
|
1757
|
-
getOperationsLog,
|
|
1758
|
-
// Specs need the state, not the `data-cross-block` mirror: a deferred $effect
|
|
1759
|
-
// writes the attribute, and an intra-table rectangle keeps one path on both
|
|
1760
|
-
// endpoints, so neither the DOM nor getSelection() answers this reliably.
|
|
1273
|
+
getUndoStack: () => undoManager.getStacks(),
|
|
1274
|
+
getOperationsLog: () => operationsLog,
|
|
1761
1275
|
isCrossBlockActive: () => selectionState.isCrossBlock,
|
|
1762
|
-
// The gap caret has no public selection shape and no paint yet, so the state is the
|
|
1763
|
-
// only place arrival can be observed.
|
|
1764
1276
|
getGapCaret: () => selectionState.gapCaret,
|
|
1765
|
-
// The engine, not the `addSource`-only public registry: the derived per-path
|
|
1766
|
-
// buckets are the only oracle for a stale bucket, since jsdom measures every
|
|
1767
|
-
// range at zero width and no overlay ever paints there.
|
|
1768
1277
|
getDecorationEngine: () => decorationEngine,
|
|
1769
|
-
// The measured-height cache is root-constructed and handed down through context,
|
|
1770
|
-
// so its lifetime against a document swap has no headless seam either.
|
|
1771
1278
|
getHeightOracle: () => heightOracle,
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
// leave behind (see `isSlotDetached`); e2e-only.
|
|
1279
|
+
getBlockIds: () => blockIds,
|
|
1280
|
+
getMeasuredIds: () => heightOracle.measuredIds(),
|
|
1281
|
+
getListTree: () => listTree,
|
|
1282
|
+
getWidthVersion: layout.widthVersion,
|
|
1777
1283
|
setBlockRefSlot: blockRefSlots.set
|
|
1778
1284
|
};
|
|
1779
1285
|
</script>
|
|
1780
1286
|
|
|
1781
|
-
<!-- tabindex="-1":
|
|
1782
|
-
|
|
1783
|
-
selection for the selectionchange bridge to collapse. -->
|
|
1784
|
-
<!-- data-presentation is ABSENT in source mode on purpose: the default path's DOM
|
|
1785
|
-
stays byte-identical, and reading-mode CSS keys on the attribute's presence. -->
|
|
1287
|
+
<!-- tabindex="-1": an unmounted block hands focus here rather than to <body>, and the root
|
|
1288
|
+
stays out of the tab order. -->
|
|
1786
1289
|
<div
|
|
1787
1290
|
class="editor"
|
|
1788
1291
|
data-editor-theme={theme}
|
|
@@ -1793,12 +1296,11 @@
|
|
|
1793
1296
|
tabindex="-1"
|
|
1794
1297
|
role="group"
|
|
1795
1298
|
aria-label={EDITOR_LABEL}
|
|
1796
|
-
oncontextmenu={onRootContextMenu}
|
|
1299
|
+
oncontextmenu={rootMenus.onRootContextMenu}
|
|
1797
1300
|
>
|
|
1798
1301
|
{#if searchBar}
|
|
1799
|
-
<!--
|
|
1800
|
-
|
|
1801
|
-
editor's own theme scope, since custom properties resolve by DOM ancestry. -->
|
|
1302
|
+
<!-- A zero-height sticky anchor; portaled out, it takes the theme class instead, since
|
|
1303
|
+
custom properties resolve by DOM ancestry. -->
|
|
1802
1304
|
<div
|
|
1803
1305
|
class:search-anchor={!searchBarAnchor}
|
|
1804
1306
|
class:aragonite-editor-theme={!!searchBarAnchor && inThemedScope}
|
|
@@ -1811,12 +1313,12 @@
|
|
|
1811
1313
|
/>
|
|
1812
1314
|
</div>
|
|
1813
1315
|
{/if}
|
|
1814
|
-
<!-- One `em` tall and out of flow
|
|
1815
|
-
no other box reports, and windowing's activation decision needs that scale. -->
|
|
1316
|
+
<!-- One `em` tall and out of flow, so its box reports the root's font size. -->
|
|
1816
1317
|
<div class="type-scale-probe" bind:this={typeScaleProbeEl} aria-hidden="true"></div>
|
|
1318
|
+
<!-- The root's padding box wide and zero tall, so its box reports the root's width only. -->
|
|
1319
|
+
<div class="width-probe" bind:this={widthProbeEl} aria-hidden="true"></div>
|
|
1817
1320
|
{#if header}
|
|
1818
|
-
<!-- A
|
|
1819
|
-
its list as a direct child of this root. -->
|
|
1321
|
+
<!-- A sibling of the block list: windowing needs the list as a direct child of the root. -->
|
|
1820
1322
|
<div class="editor-header" bind:this={headerEl}>{@render header()}</div>
|
|
1821
1323
|
{/if}
|
|
1822
1324
|
<BlockList
|
|
@@ -1827,13 +1329,8 @@
|
|
|
1827
1329
|
window={topWindowing.window}
|
|
1828
1330
|
reorderable={true}
|
|
1829
1331
|
/>
|
|
1830
|
-
<!-- A sibling of the list like the header
|
|
1831
|
-
<TailInsert
|
|
1832
|
-
{blockEdit}
|
|
1833
|
-
childCount={doc.children.length}
|
|
1834
|
-
readOnly={effectiveMode === 'reading'}
|
|
1835
|
-
onPlus={(button) => void onTailPlus(button)}
|
|
1836
|
-
/>
|
|
1332
|
+
<!-- A sibling of the block list, like the header. -->
|
|
1333
|
+
<TailInsert {blockEdit} childCount={doc.children.length} readOnly={effectiveMode === 'reading'} />
|
|
1837
1334
|
{#if blockMenu}
|
|
1838
1335
|
<BlockMenu
|
|
1839
1336
|
x={blockMenu.x}
|
|
@@ -1843,41 +1340,70 @@
|
|
|
1843
1340
|
label={blockMenu.label}
|
|
1844
1341
|
onPick={blockMenu.pick}
|
|
1845
1342
|
onClose={() => (blockMenu = null)}
|
|
1343
|
+
{menuPresence}
|
|
1344
|
+
/>
|
|
1345
|
+
{/if}
|
|
1346
|
+
{#if selectionToolbar && effectiveMode !== 'reading'}
|
|
1347
|
+
<SelectionToolbar
|
|
1348
|
+
editor={{
|
|
1349
|
+
getSelection,
|
|
1350
|
+
getRects,
|
|
1351
|
+
getBlockKindAt,
|
|
1352
|
+
runCommand,
|
|
1353
|
+
canRunCommand,
|
|
1354
|
+
isCommandActive,
|
|
1355
|
+
getEvents
|
|
1356
|
+
}}
|
|
1357
|
+
root={editorEl}
|
|
1846
1358
|
/>
|
|
1847
1359
|
{/if}
|
|
1848
1360
|
<ImageOverlayHost
|
|
1849
1361
|
{widgetSelection}
|
|
1850
|
-
{
|
|
1362
|
+
{inlineRange}
|
|
1851
1363
|
{events}
|
|
1852
1364
|
{getDoc}
|
|
1365
|
+
getContentVersion={contentVersion.read}
|
|
1853
1366
|
getEditorEl={() => editorEl ?? null}
|
|
1854
1367
|
getSelectionIsCustomRendered={() => selectionState.isCustomRendered}
|
|
1855
|
-
getPresentationMode={() => effectiveMode}
|
|
1856
|
-
grammar={registryView.grammar}
|
|
1857
1368
|
lifetime={lifetimeController.signal}
|
|
1369
|
+
{menuPresence}
|
|
1858
1370
|
/>
|
|
1859
1371
|
<LinkCardHost
|
|
1860
1372
|
card={linkCard}
|
|
1861
|
-
{
|
|
1373
|
+
{inlineRange}
|
|
1862
1374
|
{events}
|
|
1863
1375
|
{getDoc}
|
|
1864
1376
|
getEditorEl={() => editorEl ?? null}
|
|
1865
1377
|
measureRange={rects.rangeRects}
|
|
1866
|
-
landCaret={landCaretAtOffset}
|
|
1867
1378
|
{activateLink}
|
|
1868
1379
|
resolveLinkUrl={resolveLinkUrlImpl}
|
|
1869
1380
|
caretRestore={linkCardCaret}
|
|
1870
|
-
|
|
1871
|
-
|
|
1381
|
+
{reading}
|
|
1382
|
+
{menuPresence}
|
|
1383
|
+
{commands}
|
|
1384
|
+
/>
|
|
1385
|
+
<InlineMenuHost
|
|
1386
|
+
menu={inlineMenu}
|
|
1387
|
+
{events}
|
|
1388
|
+
{menuPresence}
|
|
1389
|
+
getEditorEl={() => editorEl ?? null}
|
|
1390
|
+
measureRange={rects.rangeRects}
|
|
1872
1391
|
/>
|
|
1873
1392
|
<div class="editor-sr-live" role="status" aria-live="polite">{selectionDescription}</div>
|
|
1874
|
-
<div class="editor-sr-live-reorder" role="status" aria-live="polite">{
|
|
1393
|
+
<div class="editor-sr-live-reorder" role="status" aria-live="polite">{editAnnouncement}</div>
|
|
1394
|
+
<div class="editor-sr-live-kind" role="status" aria-live="polite">{kindAnnouncement}</div>
|
|
1875
1395
|
{#if reorderLine}
|
|
1876
1396
|
<div
|
|
1877
1397
|
class="reorder-line"
|
|
1878
1398
|
style="left:{reorderLine.left}px;top:{reorderLine.top}px;width:{reorderLine.width}px"
|
|
1879
1399
|
></div>
|
|
1880
1400
|
{/if}
|
|
1401
|
+
{#if dropCaret}
|
|
1402
|
+
<div
|
|
1403
|
+
class="drop-caret"
|
|
1404
|
+
style="left:{dropCaret.left}px;top:{dropCaret.top}px;height:{dropCaret.height}px"
|
|
1405
|
+
></div>
|
|
1406
|
+
{/if}
|
|
1881
1407
|
{#if reorderGhost}
|
|
1882
1408
|
<div class="reorder-ghost" style="left:{reorderGhost.clientX}px;top:{reorderGhost.clientY}px">
|
|
1883
1409
|
{reorderGhost.label}
|
|
@@ -1889,35 +1415,27 @@
|
|
|
1889
1415
|
.editor {
|
|
1890
1416
|
width: 100%;
|
|
1891
1417
|
flex: 1;
|
|
1892
|
-
/* Wider on the left
|
|
1893
|
-
and the rest of this padding is what keeps it off the scroll container's border. */
|
|
1418
|
+
/* Wider on the left to hold the drag handle's gutter. */
|
|
1894
1419
|
padding: 1rem 1rem 1rem 1.5rem;
|
|
1895
1420
|
font-family: var(--font-editor, ui-monospace, monospace);
|
|
1896
|
-
/*
|
|
1897
|
-
declaration scales the whole surface. */
|
|
1421
|
+
/* Every construct sizes in `em` off this. */
|
|
1898
1422
|
font-size: var(--editor-font-size, 1rem);
|
|
1899
1423
|
line-height: 1.6;
|
|
1900
1424
|
/* Inherit rather than assume a dark host: an unthemed page keeps its own text color. */
|
|
1901
1425
|
color: var(--color-text-primary, currentColor);
|
|
1902
1426
|
min-height: 200px;
|
|
1903
1427
|
overflow-y: auto;
|
|
1904
|
-
/* The editor corrects
|
|
1905
|
-
correctAnchor); native anchoring independently rewrites scrollTop and
|
|
1906
|
-
double-corrects. Do NOT restore `overflow-anchor` (VR-2). */
|
|
1428
|
+
/* The editor corrects scroll by hand; browser anchoring would correct twice (VR-2). */
|
|
1907
1429
|
overflow-anchor: none;
|
|
1908
1430
|
scrollbar-width: thin;
|
|
1909
1431
|
scrollbar-color: var(--color-border, #3e3e3b) transparent;
|
|
1910
|
-
/*
|
|
1911
|
-
|
|
1912
|
-
a column. A host that wants the frame draws it on its own container. */
|
|
1913
|
-
/* Containing block for the image overlay portal. */
|
|
1432
|
+
/* Containing block for the image overlay portal, and for `.width-probe`, whose width is the
|
|
1433
|
+
root's only while this holds. */
|
|
1914
1434
|
position: relative;
|
|
1915
1435
|
}
|
|
1916
1436
|
|
|
1917
|
-
/*
|
|
1918
|
-
|
|
1919
|
-
budget nothing corrects by hand, so native anchoring is restored — `none` would strip the
|
|
1920
|
-
subtree from the HOST's anchor candidates and hold nothing in its place. */
|
|
1437
|
+
/* Host scroll: an ancestor scrolls, and without windowing the browser's anchoring holds the
|
|
1438
|
+
position, so it is restored. */
|
|
1921
1439
|
.editor[data-scroll-mode='host'] {
|
|
1922
1440
|
overflow-y: visible;
|
|
1923
1441
|
overflow-anchor: auto;
|
|
@@ -1927,27 +1445,32 @@
|
|
|
1927
1445
|
padding: 0;
|
|
1928
1446
|
}
|
|
1929
1447
|
|
|
1930
|
-
/*
|
|
1931
|
-
|
|
1932
|
-
anchor candidates and holds the line itself (VR-2). The host's scroller is untouched. */
|
|
1448
|
+
/* With windowing on, the editor corrects scroll itself, so its subtree leaves the host's
|
|
1449
|
+
anchor candidates (VR-2). */
|
|
1933
1450
|
.editor[data-scroll-mode='host'][data-windowing='active'] {
|
|
1934
1451
|
overflow-anchor: none;
|
|
1935
1452
|
}
|
|
1936
1453
|
|
|
1937
|
-
/*
|
|
1938
|
-
|
|
1939
|
-
.
|
|
1454
|
+
/* Out of layout, but never `display: none`, which stops a ResizeObserver reporting. */
|
|
1455
|
+
.type-scale-probe,
|
|
1456
|
+
.width-probe {
|
|
1940
1457
|
position: absolute;
|
|
1941
1458
|
top: 0;
|
|
1942
1459
|
left: 0;
|
|
1943
|
-
width: 0;
|
|
1944
|
-
height: 1em;
|
|
1945
1460
|
visibility: hidden;
|
|
1946
1461
|
pointer-events: none;
|
|
1947
1462
|
}
|
|
1948
1463
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1464
|
+
.type-scale-probe {
|
|
1465
|
+
width: 0;
|
|
1466
|
+
height: 1em;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
.width-probe {
|
|
1470
|
+
right: 0;
|
|
1471
|
+
height: 0;
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1951
1474
|
.search-anchor {
|
|
1952
1475
|
position: sticky;
|
|
1953
1476
|
top: 0;
|
|
@@ -1955,8 +1478,7 @@
|
|
|
1955
1478
|
z-index: 5;
|
|
1956
1479
|
}
|
|
1957
1480
|
|
|
1958
|
-
/* Sticky
|
|
1959
|
-
floating the bar over unrelated page content; absolute re-homes it to the root. */
|
|
1481
|
+
/* Sticky would resolve against the host's scroller and float over unrelated page content. */
|
|
1960
1482
|
.editor[data-scroll-mode='host'] .search-anchor {
|
|
1961
1483
|
position: absolute;
|
|
1962
1484
|
top: 0;
|
|
@@ -1982,7 +1504,8 @@
|
|
|
1982
1504
|
}
|
|
1983
1505
|
|
|
1984
1506
|
.editor-sr-live,
|
|
1985
|
-
.editor-sr-live-reorder
|
|
1507
|
+
.editor-sr-live-reorder,
|
|
1508
|
+
.editor-sr-live-kind {
|
|
1986
1509
|
position: absolute;
|
|
1987
1510
|
width: 1px;
|
|
1988
1511
|
height: 1px;
|
|
@@ -1994,17 +1517,15 @@
|
|
|
1994
1517
|
border: 0;
|
|
1995
1518
|
}
|
|
1996
1519
|
|
|
1997
|
-
/* A nested drag
|
|
1998
|
-
|
|
1999
|
-
document, not a pile of blocks). Applied by editor-actions/reorder-drag.ts. */
|
|
1520
|
+
/* A nested drag stays inside its container, so the container takes a faint wash, not an
|
|
1521
|
+
outline. */
|
|
2000
1522
|
:global(.reorder-scope) {
|
|
2001
1523
|
background: var(--reorder-scope-bg, rgba(100, 150, 255, 0.14));
|
|
2002
1524
|
border-radius: 4px;
|
|
2003
1525
|
transition: background-color 0.12s ease;
|
|
2004
1526
|
}
|
|
2005
1527
|
|
|
2006
|
-
/*
|
|
2007
|
-
they never intercept the drag's own pointer stream. */
|
|
1528
|
+
/* Fixed, since the rects are client coordinates. */
|
|
2008
1529
|
.reorder-line {
|
|
2009
1530
|
position: fixed;
|
|
2010
1531
|
height: 2px;
|
|
@@ -2014,6 +1535,15 @@
|
|
|
2014
1535
|
z-index: 20;
|
|
2015
1536
|
}
|
|
2016
1537
|
|
|
1538
|
+
/* Replaces the browser's drop caret, which goes with the drop the editor cancels. */
|
|
1539
|
+
.drop-caret {
|
|
1540
|
+
position: fixed;
|
|
1541
|
+
width: 1.5px;
|
|
1542
|
+
background: var(--color-text-primary, currentColor);
|
|
1543
|
+
pointer-events: none;
|
|
1544
|
+
z-index: 20;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
2017
1547
|
.reorder-ghost {
|
|
2018
1548
|
position: fixed;
|
|
2019
1549
|
transform: translate(0.75rem, 0.5rem);
|