@voithos-labs/aragonite 0.10.2 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +19 -0
- package/dist/a11y-strings.js +19 -0
- package/dist/action-contracts.d.ts +7 -1
- package/dist/ambient/ambient-dom.js +5 -1
- package/dist/block-component.d.ts +14 -0
- package/dist/components/BlockDragHandle.svelte +37 -24
- package/dist/components/BlockHost.svelte +18 -11
- package/dist/components/Editor.svelte +434 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +25 -17
- package/dist/components/SelectionOverlay.svelte.d.ts +1 -1
- package/dist/components/TailInsert.svelte +107 -0
- package/dist/components/TailInsert.svelte.d.ts +17 -0
- package/dist/components/block-content-selector.d.ts +6 -2
- package/dist/components/block-content-selector.js +6 -2
- package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/code/CodeBlock.svelte +211 -30
- package/dist/components/blocks/code/CodeBlockRail.svelte +704 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +16 -0
- package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
- package/dist/components/blocks/code/code-context-actions.js +24 -0
- package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
- package/dist/components/blocks/code/code-fence-exit.js +26 -0
- package/dist/components/blocks/code/code-languages.d.ts +6 -0
- package/dist/components/blocks/code/code-languages.js +22 -3
- package/dist/components/blocks/code/code-renderer.js +11 -0
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
- package/dist/components/blocks/editable-leaf.d.ts +37 -6
- package/dist/components/blocks/editable-leaf.js +248 -30
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListBlock.svelte +1 -0
- package/dist/components/blocks/list/ListItemBlock.svelte +14 -5
- package/dist/components/blocks/list/ListItemBlock.svelte.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.js +11 -2
- package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
- package/dist/components/blocks/surface-wiring.svelte.js +8 -1
- package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
- package/dist/components/blocks/table/TableBlock.svelte +205 -186
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
- package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
- package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
- package/dist/components/blocks/table/cell-clipboard.js +34 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
- package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
- package/dist/components/blocks/table/table-cell-paste.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
- package/dist/components/blocks/table/table-menu-model.js +36 -17
- package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
- package/dist/components/blocks/text/click-snap-guard.d.ts +3 -0
- package/dist/components/blocks/text/click-snap-guard.js +8 -0
- package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
- package/dist/components/blocks/text/delimiter-autopair.js +216 -0
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
- package/dist/components/blocks/text/edge-policy-dispatch.js +89 -20
- package/dist/components/blocks/text/live-selection-edit.d.ts +5 -5
- package/dist/components/blocks/text/live-selection-edit.js +37 -7
- package/dist/components/blocks/text/text-clipboard.js +2 -2
- package/dist/components/blocks/text/text-keydown.d.ts +7 -1
- package/dist/components/blocks/text/text-keydown.js +11 -1
- package/dist/components/blocks/text/text-render.d.ts +1 -1
- package/dist/components/blocks/text/text-render.js +3 -1
- package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
- package/dist/components/blocks/text/widget-interaction.js +181 -37
- package/dist/components/drag-handle.d.ts +41 -0
- package/dist/components/drag-handle.js +134 -0
- package/dist/components/editor-root-focus.d.ts +19 -0
- package/dist/components/editor-root-focus.js +67 -0
- package/dist/components/editor-root-geometry.d.ts +39 -0
- package/dist/components/editor-root-geometry.js +91 -0
- package/dist/components/editor-root-keydown.d.ts +1 -1
- package/dist/components/editor-root-keydown.js +12 -3
- package/dist/components/editor-root-listeners.d.ts +3 -6
- package/dist/components/editor-root-listeners.js +3 -24
- package/dist/components/editor-root-mode-flip.d.ts +36 -0
- package/dist/components/editor-root-mode-flip.js +92 -0
- package/dist/components/image/ImageOverlayHost.svelte +14 -6
- package/dist/components/image/ImageProperties.svelte +512 -70
- package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
- package/dist/components/image/ImageResizeHandles.svelte +67 -34
- package/dist/components/image/image-crop.d.ts +39 -0
- package/dist/components/image/image-crop.js +74 -0
- package/dist/components/image/image-edit-commit.d.ts +1 -0
- package/dist/components/image/image-edit-commit.js +21 -5
- package/dist/components/image/image-source-bytes.js +10 -3
- package/dist/components/image/image-widget-editing.js +1 -0
- package/dist/components/image/widget-dom.js +5 -1
- package/dist/components/link-card/link-card-commit.js +1 -1
- package/dist/components/lrd-map-gate.js +1 -1
- package/dist/components/menu/BlockMenu.svelte +315 -0
- package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
- package/dist/components/menu/MenuIcon.svelte +153 -0
- package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
- package/dist/components/menu/SelectionToolbar.svelte +385 -0
- package/dist/components/menu/SelectionToolbar.svelte.d.ts +16 -0
- package/dist/components/menu/clipboard-actions.d.ts +12 -0
- package/dist/components/menu/clipboard-actions.js +42 -0
- package/dist/components/menu/default-context-actions.d.ts +15 -0
- package/dist/components/menu/default-context-actions.js +76 -0
- package/dist/components/menu/flyout-placement.d.ts +6 -0
- package/dist/components/menu/flyout-placement.js +25 -0
- package/dist/core/inline/format-toggle.d.ts +12 -4
- package/dist/core/inline/format-toggle.js +94 -40
- package/dist/core/inline/image-dimensions.d.ts +3 -0
- package/dist/core/inline/image-dimensions.js +46 -10
- package/dist/core/inline/inline-widgets.d.ts +19 -0
- package/dist/core/inline/inline-widgets.js +5 -0
- package/dist/core/inline/scan/brackets.js +1 -0
- package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
- package/dist/core/inline/scan/plugin-syntax.js +15 -1
- package/dist/core/inline/transparency.js +3 -3
- package/dist/core/inline-render.d.ts +6 -0
- package/dist/core/inline-render.js +32 -0
- package/dist/core/nodes.d.ts +14 -0
- package/dist/cursor/edge-affinity.js +2 -1
- package/dist/cursor/height-oracle.d.ts +2 -3
- package/dist/cursor/height-oracle.js +0 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/scroll-hold.d.ts +10 -0
- package/dist/cursor/scroll-hold.js +22 -0
- package/dist/cursor/scrollport.d.ts +9 -0
- package/dist/cursor/scrollport.js +30 -1
- package/dist/cursor/visual-lines.d.ts +5 -4
- package/dist/cursor/visual-lines.js +56 -11
- package/dist/cursor/widget-edge-snap.d.ts +28 -0
- package/dist/cursor/widget-edge-snap.js +38 -0
- package/dist/cursor/widget-offset.d.ts +9 -0
- package/dist/cursor/widget-offset.js +71 -4
- package/dist/debug/interaction-trace.d.ts +4 -0
- package/dist/debug/interaction-trace.js +15 -0
- package/dist/decorations/decoration-state.svelte.js +1 -1
- package/dist/decorations/reserved-attrs.js +1 -0
- package/dist/editor-actions/ancestry-folds.d.ts +2 -2
- package/dist/editor-actions/ancestry-folds.js +1 -1
- package/dist/editor-actions/block-edit-scope.js +1 -1
- package/dist/editor-actions/commit/text-batch.d.ts +3 -2
- package/dist/editor-actions/commit/text-batch.js +1 -1
- package/dist/editor-actions/commit/undo-controller.js +4 -2
- package/dist/editor-actions/container-edit.js +2 -1
- package/dist/editor-actions/enter-completion.d.ts +2 -0
- package/dist/editor-actions/enter-completion.js +23 -2
- package/dist/editor-actions/focus/focus-dispatch.js +7 -4
- package/dist/editor-actions/focus/focus-landing.d.ts +10 -1
- package/dist/editor-actions/focus/focus-landing.js +20 -6
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/plugin/container.d.ts +7 -0
- package/dist/editor-actions/plugin/container.js +3 -1
- package/dist/editor-actions/reorder-action.js +19 -10
- package/dist/editor-actions/reorder-drag.js +29 -1
- package/dist/editor-actions/replacement-focus.d.ts +1 -1
- package/dist/editor-actions/replacement-focus.js +1 -1
- package/dist/editor-actions/search-replace.js +1 -1
- package/dist/editor-actions/table-context.d.ts +4 -1
- package/dist/editor-actions/table-context.js +57 -1
- package/dist/editor-events.d.ts +3 -0
- package/dist/editor-keys.d.ts +33 -0
- package/dist/editor-props.d.ts +24 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +7 -0
- package/dist/plugin.js +16 -0
- package/dist/plugins/latex/BlockMath.svelte +264 -29
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
- package/dist/plugins/latex/index.d.ts +2 -1
- package/dist/plugins/latex/latex-kind.js +60 -10
- package/dist/plugins/latex/math-completion.js +4 -1
- package/dist/plugins/latex/math-layout.d.ts +15 -0
- package/dist/plugins/latex/math-layout.js +13 -0
- package/dist/plugins/latex/math-source.d.ts +19 -0
- package/dist/plugins/latex/math-source.js +103 -0
- package/dist/plugins/latex/register.d.ts +11 -2
- package/dist/plugins/latex/register.js +3 -1
- package/dist/plugins/latex/renderer.d.ts +3 -3
- package/dist/plugins/latex/renderer.js +13 -6
- package/dist/plugins/mermaid/MermaidBlock.svelte +19 -9
- package/dist/plugins/parrot/ParrotBlock.svelte +3 -1
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +75 -30
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/commands.d.ts +9 -5
- package/dist/schema/commands.js +12 -7
- package/dist/schema/context-actions.d.ts +31 -0
- package/dist/schema/context-actions.js +23 -0
- package/dist/schema/fenced-code-raw.js +31 -1
- package/dist/schema/operations.d.ts +8 -1
- package/dist/schema/reserved-chords.js +37 -4
- package/dist/schema/table-cell-raw.d.ts +1 -1
- package/dist/schema/table-cell-raw.js +1 -1
- package/dist/selection/block-hit-test.js +3 -2
- package/dist/selection/char-endpoint-snap.js +1 -1
- package/dist/selection/clipboard-text.js +6 -1
- package/dist/selection/covered-block.d.ts +10 -0
- package/dist/selection/covered-block.js +24 -0
- package/dist/selection/cross-block/dispatch.d.ts +3 -0
- package/dist/selection/cross-block/dispatch.js +13 -1
- package/dist/selection/cross-block/format-range.d.ts +1 -1
- package/dist/selection/cross-block/format-range.js +4 -15
- package/dist/selection/cross-block/format-toggle.js +1 -1
- package/dist/selection/cross-block/keydown.js +3 -29
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +31 -35
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +52 -13
- package/dist/selection/dead-space-caret.d.ts +13 -0
- package/dist/selection/dead-space-caret.js +57 -1
- package/dist/selection/drag-pointer.d.ts +26 -2
- package/dist/selection/drag-pointer.js +103 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.d.ts +3 -2
- package/dist/selection/keyboard-extend.js +4 -3
- package/dist/selection/multi-click.d.ts +42 -0
- package/dist/selection/multi-click.js +203 -0
- package/dist/selection/native-bridge.d.ts +3 -0
- package/dist/selection/native-bridge.js +31 -2
- package/dist/selection/path-lookup.d.ts +10 -2
- package/dist/selection/path-lookup.js +11 -4
- package/dist/selection/pointer-gesture.d.ts +9 -0
- package/dist/selection/pointer-gesture.js +11 -0
- package/dist/selection/primitives.d.ts +7 -0
- package/dist/selection/primitives.js +19 -1
- package/dist/selection/range-delete-ceremony.js +4 -2
- package/dist/selection/range-delete-chrome.js +2 -1
- package/dist/selection/range-delete-table-coverage.js +2 -1
- package/dist/selection/range-delete-table.js +3 -2
- package/dist/selection/range-delete.d.ts +4 -0
- package/dist/selection/range-delete.js +31 -3
- package/dist/selection/selection-drop.d.ts +34 -0
- package/dist/selection/selection-drop.js +253 -0
- package/dist/selection/selection-restore.js +1 -1
- package/dist/selection/selection-state.svelte.d.ts +6 -0
- package/dist/selection/selection-state.svelte.js +36 -1
- package/dist/selection/table-endpoint-snap.js +1 -1
- package/dist/selection/table-rect-extend.js +1 -1
- package/dist/styles/editor-theme.css +75 -31
- package/dist/styles/editor.css +268 -18
- package/dist/testing/container-conformance.js +2 -2
- package/dist/testing/inline-conformance.js +2 -1
- package/dist/tree-operations/blockquote.js +1 -1
- package/dist/tree-operations/chain-rebuild.d.ts +63 -0
- package/dist/tree-operations/chain-rebuild.js +142 -0
- package/dist/tree-operations/children.d.ts +1 -1
- package/dist/tree-operations/children.js +1 -1
- package/dist/tree-operations/cleanup.js +1 -1
- package/dist/tree-operations/content-write.d.ts +50 -0
- package/dist/tree-operations/content-write.js +263 -0
- package/dist/tree-operations/index.d.ts +8 -3
- package/dist/tree-operations/index.js +6 -2
- package/dist/tree-operations/list/exit-replacement.js +1 -1
- package/dist/tree-operations/list/unwrap-merge.js +3 -3
- package/dist/tree-operations/node-ops.d.ts +17 -234
- package/dist/tree-operations/node-ops.js +47 -1113
- package/dist/tree-operations/node-primitives.d.ts +75 -0
- package/dist/tree-operations/node-primitives.js +117 -0
- package/dist/tree-operations/paste/apply.js +1 -1
- package/dist/tree-operations/paste/body-write.d.ts +1 -1
- package/dist/tree-operations/paste/body-write.js +2 -2
- package/dist/tree-operations/paste/container-match.js +4 -2
- package/dist/tree-operations/paste/dispatch.js +2 -1
- package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
- package/dist/tree-operations/paste/focus-target.d.ts +1 -1
- package/dist/tree-operations/paste/list-absorb.js +1 -1
- package/dist/tree-operations/paste/list-break-out.js +1 -1
- package/dist/tree-operations/paste/parent-scope.js +1 -1
- package/dist/tree-operations/paste/paste-replacement.js +1 -1
- package/dist/tree-operations/paste/replace-block-at-parent.d.ts +3 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +5 -2
- package/dist/tree-operations/paste/replacement-parse.d.ts +17 -0
- package/dist/tree-operations/paste/replacement-parse.js +22 -0
- package/dist/tree-operations/path-mutate.d.ts +1 -1
- package/dist/tree-operations/path-mutate.js +2 -1
- package/dist/tree-operations/reorder-unit.js +1 -1
- package/dist/tree-operations/reorder.d.ts +5 -2
- package/dist/tree-operations/reorder.js +55 -2
- package/dist/tree-operations/settle.d.ts +105 -0
- package/dist/tree-operations/settle.js +660 -0
- package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
- package/dist/tree-operations/table-grid-clipboard.js +90 -0
- package/dist/tree-operations/unshare.d.ts +15 -77
- package/dist/tree-operations/unshare.js +15 -166
- package/docs/guide/consumer-guide.md +136 -104
- package/docs/guide/plugin-api.md +73 -29
- package/docs/guide/plugin-guide.md +55 -23
- package/package.json +9 -7
- package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
- package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
- package/dist/components/blocks/table/TableGrip.svelte +0 -91
- package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
- package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
- package/dist/components/blocks/table/table-drop-target.js +0 -16
- package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
- package/dist/components/blocks/table/table-reorder-drag.js +0 -97
|
@@ -33,19 +33,21 @@
|
|
|
33
33
|
} from '../../../schema/inline-construct-policy';
|
|
34
34
|
import {
|
|
35
35
|
cycleHeading,
|
|
36
|
+
demoteEmptyAtxHeading,
|
|
36
37
|
demoteToParagraph,
|
|
37
38
|
insertHardBreak,
|
|
38
39
|
insertLiteralTab,
|
|
39
40
|
type TextEditResult
|
|
40
41
|
} from './text-keydown';
|
|
41
42
|
import { tryGetBlockKindDescriptor } from '../../../schema/block-kind-descriptor';
|
|
42
|
-
import { blockNodeAt } from '../../../tree-operations/node-
|
|
43
|
+
import { blockNodeAt } from '../../../tree-operations/node-primitives';
|
|
43
44
|
import { createTextClipboard } from './text-clipboard';
|
|
44
45
|
import { createTextRender } from './text-render';
|
|
45
46
|
import { createWidgetInteraction } from './widget-interaction';
|
|
46
|
-
import { createEdgePolicyDispatch } from './edge-policy-dispatch';
|
|
47
|
+
import { createEdgePolicyDispatch, keepsBlockKind } from './edge-policy-dispatch';
|
|
47
48
|
import { hidesStructuralSuffix } from './hidden-suffix';
|
|
48
49
|
import { applyLiveRangeEdit, resolveSelectionEdit } from './live-selection-edit';
|
|
50
|
+
import { applyDelimiterAutoPair } from './delimiter-autopair';
|
|
49
51
|
import { createCompositionSeat } from './composition-seat';
|
|
50
52
|
import { createConstructReveal } from './construct-reveal';
|
|
51
53
|
import { assertInvariant } from '../../../assert';
|
|
@@ -56,7 +58,11 @@
|
|
|
56
58
|
handleSharedKeydown,
|
|
57
59
|
handleSharedBeforeInput
|
|
58
60
|
} from '../../../selection/shared-keydown';
|
|
59
|
-
import {
|
|
61
|
+
import {
|
|
62
|
+
createEditableSurface,
|
|
63
|
+
consumePendingRestore,
|
|
64
|
+
withKeydownVerdict
|
|
65
|
+
} from '../editable-surface';
|
|
60
66
|
import { wireSurfaceContexts, useParkFocusOnUnmount } from '../surface-wiring.svelte';
|
|
61
67
|
import {
|
|
62
68
|
domTextOffsetAtNode,
|
|
@@ -76,6 +82,7 @@
|
|
|
76
82
|
import { createAmbientCursorIO } from '../../../ambient/ambient-cursor';
|
|
77
83
|
import { type CommandId } from '../../../schema/commands';
|
|
78
84
|
import { reorderRunCommand } from '../../../editor-actions/reorder-action';
|
|
85
|
+
import { planTypedCompletion } from '../../../editor-actions/enter-completion';
|
|
79
86
|
import {
|
|
80
87
|
perfEnabled,
|
|
81
88
|
recordBlockRender,
|
|
@@ -111,6 +118,12 @@
|
|
|
111
118
|
const ambientPrefixText = $derived(
|
|
112
119
|
typeof ambientPrefix === 'string' ? ambientPrefix : ambientPrefix.text
|
|
113
120
|
);
|
|
121
|
+
// The hanging indent is the prefix's painted width: its text width by default, or what a
|
|
122
|
+
// prefix painting itself as chrome (the task box) declares.
|
|
123
|
+
const ambientIndent = $derived(
|
|
124
|
+
(typeof ambientPrefix === 'string' ? undefined : ambientPrefix.indent) ??
|
|
125
|
+
`${ambientPrefixText.length}ch`
|
|
126
|
+
);
|
|
114
127
|
|
|
115
128
|
const wiring = wireSurfaceContexts();
|
|
116
129
|
const {
|
|
@@ -361,6 +374,7 @@
|
|
|
361
374
|
widgetInteraction.enterWidget(widget, fromTrailingEdge),
|
|
362
375
|
isReading: () => readOnly,
|
|
363
376
|
getEdgeAffinity: () => edgeAffinity.get(),
|
|
377
|
+
noteOutside: edgeAffinity.noteExtreme,
|
|
364
378
|
pendingMarks,
|
|
365
379
|
installedAs: 'block'
|
|
366
380
|
});
|
|
@@ -668,9 +682,9 @@
|
|
|
668
682
|
// Only while this block still owns focus: a blur-commit also arms a pending
|
|
669
683
|
// offset, and restoring would yank the selection back into the blurred block.
|
|
670
684
|
// The clear runs regardless, so a skipped restore is dropped, never re-armed.
|
|
671
|
-
const applied = consumePendingRestore(el ?? null, pendingCursorOffset, (offset) =>
|
|
672
|
-
cursor.setRaw(asRawOffset(offset))
|
|
673
|
-
);
|
|
685
|
+
const applied = consumePendingRestore(el ?? null, pendingCursorOffset, (offset) => {
|
|
686
|
+
if (!widgetInteraction.revealInterior(offset)) cursor.setRaw(asRawOffset(offset));
|
|
687
|
+
});
|
|
674
688
|
tracePendingCursorConsume(pendingCursorOffset, applied);
|
|
675
689
|
pendingCursorOffset = null;
|
|
676
690
|
}
|
|
@@ -815,6 +829,8 @@
|
|
|
815
829
|
if (wiring.dispatchChord(e, { kind: node.kind, runCommand })) return;
|
|
816
830
|
}
|
|
817
831
|
|
|
832
|
+
const onKeyDownTraced = withKeydownVerdict(onKeyDown);
|
|
833
|
+
|
|
818
834
|
/**
|
|
819
835
|
* A native ranged edit inside ONE block, in a mode that paints no delimiter: the engine would
|
|
820
836
|
* write the runs the range crossed literally, so the edit goes through the join seam instead.
|
|
@@ -837,9 +853,33 @@
|
|
|
837
853
|
);
|
|
838
854
|
}
|
|
839
855
|
|
|
856
|
+
// The write takes the ranged edit's road above, so the surface repaints once with the caret
|
|
857
|
+
// inside the pair. During a reveal the caret indexes the DOM text, which has outrun `node.raw`.
|
|
858
|
+
function handleDelimiterAutoPair(e: InputEvent): boolean {
|
|
859
|
+
return applyDelimiterAutoPair(e, {
|
|
860
|
+
text: () => (widgetInteraction.isRevealing() ? readRawText() : getDisplayText()),
|
|
861
|
+
content: () => getContentRange(node),
|
|
862
|
+
caret: () => cursor.getRaw(),
|
|
863
|
+
hasSelection: () => cursor.getRawSelection() !== null,
|
|
864
|
+
isRevealing: widgetInteraction.isRevealing,
|
|
865
|
+
foldReveal: () => widgetInteraction.foldRevealBeforeMutation(),
|
|
866
|
+
markersPaint: () => paintsFocusedMarkers(presentationMode),
|
|
867
|
+
setCaret: (offset) => cursor.setRaw(asRawOffset(offset)),
|
|
868
|
+
seatOutside: edgeAffinity.noteExtreme,
|
|
869
|
+
completesLine: (caret) => planTypedCompletion(node, caret) !== null,
|
|
870
|
+
keepsBlockKind: (text) => keepsBlockKind(node, text),
|
|
871
|
+
write: (text, caretBefore, caretAfter) => {
|
|
872
|
+
const raw = text + trailingLineEnding(node.raw);
|
|
873
|
+
void blockEdit.updateBlockContent(index, raw, caretBefore, caretAfter);
|
|
874
|
+
setPendingCursorOffset(caretAfter, 'delimiter-autopair');
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
840
879
|
async function onBeforeInput(e: InputEvent): Promise<void> {
|
|
841
880
|
if (await handleSharedBeforeInput(e, sharedCtx)) return;
|
|
842
881
|
if (handleLiveSelectionEdit(e)) return;
|
|
882
|
+
if (handleDelimiterAutoPair(e)) return;
|
|
843
883
|
// Soft-keyboard/IME insertLineBreak slipped past onKeyDown — swallow; Shift+Enter there owns hard breaks.
|
|
844
884
|
if (e.inputType === 'insertLineBreak') {
|
|
845
885
|
e.preventDefault();
|
|
@@ -868,6 +908,13 @@
|
|
|
868
908
|
// Persist a revealed source edit before the caret is gone.
|
|
869
909
|
widgetInteraction.commitRevealOnBlur();
|
|
870
910
|
lastSnapTargetOffset = null;
|
|
911
|
+
demoteEmptyHeadingOnBlur();
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function demoteEmptyHeadingOnBlur(): void {
|
|
915
|
+
if (readOnly || node.kind !== 'heading' || editableSurface.isDetached()) return;
|
|
916
|
+
const demoted = demoteEmptyAtxHeading(node.raw, getContentRange(node));
|
|
917
|
+
if (demoted) void blockEdit.updateBlockContent(index, demoted.newRaw, 0);
|
|
871
918
|
}
|
|
872
919
|
|
|
873
920
|
function onClick(e: MouseEvent): void {
|
|
@@ -933,10 +980,10 @@
|
|
|
933
980
|
contenteditable={readOnly ? 'false' : 'true'}
|
|
934
981
|
aria-readonly={readOnly ? 'true' : undefined}
|
|
935
982
|
role="textbox"
|
|
936
|
-
style:text-indent={ambientPrefixText ? `-${
|
|
937
|
-
style:padding-left={ambientPrefixText ?
|
|
983
|
+
style:text-indent={ambientPrefixText ? `-${ambientIndent}` : null}
|
|
984
|
+
style:padding-left={ambientPrefixText ? ambientIndent : null}
|
|
938
985
|
oninput={onInput}
|
|
939
|
-
onkeydown={
|
|
986
|
+
onkeydown={onKeyDownTraced}
|
|
940
987
|
onbeforeinput={onBeforeInput}
|
|
941
988
|
oncopy={clipboardHandlers.onCopy}
|
|
942
989
|
oncut={clipboardHandlers.onCut}
|
|
@@ -986,7 +1033,7 @@
|
|
|
986
1033
|
}
|
|
987
1034
|
|
|
988
1035
|
.text-editable-block.raw-block {
|
|
989
|
-
font-family: var(--font-
|
|
1036
|
+
font-family: var(--font-code, ui-monospace, monospace);
|
|
990
1037
|
font-size: 0.9em;
|
|
991
1038
|
opacity: 0.85;
|
|
992
1039
|
}
|
|
@@ -998,7 +1045,7 @@
|
|
|
998
1045
|
}
|
|
999
1046
|
|
|
1000
1047
|
.text-editable-block :global(.inline-code-content) {
|
|
1001
|
-
font-family: var(--font-
|
|
1048
|
+
font-family: var(--font-code, ui-monospace, monospace);
|
|
1002
1049
|
font-size: 0.9em;
|
|
1003
1050
|
background: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
|
|
1004
1051
|
border-radius: 3px;
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
/** Caret in a real text node the reader can see: the native caret renders there, so the
|
|
3
3
|
* synthetic indicator stands down. Hidden marker text renders nothing, so it does not count. */
|
|
4
4
|
export declare function caretIsInTextContent(el: HTMLElement, sel: Selection | null): boolean;
|
|
5
|
+
/** A ranged selection this surface holds — the click ladder's rungs, a shift-click, a
|
|
6
|
+
* drag-select. It is the gesture's own paint, so a click-time caret seat stands down for it. */
|
|
7
|
+
export declare function surfaceHoldsRange(el: HTMLElement, sel: Selection | null): boolean;
|
|
5
8
|
/** Ctrl/meta/alt held — a platform command, never an edit of the byte beside the caret. */
|
|
6
9
|
export declare function hasModifier(e: KeyboardEvent): boolean;
|
|
7
10
|
/** A typed character: no modifier chord, one code point. A chord is a command instead. */
|
|
@@ -9,6 +9,14 @@ export function caretIsInTextContent(el, sel) {
|
|
|
9
9
|
const node = sel.getRangeAt(0).startContainer;
|
|
10
10
|
return node.nodeType === Node.TEXT_NODE && el.contains(node) && !isHiddenMarkerText(node, el);
|
|
11
11
|
}
|
|
12
|
+
/** A ranged selection this surface holds — the click ladder's rungs, a shift-click, a
|
|
13
|
+
* drag-select. It is the gesture's own paint, so a click-time caret seat stands down for it. */
|
|
14
|
+
export function surfaceHoldsRange(el, sel) {
|
|
15
|
+
if (!sel || sel.isCollapsed || sel.rangeCount === 0)
|
|
16
|
+
return false;
|
|
17
|
+
const range = sel.getRangeAt(0);
|
|
18
|
+
return el.contains(range.startContainer) || el.contains(range.endContainer);
|
|
19
|
+
}
|
|
12
20
|
/** Ctrl/meta/alt held — a platform command, never an edit of the byte beside the caret. */
|
|
13
21
|
export function hasModifier(e) {
|
|
14
22
|
return e.ctrlKey || e.metaKey || e.altKey;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A typed inline delimiter closes itself: a lone `$`, backtick, `*`, `_` or `~~` would pair with
|
|
3
|
+
* whatever matching run comes later on the line, so the keystroke lands its twin after the caret.
|
|
4
|
+
* Typing the closer over the twin steps past it, a closer typed by hand seats the next byte
|
|
5
|
+
* outside, and the empty pair drops its twin when the first body byte makes it no construct.
|
|
6
|
+
* `applyDelimiterAutoPair` is the one `beforeinput` arm every prose surface runs (G4.65).
|
|
7
|
+
*/
|
|
8
|
+
import { type ContentRange } from '../../../core/inline';
|
|
9
|
+
export type AutoPairEdit = {
|
|
10
|
+
kind: 'write';
|
|
11
|
+
text: string;
|
|
12
|
+
caret: number;
|
|
13
|
+
}
|
|
14
|
+
/** The typed byte completed a construct's closer; what follows belongs outside it. */
|
|
15
|
+
| {
|
|
16
|
+
kind: 'close';
|
|
17
|
+
text: string;
|
|
18
|
+
caret: number;
|
|
19
|
+
}
|
|
20
|
+
/** Nothing written: the caret passes the twin. Over a construct's closer that run may be
|
|
21
|
+
* unpainted, and then only the SIDE moves, which the surface records as an edge affinity. */
|
|
22
|
+
| {
|
|
23
|
+
kind: 'step-over';
|
|
24
|
+
caret: number;
|
|
25
|
+
overConstruct: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* What a single typed byte does at a collapsed caret, or null to leave the engine its insert.
|
|
29
|
+
* `content` bounds the inline scan (a heading's `# ` is not prose); the caret must lie inside it.
|
|
30
|
+
* `keepsBlockKind` says whether a written line still reloads as this block: a grown `****` is a
|
|
31
|
+
* thematic break and `~~~~` a fence, so such a pair steps past a twin instead, or declines.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveDelimiterAutoPair(text: string, content: ContentRange, caret: number, typed: string, keepsBlockKind?: (text: string) => boolean): AutoPairEdit | null;
|
|
34
|
+
/**
|
|
35
|
+
* Inside a revealed source (`$ab|$`) the twin is the live closer, and typing it means "done":
|
|
36
|
+
* the caret steps past it and the caller folds the reveal, so the formula renders at once.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stepsOverRevealedCloser(text: string, caret: number, typed: string): boolean;
|
|
39
|
+
/** Backspace at an empty pair takes both runs, between them (`**|**`) or right after them
|
|
40
|
+
* (`$$|`), as it does in any IDE. Exactly a pair of equal runs, one or two bytes each: a longer
|
|
41
|
+
* run is a fence or a literal the user built by hand. */
|
|
42
|
+
export declare function resolveEmptyPairBackspace(text: string, caret: number): AutoPairEdit | null;
|
|
43
|
+
/** What a prose surface lends the arm. Reactive reads are thunks, so nothing here goes stale. */
|
|
44
|
+
export interface AutoPairSurface {
|
|
45
|
+
/** The bytes the caret indexes: the display text, or the revealed DOM text during a reveal. */
|
|
46
|
+
text(): string;
|
|
47
|
+
content(): ContentRange;
|
|
48
|
+
caret(): number | null;
|
|
49
|
+
hasSelection(): boolean;
|
|
50
|
+
isRevealing(): boolean;
|
|
51
|
+
foldReveal(): {
|
|
52
|
+
settled: Promise<void>;
|
|
53
|
+
} | null;
|
|
54
|
+
/** Whether the closer a step-over passes is on screen; unpainted, only the side moves. */
|
|
55
|
+
markersPaint(): boolean;
|
|
56
|
+
setCaret(offset: number): void;
|
|
57
|
+
/** The arrival the seat reads next: past the construct's delimiters. */
|
|
58
|
+
seatOutside(): void;
|
|
59
|
+
/** One CST write plus the caret it parks. */
|
|
60
|
+
write(text: string, caretBefore: number, caretAfter: number): void;
|
|
61
|
+
/** A step-over that leaves the line one an on-type completer claims (`$$`), which only a
|
|
62
|
+
* content write consults. */
|
|
63
|
+
completesLine?(caret: number): boolean;
|
|
64
|
+
/** The resolver's block-kind guard, for a surface whose line can become another block. */
|
|
65
|
+
keepsBlockKind?(text: string): boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The `beforeinput` arm. True when the press was this seam's: the event is cancelled and the
|
|
69
|
+
* surface has written, moved or re-seated the caret.
|
|
70
|
+
*/
|
|
71
|
+
export declare function applyDelimiterAutoPair(e: InputEvent, surface: AutoPairSurface): boolean;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A typed inline delimiter closes itself: a lone `$`, backtick, `*`, `_` or `~~` would pair with
|
|
3
|
+
* whatever matching run comes later on the line, so the keystroke lands its twin after the caret.
|
|
4
|
+
* Typing the closer over the twin steps past it, a closer typed by hand seats the next byte
|
|
5
|
+
* outside, and the empty pair drops its twin when the first body byte makes it no construct.
|
|
6
|
+
* `applyDelimiterAutoPair` is the one `beforeinput` arm every prose surface runs (G4.65).
|
|
7
|
+
*/
|
|
8
|
+
import { constructContentRange, inlineDescendants, parseInline } from '../../../core/inline';
|
|
9
|
+
import { isAutoPairTrigger } from '../../../core/inline/scan/plugin-syntax';
|
|
10
|
+
const STEP = { single: true, inside: 'step-over' };
|
|
11
|
+
const BUILTIN = {
|
|
12
|
+
'`': STEP,
|
|
13
|
+
'*': { single: true, inside: 'grow', notAfterWord: true },
|
|
14
|
+
_: { single: true, inside: 'grow', notAfterWord: true },
|
|
15
|
+
// `~5 minutes` would strike the 5 (GFM takes a single tilde), so only `~~` pairs.
|
|
16
|
+
'~': { single: false, inside: 'grow' }
|
|
17
|
+
};
|
|
18
|
+
const isWordByte = (ch) => ch !== undefined && /[\p{L}\p{N}]/u.test(ch);
|
|
19
|
+
function policyOf(ch) {
|
|
20
|
+
return BUILTIN[ch] ?? (isAutoPairTrigger(ch) ? STEP : null);
|
|
21
|
+
}
|
|
22
|
+
// ── The resolver ─────────────────────────────────────────────────────────────
|
|
23
|
+
/**
|
|
24
|
+
* What a single typed byte does at a collapsed caret, or null to leave the engine its insert.
|
|
25
|
+
* `content` bounds the inline scan (a heading's `# ` is not prose); the caret must lie inside it.
|
|
26
|
+
* `keepsBlockKind` says whether a written line still reloads as this block: a grown `****` is a
|
|
27
|
+
* thematic break and `~~~~` a fence, so such a pair steps past a twin instead, or declines.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveDelimiterAutoPair(text, content, caret, typed, keepsBlockKind = () => true) {
|
|
30
|
+
if (typed.length !== 1 || caret < content.start || caret > content.end)
|
|
31
|
+
return null;
|
|
32
|
+
const before = text[caret - 1];
|
|
33
|
+
const after = text[caret];
|
|
34
|
+
const policy = policyOf(typed);
|
|
35
|
+
if (!policy)
|
|
36
|
+
return collapseEmptyPair(text, content, caret, typed);
|
|
37
|
+
if (after === typed && closingRunAt(text, content, caret, typed)) {
|
|
38
|
+
return { kind: 'step-over', caret: caret + 1, overConstruct: true };
|
|
39
|
+
}
|
|
40
|
+
const paired = text.slice(0, caret) + typed + text.slice(caret);
|
|
41
|
+
if (closerEndsAt(paired, content, caret + 1, typed)) {
|
|
42
|
+
return { kind: 'close', text: paired, caret: caret + 1 };
|
|
43
|
+
}
|
|
44
|
+
const pair = (next) => {
|
|
45
|
+
if (keepsBlockKind(next))
|
|
46
|
+
return write(next, caret + 1);
|
|
47
|
+
return after === typed ? { kind: 'step-over', caret: caret + 1, overConstruct: false } : null;
|
|
48
|
+
};
|
|
49
|
+
if (after === typed) {
|
|
50
|
+
if (before === typed) {
|
|
51
|
+
if (policy.inside === 'step-over') {
|
|
52
|
+
return { kind: 'step-over', caret: caret + 1, overConstruct: false };
|
|
53
|
+
}
|
|
54
|
+
return pair(text.slice(0, caret) + typed + typed + text.slice(caret));
|
|
55
|
+
}
|
|
56
|
+
// A byte in front of another run's opener is spelling something out, not opening a span.
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
if (before === typed && !closerEndsAt(text, content, caret, typed)) {
|
|
60
|
+
// `~|` plus `~`: the double run this delimiter pairs on, as long as it IS a lone run.
|
|
61
|
+
const single = text[caret - 2] !== typed;
|
|
62
|
+
if (policy.inside === 'grow' && single) {
|
|
63
|
+
return pair(text.slice(0, caret) + typed.repeat(3) + text.slice(caret));
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (!policy.single || (policy.notAfterWord && isWordByte(before)))
|
|
68
|
+
return null;
|
|
69
|
+
return pair(text.slice(0, caret) + typed + typed + text.slice(caret));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Inside a revealed source (`$ab|$`) the twin is the live closer, and typing it means "done":
|
|
73
|
+
* the caret steps past it and the caller folds the reveal, so the formula renders at once.
|
|
74
|
+
*/
|
|
75
|
+
export function stepsOverRevealedCloser(text, caret, typed) {
|
|
76
|
+
return typed.length === 1 && policyOf(typed) !== null && text[caret] === typed;
|
|
77
|
+
}
|
|
78
|
+
/** Backspace at an empty pair takes both runs, between them (`**|**`) or right after them
|
|
79
|
+
* (`$$|`), as it does in any IDE. Exactly a pair of equal runs, one or two bytes each: a longer
|
|
80
|
+
* run is a fence or a literal the user built by hand. */
|
|
81
|
+
export function resolveEmptyPairBackspace(text, caret) {
|
|
82
|
+
const pair = emptyPairEnding(text, caret) ?? emptyPairAround(text, caret);
|
|
83
|
+
if (!pair)
|
|
84
|
+
return null;
|
|
85
|
+
return write(text.slice(0, pair.start) + text.slice(pair.end), pair.start);
|
|
86
|
+
}
|
|
87
|
+
const write = (text, caret) => ({ kind: 'write', text, caret });
|
|
88
|
+
/**
|
|
89
|
+
* The `beforeinput` arm. True when the press was this seam's: the event is cancelled and the
|
|
90
|
+
* surface has written, moved or re-seated the caret.
|
|
91
|
+
*/
|
|
92
|
+
export function applyDelimiterAutoPair(e, surface) {
|
|
93
|
+
const typing = e.inputType === 'insertText';
|
|
94
|
+
if (!typing && e.inputType !== 'deleteContentBackward')
|
|
95
|
+
return false;
|
|
96
|
+
if (e.isComposing || surface.hasSelection())
|
|
97
|
+
return false;
|
|
98
|
+
const caret = surface.caret();
|
|
99
|
+
if (caret === null)
|
|
100
|
+
return false;
|
|
101
|
+
const text = surface.text();
|
|
102
|
+
if (surface.isRevealing()) {
|
|
103
|
+
if (!typing || !stepsOverRevealedCloser(text, caret, e.data ?? ''))
|
|
104
|
+
return false;
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
surface.setCaret(caret + 1);
|
|
107
|
+
void surface.foldReveal()?.settled;
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const edit = typing
|
|
111
|
+
? resolveDelimiterAutoPair(text, surface.content(), caret, e.data ?? '', surface.keepsBlockKind)
|
|
112
|
+
: resolveEmptyPairBackspace(text, caret);
|
|
113
|
+
if (!edit)
|
|
114
|
+
return false;
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
switch (edit.kind) {
|
|
117
|
+
case 'step-over':
|
|
118
|
+
if (edit.overConstruct && !surface.markersPaint())
|
|
119
|
+
surface.seatOutside();
|
|
120
|
+
else if (surface.completesLine?.(edit.caret))
|
|
121
|
+
surface.write(text, caret, edit.caret);
|
|
122
|
+
else
|
|
123
|
+
surface.setCaret(edit.caret);
|
|
124
|
+
return true;
|
|
125
|
+
case 'close':
|
|
126
|
+
surface.write(edit.text, caret, edit.caret);
|
|
127
|
+
surface.seatOutside();
|
|
128
|
+
return true;
|
|
129
|
+
case 'write':
|
|
130
|
+
surface.write(edit.text, caret, edit.caret);
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// ── Runs ─────────────────────────────────────────────────────────────────────
|
|
135
|
+
/** Length of the run of `ch` ending just before `at` (0 when `text[at - 1]` is not `ch`). */
|
|
136
|
+
function runBefore(text, at, ch) {
|
|
137
|
+
let n = 0;
|
|
138
|
+
while (text[at - 1 - n] === ch)
|
|
139
|
+
n++;
|
|
140
|
+
return n;
|
|
141
|
+
}
|
|
142
|
+
function runAfter(text, at, ch) {
|
|
143
|
+
let n = 0;
|
|
144
|
+
while (text[at + n] === ch)
|
|
145
|
+
n++;
|
|
146
|
+
return n;
|
|
147
|
+
}
|
|
148
|
+
// `X..X|X..X`: equal runs of a pair delimiter, one or two bytes, with nothing of it either side.
|
|
149
|
+
function emptyPairAround(text, caret) {
|
|
150
|
+
const d = text[caret - 1];
|
|
151
|
+
if (d === undefined || !policyOf(d))
|
|
152
|
+
return null;
|
|
153
|
+
const k = runBefore(text, caret, d);
|
|
154
|
+
if (k > 2 || runAfter(text, caret, d) !== k)
|
|
155
|
+
return null;
|
|
156
|
+
return { start: caret - k, end: caret + k };
|
|
157
|
+
}
|
|
158
|
+
// `XX|`: a stepped-over empty pair with the caret after it. Only the delimiters that step (the
|
|
159
|
+
// emphasis family grows instead, and its `**|` is a double opener with content ahead).
|
|
160
|
+
function emptyPairEnding(text, caret) {
|
|
161
|
+
const d = text[caret - 1];
|
|
162
|
+
if (d === undefined || policyOf(d)?.inside !== 'step-over' || text[caret] === d)
|
|
163
|
+
return null;
|
|
164
|
+
if (runBefore(text, caret, d) !== 2)
|
|
165
|
+
return null;
|
|
166
|
+
return { start: caret - 2, end: caret };
|
|
167
|
+
}
|
|
168
|
+
// ── Constructs ───────────────────────────────────────────────────────────────
|
|
169
|
+
// `X|X` plus the typed byte: keep the twin run only if the pair then parses as a construct.
|
|
170
|
+
function collapseEmptyPair(text, content, caret, typed) {
|
|
171
|
+
const d = text[caret - 1];
|
|
172
|
+
if (d === undefined || !policyOf(d))
|
|
173
|
+
return null;
|
|
174
|
+
const k = runBefore(text, caret, d);
|
|
175
|
+
if (k > 2 || runAfter(text, caret, d) !== k)
|
|
176
|
+
return null;
|
|
177
|
+
const paired = text.slice(0, caret) + typed + text.slice(caret);
|
|
178
|
+
const shifted = { start: content.start, end: content.end + 1 };
|
|
179
|
+
if (constructAt(paired, shifted, caret - k, caret + 1 + k))
|
|
180
|
+
return null;
|
|
181
|
+
return write(text.slice(0, caret) + typed + text.slice(caret + k), caret + 1);
|
|
182
|
+
}
|
|
183
|
+
// The caret sits inside or at the start of the closing run of a construct this delimiter both
|
|
184
|
+
// opens and closes, so the byte under it is that construct's own closer.
|
|
185
|
+
function closingRunAt(text, content, caret, typed) {
|
|
186
|
+
for (const node of inlineDescendants(parseInline(text, content.start, content.end))) {
|
|
187
|
+
if (node.kind === 'text' || text[node.start] !== typed || node.end <= caret)
|
|
188
|
+
continue;
|
|
189
|
+
let closer = node.end;
|
|
190
|
+
while (closer > caret && text[closer - 1] === typed)
|
|
191
|
+
closer--;
|
|
192
|
+
if (closer <= caret)
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// A construct of `typed`'s family ends exactly at `end`, its last byte in the closing run. The
|
|
198
|
+
// scan's bound grows past the content by the byte a caller has just inserted.
|
|
199
|
+
function closerEndsAt(text, content, end, typed) {
|
|
200
|
+
const bound = Math.max(content.end, end);
|
|
201
|
+
for (const node of inlineDescendants(parseInline(text, content.start, bound))) {
|
|
202
|
+
if (node.kind === 'text' || node.end !== end || text[end - 1] !== typed)
|
|
203
|
+
continue;
|
|
204
|
+
const inner = constructContentRange(node);
|
|
205
|
+
if (!inner || inner.end < end)
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
function constructAt(text, content, start, end) {
|
|
211
|
+
for (const node of inlineDescendants(parseInline(text, content.start, content.end))) {
|
|
212
|
+
if (node.kind !== 'text' && node.start === start && node.end === end)
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
@@ -13,6 +13,8 @@ import { type RawOffset } from '../../../cursor/coordinate-spaces';
|
|
|
13
13
|
import type { EdgeAffinity } from '../../../cursor/edge-affinity';
|
|
14
14
|
import type { PendingMarksState } from '../../../cursor/pending-marks';
|
|
15
15
|
import { type EdgeDeletionSurface } from './construct-edge-delete';
|
|
16
|
+
/** Whether `line` still reloads as `node`'s kind, the auto-pair resolver's guard. */
|
|
17
|
+
export declare function keepsBlockKind(node: NodeView, line: string): boolean;
|
|
16
18
|
/** The subset of the inline-widget vocabulary the internal island policies reuse,
|
|
17
19
|
* expressed in the same terms without leaking into the public API. */
|
|
18
20
|
export type EdgePolicy = Pick<InlineWidgetEditingPolicy, 'onEdge' | 'deleteGranularity'>;
|
|
@@ -70,6 +72,8 @@ export interface EdgePolicyDispatchDeps {
|
|
|
70
72
|
isReading: () => boolean;
|
|
71
73
|
/** The arrival side the typing seat consults; null when no arrival claimed one. */
|
|
72
74
|
getEdgeAffinity: () => EdgeAffinity | null;
|
|
75
|
+
/** A seated delimiter that completed a construct leaves the caret meaning outside it. */
|
|
76
|
+
noteOutside?: () => void;
|
|
73
77
|
/** The constructs a collapsed-caret toggle promised the next insertion. Read AND spent
|
|
74
78
|
* here: the first byte after the chord is the one insertion they were pending for. */
|
|
75
79
|
pendingMarks: PendingMarksState;
|