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