@voithos-labs/aragonite 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -21
- package/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/a11y-strings.d.ts +18 -0
- package/dist/a11y-strings.js +18 -0
- package/dist/action-contracts.d.ts +7 -1
- package/dist/ambient/ambient-dom.js +5 -1
- package/dist/block-component.d.ts +14 -0
- package/dist/components/BlockDragHandle.svelte +35 -24
- package/dist/components/BlockHost.svelte +16 -9
- package/dist/components/Editor.svelte +381 -198
- package/dist/components/Editor.svelte.d.ts +1 -1
- package/dist/components/SelectionOverlay.svelte +17 -3
- package/dist/components/TailInsert.svelte +107 -0
- package/dist/components/TailInsert.svelte.d.ts +17 -0
- package/dist/components/block-content-selector.d.ts +6 -2
- package/dist/components/block-content-selector.js +6 -2
- package/dist/components/blocks/ThematicBreakBlock.svelte +19 -6
- package/dist/components/blocks/ThematicBreakBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/code/CodeBlock.svelte +211 -30
- package/dist/components/blocks/code/CodeBlockRail.svelte +686 -0
- package/dist/components/blocks/code/CodeBlockRail.svelte.d.ts +26 -0
- package/dist/components/blocks/code/code-bootstrap.js +4 -0
- package/dist/components/blocks/code/code-context-actions.d.ts +1 -0
- package/dist/components/blocks/code/code-context-actions.js +24 -0
- package/dist/components/blocks/code/code-fence-exit.d.ts +15 -0
- package/dist/components/blocks/code/code-fence-exit.js +26 -0
- package/dist/components/blocks/code/code-languages.d.ts +6 -0
- package/dist/components/blocks/code/code-languages.js +11 -0
- package/dist/components/blocks/code/code-renderer.js +11 -0
- package/dist/components/blocks/directive/DirectiveContainerBlock.svelte +1 -1
- package/dist/components/blocks/editable-leaf.d.ts +37 -6
- package/dist/components/blocks/editable-leaf.js +243 -29
- package/dist/components/blocks/editable-surface.d.ts +9 -0
- package/dist/components/blocks/editable-surface.js +22 -3
- package/dist/components/blocks/list/ListItemBlock.svelte +3 -3
- package/dist/components/blocks/list/task-checkbox.d.ts +2 -0
- package/dist/components/blocks/list/task-checkbox.js +11 -2
- package/dist/components/blocks/surface-wiring.svelte.d.ts +4 -0
- package/dist/components/blocks/surface-wiring.svelte.js +8 -1
- package/dist/components/blocks/table/TableActionMenu.svelte +210 -86
- package/dist/components/blocks/table/TableActionMenu.svelte.d.ts +5 -0
- package/dist/components/blocks/table/TableBlock.svelte +205 -186
- package/dist/components/blocks/table/TableBlock.svelte.d.ts +1 -0
- package/dist/components/blocks/table/TableCellBlock.svelte +87 -22
- package/dist/components/blocks/table/TableRowBlock.svelte +4 -18
- package/dist/components/blocks/table/TableRowBlock.svelte.d.ts +0 -5
- package/dist/components/blocks/table/cell-clipboard.d.ts +10 -0
- package/dist/components/blocks/table/cell-clipboard.js +34 -1
- package/dist/components/blocks/table/cell-keydown-plan.d.ts +1 -1
- package/dist/components/blocks/table/cell-keydown-plan.js +3 -1
- package/dist/components/blocks/table/table-cell-paste.js +2 -1
- package/dist/components/blocks/table/table-menu-model.d.ts +35 -8
- package/dist/components/blocks/table/table-menu-model.js +36 -17
- package/dist/components/blocks/text/TextEditableBlock.svelte +58 -11
- package/dist/components/blocks/text/delimiter-autopair.d.ts +71 -0
- package/dist/components/blocks/text/delimiter-autopair.js +216 -0
- package/dist/components/blocks/text/edge-policy-dispatch.d.ts +4 -0
- package/dist/components/blocks/text/edge-policy-dispatch.js +56 -2
- package/dist/components/blocks/text/live-selection-edit.js +27 -0
- package/dist/components/blocks/text/text-keydown.d.ts +7 -1
- package/dist/components/blocks/text/text-keydown.js +11 -1
- package/dist/components/blocks/text/text-render.d.ts +1 -1
- package/dist/components/blocks/text/text-render.js +3 -1
- package/dist/components/blocks/text/widget-interaction.d.ts +3 -0
- package/dist/components/blocks/text/widget-interaction.js +144 -22
- package/dist/components/drag-handle.d.ts +35 -0
- package/dist/components/drag-handle.js +126 -0
- package/dist/components/editor-root-focus.d.ts +19 -0
- package/dist/components/editor-root-focus.js +67 -0
- package/dist/components/editor-root-geometry.d.ts +39 -0
- package/dist/components/editor-root-geometry.js +91 -0
- package/dist/components/editor-root-keydown.d.ts +1 -1
- package/dist/components/editor-root-keydown.js +12 -3
- package/dist/components/editor-root-listeners.d.ts +10 -6
- package/dist/components/editor-root-listeners.js +23 -22
- package/dist/components/editor-root-mode-flip.d.ts +36 -0
- package/dist/components/editor-root-mode-flip.js +92 -0
- package/dist/components/image/ImageOverlayHost.svelte +14 -6
- package/dist/components/image/ImageProperties.svelte +512 -70
- package/dist/components/image/ImageProperties.svelte.d.ts +6 -1
- package/dist/components/image/ImageResizeHandles.svelte +59 -34
- package/dist/components/image/image-crop.d.ts +39 -0
- package/dist/components/image/image-crop.js +74 -0
- package/dist/components/image/image-edit-commit.d.ts +1 -0
- package/dist/components/image/image-edit-commit.js +21 -5
- package/dist/components/image/image-source-bytes.js +10 -3
- package/dist/components/image/image-widget-editing.js +1 -0
- package/dist/components/image/widget-dom.js +5 -1
- package/dist/components/link-card/link-card-commit.js +1 -1
- package/dist/components/lrd-map-gate.js +1 -1
- package/dist/components/menu/BlockMenu.svelte +315 -0
- package/dist/components/menu/BlockMenu.svelte.d.ts +34 -0
- package/dist/components/menu/MenuIcon.svelte +153 -0
- package/dist/components/menu/MenuIcon.svelte.d.ts +51 -0
- package/dist/components/menu/clipboard-actions.d.ts +12 -0
- package/dist/components/menu/clipboard-actions.js +42 -0
- package/dist/components/menu/default-context-actions.d.ts +15 -0
- package/dist/components/menu/default-context-actions.js +76 -0
- package/dist/components/menu/flyout-placement.d.ts +6 -0
- package/dist/components/menu/flyout-placement.js +25 -0
- package/dist/core/inline/format-toggle.d.ts +12 -4
- package/dist/core/inline/format-toggle.js +94 -40
- package/dist/core/inline/image-dimensions.d.ts +3 -0
- package/dist/core/inline/image-dimensions.js +46 -10
- package/dist/core/inline/inline-widgets.d.ts +11 -0
- package/dist/core/inline/scan/brackets.js +1 -0
- package/dist/core/inline/scan/plugin-syntax.d.ts +8 -0
- package/dist/core/inline/scan/plugin-syntax.js +15 -1
- package/dist/core/inline-render.d.ts +6 -0
- package/dist/core/inline-render.js +32 -0
- package/dist/core/nodes.d.ts +14 -0
- package/dist/cursor/edge-affinity.js +2 -1
- package/dist/cursor/overlay-remeasure.js +8 -0
- package/dist/cursor/reveal-source.js +7 -2
- package/dist/cursor/widget-offset.d.ts +6 -0
- package/dist/cursor/widget-offset.js +61 -4
- package/dist/debug/interaction-trace.d.ts +4 -0
- package/dist/debug/interaction-trace.js +15 -0
- package/dist/decorations/decoration-state.svelte.js +1 -1
- package/dist/editor-actions/ancestry-folds.d.ts +2 -2
- package/dist/editor-actions/ancestry-folds.js +1 -1
- package/dist/editor-actions/block-edit-scope.js +1 -1
- package/dist/editor-actions/commit/text-batch.d.ts +3 -2
- package/dist/editor-actions/commit/text-batch.js +1 -1
- package/dist/editor-actions/commit/undo-controller.js +4 -2
- package/dist/editor-actions/container-edit.js +2 -1
- package/dist/editor-actions/enter-completion.d.ts +2 -0
- package/dist/editor-actions/enter-completion.js +23 -2
- package/dist/editor-actions/inline-range-commit.js +1 -1
- package/dist/editor-actions/reorder-action.js +19 -10
- package/dist/editor-actions/reorder-drag.js +29 -1
- package/dist/editor-actions/replacement-focus.d.ts +1 -1
- package/dist/editor-actions/replacement-focus.js +1 -1
- package/dist/editor-actions/search-replace.js +1 -1
- package/dist/editor-actions/table-context.d.ts +4 -1
- package/dist/editor-actions/table-context.js +57 -1
- package/dist/editor-events.d.ts +3 -0
- package/dist/editor-keys.d.ts +33 -0
- package/dist/editor-props.d.ts +21 -12
- package/dist/index.d.ts +1 -1
- package/dist/plugin.d.ts +6 -0
- package/dist/plugin.js +11 -0
- package/dist/plugins/latex/BlockMath.svelte +264 -29
- package/dist/plugins/latex/BlockMath.svelte.d.ts +2 -0
- package/dist/plugins/latex/index.d.ts +2 -1
- package/dist/plugins/latex/latex-kind.js +36 -3
- package/dist/plugins/latex/math-completion.js +4 -1
- package/dist/plugins/latex/math-layout.d.ts +15 -0
- package/dist/plugins/latex/math-layout.js +13 -0
- package/dist/plugins/latex/math-source.d.ts +19 -0
- package/dist/plugins/latex/math-source.js +97 -0
- package/dist/plugins/latex/register.d.ts +11 -2
- package/dist/plugins/latex/register.js +3 -1
- package/dist/plugins/latex/renderer.d.ts +3 -3
- package/dist/plugins/latex/renderer.js +13 -6
- package/dist/reactivity/list-windowing.svelte.d.ts +8 -8
- package/dist/reactivity/list-windowing.svelte.js +49 -24
- package/dist/schema/block-completions.d.ts +8 -0
- package/dist/schema/block-completions.js +11 -0
- package/dist/schema/context-actions.d.ts +31 -0
- package/dist/schema/context-actions.js +23 -0
- package/dist/schema/fenced-code-raw.js +31 -1
- package/dist/schema/operations.d.ts +8 -1
- package/dist/schema/reserved-chords.js +25 -4
- package/dist/schema/table-cell-raw.d.ts +1 -1
- package/dist/schema/table-cell-raw.js +1 -1
- package/dist/selection/block-hit-test.js +3 -2
- package/dist/selection/char-endpoint-snap.js +1 -1
- package/dist/selection/clipboard-text.js +6 -1
- package/dist/selection/covered-block.d.ts +10 -0
- package/dist/selection/covered-block.js +24 -0
- package/dist/selection/cross-block/dispatch.d.ts +3 -0
- package/dist/selection/cross-block/dispatch.js +13 -1
- package/dist/selection/cross-block/format-range.d.ts +1 -1
- package/dist/selection/cross-block/format-range.js +4 -15
- package/dist/selection/cross-block/format-toggle.js +1 -1
- package/dist/selection/cross-block/keydown.js +1 -1
- package/dist/selection/cross-block/ops.js +1 -1
- package/dist/selection/cross-block/paste.js +32 -30
- package/dist/selection/cross-block/type-replace.d.ts +3 -2
- package/dist/selection/cross-block/type-replace.js +62 -13
- package/dist/selection/dead-space-caret.d.ts +10 -0
- package/dist/selection/dead-space-caret.js +47 -1
- package/dist/selection/double-click-trim.d.ts +17 -0
- package/dist/selection/double-click-trim.js +57 -0
- package/dist/selection/drag-pointer.d.ts +7 -2
- package/dist/selection/drag-pointer.js +61 -2
- package/dist/selection/gap-caret.js +1 -1
- package/dist/selection/keyboard-extend.js +1 -1
- package/dist/selection/path-lookup.js +1 -1
- package/dist/selection/range-delete-ceremony.js +4 -2
- package/dist/selection/range-delete-chrome.js +2 -1
- package/dist/selection/range-delete-table-coverage.js +2 -1
- package/dist/selection/range-delete-table.js +3 -2
- package/dist/selection/range-delete.js +30 -2
- package/dist/selection/selection-restore.js +1 -1
- package/dist/selection/selection-state.svelte.d.ts +6 -0
- package/dist/selection/selection-state.svelte.js +36 -1
- package/dist/selection/table-endpoint-snap.js +1 -1
- package/dist/selection/table-rect-extend.js +1 -1
- package/dist/styles/editor-theme.css +57 -28
- package/dist/styles/editor.css +217 -18
- package/dist/testing/container-conformance.js +2 -2
- package/dist/testing/inline-conformance.js +2 -1
- package/dist/tree-operations/blockquote.js +1 -1
- package/dist/tree-operations/chain-rebuild.d.ts +63 -0
- package/dist/tree-operations/chain-rebuild.js +142 -0
- package/dist/tree-operations/children.d.ts +1 -1
- package/dist/tree-operations/children.js +1 -1
- package/dist/tree-operations/cleanup.js +1 -1
- package/dist/tree-operations/content-write.d.ts +50 -0
- package/dist/tree-operations/content-write.js +263 -0
- package/dist/tree-operations/index.d.ts +8 -3
- package/dist/tree-operations/index.js +6 -2
- package/dist/tree-operations/list/exit-replacement.js +1 -1
- package/dist/tree-operations/list/unwrap-merge.js +3 -3
- package/dist/tree-operations/node-ops.d.ts +17 -234
- package/dist/tree-operations/node-ops.js +47 -1113
- package/dist/tree-operations/node-primitives.d.ts +75 -0
- package/dist/tree-operations/node-primitives.js +117 -0
- package/dist/tree-operations/paste/apply.js +1 -1
- package/dist/tree-operations/paste/body-write.d.ts +1 -1
- package/dist/tree-operations/paste/body-write.js +2 -2
- package/dist/tree-operations/paste/container-match.js +4 -2
- package/dist/tree-operations/paste/dispatch.js +2 -1
- package/dist/tree-operations/paste/find-enclosing-list.js +1 -1
- package/dist/tree-operations/paste/focus-target.d.ts +1 -1
- package/dist/tree-operations/paste/list-absorb.js +1 -1
- package/dist/tree-operations/paste/list-break-out.js +1 -1
- package/dist/tree-operations/paste/parent-scope.js +1 -1
- package/dist/tree-operations/paste/paste-replacement.js +1 -1
- package/dist/tree-operations/paste/replace-block-at-parent.js +1 -1
- package/dist/tree-operations/path-mutate.d.ts +1 -1
- package/dist/tree-operations/path-mutate.js +2 -1
- package/dist/tree-operations/reorder-unit.js +1 -1
- package/dist/tree-operations/reorder.d.ts +5 -2
- package/dist/tree-operations/reorder.js +55 -2
- package/dist/tree-operations/settle.d.ts +105 -0
- package/dist/tree-operations/settle.js +660 -0
- package/dist/tree-operations/table-grid-clipboard.d.ts +21 -0
- package/dist/tree-operations/table-grid-clipboard.js +90 -0
- package/dist/tree-operations/unshare.d.ts +15 -77
- package/dist/tree-operations/unshare.js +15 -166
- package/docs/guide/consumer-guide.md +126 -96
- package/docs/guide/plugin-api.md +31 -3
- package/docs/guide/plugin-guide.md +26 -3
- package/package.json +4 -2
- package/dist/components/blocks/code/CodeLanguageChip.svelte +0 -127
- package/dist/components/blocks/code/CodeLanguageChip.svelte.d.ts +0 -14
- package/dist/components/blocks/table/TableGrip.svelte +0 -91
- package/dist/components/blocks/table/TableGrip.svelte.d.ts +0 -8
- package/dist/components/blocks/table/table-drop-target.d.ts +0 -1
- package/dist/components/blocks/table/table-drop-target.js +0 -16
- package/dist/components/blocks/table/table-reorder-drag.d.ts +0 -78
- package/dist/components/blocks/table/table-reorder-drag.js +0 -97
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor-root geometry observers: the width and viewport-height watchers, the type-scale probe
|
|
3
|
+
* and the header-slot compensation. Each observes the element its installing `$effect`
|
|
4
|
+
* captured and returns the teardown; the reactive signals they feed (`widthVersion`,
|
|
5
|
+
* `typeScale`, `viewportHeightVersion`) stay in `Editor.svelte`, which is what the rest of the
|
|
6
|
+
* root reads. ResizeObserver's per-callback batching is the coalescing; no timer debounce (G4.4).
|
|
7
|
+
*/
|
|
8
|
+
import { ESTIMATE_BASE_FONT_SIZE } from '../cursor/typography-estimates';
|
|
9
|
+
import { onRoot, removeAll } from './editor-root-listeners';
|
|
10
|
+
// ── Width and height ────────────────────────────────────────────────
|
|
11
|
+
/** Calls `onWidthChange` when `el`'s width moves. A height-only resize is ignored: only a
|
|
12
|
+
* re-wrap stales the measured heights. */
|
|
13
|
+
export function installWidthWatcher(el, onWidthChange) {
|
|
14
|
+
let lastWidth = el.clientWidth;
|
|
15
|
+
const observer = new ResizeObserver(() => {
|
|
16
|
+
const width = el.clientWidth;
|
|
17
|
+
if (width === lastWidth)
|
|
18
|
+
return;
|
|
19
|
+
lastWidth = width;
|
|
20
|
+
onWidthChange();
|
|
21
|
+
});
|
|
22
|
+
observer.observe(el);
|
|
23
|
+
return () => observer.disconnect();
|
|
24
|
+
}
|
|
25
|
+
/** Calls `bump` on a height change of the resolved scrollport `target`. */
|
|
26
|
+
export function installViewportHeightWatcher(target, bump) {
|
|
27
|
+
if (target === window) {
|
|
28
|
+
// The page viewport has no box to observe, and a visualViewport move (a mobile URL
|
|
29
|
+
// bar retracting) never touches documentElement's height — hence both, ungated.
|
|
30
|
+
const visual = window.visualViewport;
|
|
31
|
+
return removeAll(onRoot(window, 'resize', bump), visual ? onRoot(visual, 'resize', bump) : () => { });
|
|
32
|
+
}
|
|
33
|
+
// Cast, not a narrowing: `UserScrollport` is a union of object types, which `=== window`
|
|
34
|
+
// does not narrow — the same cast `createScrollport` makes on the same split.
|
|
35
|
+
const el = target;
|
|
36
|
+
let lastHeight = el.clientHeight;
|
|
37
|
+
const observer = new ResizeObserver(() => {
|
|
38
|
+
if (el.clientHeight === lastHeight)
|
|
39
|
+
return;
|
|
40
|
+
lastHeight = el.clientHeight;
|
|
41
|
+
bump();
|
|
42
|
+
});
|
|
43
|
+
observer.observe(el);
|
|
44
|
+
return () => observer.disconnect();
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The width watcher's sibling: a font-size move puts the height estimates off several-fold, so
|
|
48
|
+
* a document whose true height clears the activation watermark can fail to window at all. No
|
|
49
|
+
* other box in the root reports it, hence the `1em` probe; the scale is relative to the size
|
|
50
|
+
* `HEIGHT_ESTIMATES` were calibrated at.
|
|
51
|
+
*/
|
|
52
|
+
export function installTypeScaleProbe(el, deps) {
|
|
53
|
+
const apply = (fontSizePx) => {
|
|
54
|
+
const next = fontSizePx / ESTIMATE_BASE_FONT_SIZE;
|
|
55
|
+
// Sub-percent moves are sub-pixel on a line box — not worth a full rebuild.
|
|
56
|
+
if (!(next > 0) || Math.abs(next - deps.getScale()) < 0.01)
|
|
57
|
+
return;
|
|
58
|
+
deps.onScale(next);
|
|
59
|
+
};
|
|
60
|
+
apply(el.getBoundingClientRect().height);
|
|
61
|
+
const observer = new ResizeObserver((entries) => apply(borderBoxHeight(entries, el)));
|
|
62
|
+
observer.observe(el);
|
|
63
|
+
return () => observer.disconnect();
|
|
64
|
+
}
|
|
65
|
+
// Border boxes throughout, seed and fallback alike, so a browser without `borderBoxSize`
|
|
66
|
+
// computes the same delta.
|
|
67
|
+
function borderBoxHeight(entries, el) {
|
|
68
|
+
const box = entries[0]?.borderBoxSize?.[0];
|
|
69
|
+
return box ? box.blockSize : el.getBoundingClientRect().height;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The header slot's height lives outside the height model, so while the editor owns the
|
|
73
|
+
* correction a growing header would slide the document under the reader. Compensating from
|
|
74
|
+
* the SLOT's own resize composes with `correctAnchor` instead of double-correcting; a reveal
|
|
75
|
+
* already holding the scroll outranks it.
|
|
76
|
+
*/
|
|
77
|
+
export function installHeaderSlotCompensation(deps) {
|
|
78
|
+
const { el, port } = deps;
|
|
79
|
+
let lastHeight = el.getBoundingClientRect().height;
|
|
80
|
+
const observer = new ResizeObserver((entries) => {
|
|
81
|
+
const height = borderBoxHeight(entries, el);
|
|
82
|
+
const delta = height - lastHeight;
|
|
83
|
+
lastHeight = height;
|
|
84
|
+
if (delta === 0 || !deps.ownsScrollCorrection() || port.scrollTop() === 0)
|
|
85
|
+
return;
|
|
86
|
+
if (!deps.revealHoldsScroll())
|
|
87
|
+
port.setScrollTop(port.scrollTop() + delta);
|
|
88
|
+
});
|
|
89
|
+
observer.observe(el);
|
|
90
|
+
return () => observer.disconnect();
|
|
91
|
+
}
|
|
@@ -31,7 +31,7 @@ export interface EditorRootKeydownDeps {
|
|
|
31
31
|
* chords. `undefined` = every installed plugin. */
|
|
32
32
|
activation: PluginActivation | undefined;
|
|
33
33
|
onCommandError: CommandErrorSink;
|
|
34
|
-
crossBlock: Pick<CrossBlockHandlers, 'handleKeyDown'>;
|
|
34
|
+
crossBlock: Pick<CrossBlockHandlers, 'handleKeyDown' | 'insertText'>;
|
|
35
35
|
/** True for nodes in the host's `header` slot: they sit inside `root.contains`
|
|
36
36
|
* without being the editor's own content. */
|
|
37
37
|
isHostChrome(node: Node | null): boolean;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { claimsBodyChord, isForeignTextEntry } from '../active-editor';
|
|
10
10
|
import { isReservedUiChord, runGlobalChord } from '../schema/commands';
|
|
11
|
-
import { eventToChord } from '../schema/keybindings';
|
|
11
|
+
import { eventToChord, isCharacterKey } from '../schema/keybindings';
|
|
12
12
|
export function createEditorRootKeydown(deps) {
|
|
13
13
|
/**
|
|
14
14
|
* Search / Escape: focus inside this editor, or a search chord this instance
|
|
@@ -81,8 +81,17 @@ export function createEditorRootKeydown(deps) {
|
|
|
81
81
|
event.preventDefault();
|
|
82
82
|
return;
|
|
83
83
|
}
|
|
84
|
-
if (deps.isCrossBlock)
|
|
85
|
-
|
|
84
|
+
if (!deps.isCrossBlock)
|
|
85
|
+
return;
|
|
86
|
+
// A range whose blocks host no character position leaves no editing surface focused, so
|
|
87
|
+
// no `beforeinput` ever fires for a typed character and this is the only door it has.
|
|
88
|
+
// Composition still belongs to the browser, and a chorded key is not text.
|
|
89
|
+
if (isCharacterKey(event.key) && !event.isComposing && !event.ctrlKey && !event.metaKey) {
|
|
90
|
+
event.preventDefault();
|
|
91
|
+
void deps.crossBlock.insertText(event.key);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
void deps.crossBlock.handleKeyDown(event);
|
|
86
95
|
}
|
|
87
96
|
};
|
|
88
97
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Editor-root ambient listeners: the mod-active cursor tracker, the selectionchange
|
|
3
|
-
* bridge, and the
|
|
4
|
-
* installing `$effect` stays in `Editor.svelte` as a guard plus one install
|
|
5
|
-
* returning the teardown. `onRoot`/`removeAll` capture that add/remove pair once.
|
|
3
|
+
* bridge, the blur announcer and the double-click word select. Pure dispatch over live
|
|
4
|
+
* getters; each installing `$effect` stays in `Editor.svelte` as a guard plus one install
|
|
5
|
+
* call, returning the teardown. `onRoot`/`removeAll` capture that add/remove pair once.
|
|
6
6
|
*/
|
|
7
|
-
import type { UserScrollport } from '../cursor/scroll-ancestors';
|
|
8
7
|
export declare function onRoot<E extends Event>(target: EventTarget, type: string, handler: (event: E) => void, options?: {
|
|
9
8
|
capture?: boolean;
|
|
10
9
|
passive?: boolean;
|
|
@@ -40,5 +39,10 @@ export declare function installEditorBlurAnnouncer(deps: {
|
|
|
40
39
|
root: HTMLElement;
|
|
41
40
|
emit: () => void;
|
|
42
41
|
}): () => void;
|
|
43
|
-
/**
|
|
44
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Windows Chromium's double-click takes the space after the word, so the word is selected on
|
|
44
|
+
* the SECOND press, trimmed, with the native selection suppressed; the dblclick trim is the
|
|
45
|
+
* fallback. A press on an inline widget is that widget's own gesture (a footnote's double-click
|
|
46
|
+
* takes its whole token), so the root leaves it to the widget.
|
|
47
|
+
*/
|
|
48
|
+
export declare function installDoubleClickWordSelect(root: HTMLElement): () => void;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Editor-root ambient listeners: the mod-active cursor tracker, the selectionchange
|
|
3
|
-
* bridge, and the
|
|
4
|
-
* installing `$effect` stays in `Editor.svelte` as a guard plus one install
|
|
5
|
-
* returning the teardown. `onRoot`/`removeAll` capture that add/remove pair once.
|
|
3
|
+
* bridge, the blur announcer and the double-click word select. Pure dispatch over live
|
|
4
|
+
* getters; each installing `$effect` stays in `Editor.svelte` as a guard plus one install
|
|
5
|
+
* call, returning the teardown. `onRoot`/`removeAll` capture that add/remove pair once.
|
|
6
6
|
*/
|
|
7
7
|
import { tick } from 'svelte';
|
|
8
|
+
import { isEditableEventTarget } from '../editor-actions/whole-block-focus-surface';
|
|
9
|
+
import { selectWordAtPoint, trimDoubleClickSelection } from '../selection/double-click-trim';
|
|
8
10
|
// ── Listener plumbing ───────────────────────────────────────────────
|
|
9
11
|
// Typed off `Event`, not the per-target event maps: those names are type-only,
|
|
10
12
|
// and `target` ranges over every EventTarget the root effects listen on.
|
|
@@ -89,24 +91,23 @@ export function installEditorBlurAnnouncer(deps) {
|
|
|
89
91
|
};
|
|
90
92
|
return onRoot(deps.root, 'focusout', handler);
|
|
91
93
|
}
|
|
92
|
-
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let lastHeight = el.clientHeight;
|
|
104
|
-
const observer = new ResizeObserver(() => {
|
|
105
|
-
if (el.clientHeight === lastHeight)
|
|
94
|
+
/**
|
|
95
|
+
* Windows Chromium's double-click takes the space after the word, so the word is selected on
|
|
96
|
+
* the SECOND press, trimmed, with the native selection suppressed; the dblclick trim is the
|
|
97
|
+
* fallback. A press on an inline widget is that widget's own gesture (a footnote's double-click
|
|
98
|
+
* takes its whole token), so the root leaves it to the widget.
|
|
99
|
+
*/
|
|
100
|
+
export function installDoubleClickWordSelect(root) {
|
|
101
|
+
const onSecondPress = (e) => {
|
|
102
|
+
if (e.detail !== 2 || e.button !== 0 || e.shiftKey || e.ctrlKey || e.metaKey)
|
|
103
|
+
return;
|
|
104
|
+
if (!isEditableEventTarget(e.target) || pressesInlineWidget(e.target))
|
|
106
105
|
return;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (selectWordAtPoint(root.ownerDocument, e.clientX, e.clientY))
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
};
|
|
109
|
+
return removeAll(onRoot(root, 'mousedown', onSecondPress), onRoot(root, 'dblclick', () => trimDoubleClickSelection(root.ownerDocument)));
|
|
110
|
+
}
|
|
111
|
+
function pressesInlineWidget(target) {
|
|
112
|
+
return target instanceof Element && target.closest('[data-inline-widget]') !== null;
|
|
112
113
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor-root presentation-mode flip: carry the caret across a mode change. The pre phase is
|
|
3
|
+
* the last moment the outgoing mode owns the DOM (past it the mode's render key has rebuilt
|
|
4
|
+
* every block from its own CST bytes), so the caret is captured and the surface blurred there;
|
|
5
|
+
* the post phase drops the geometry the old markers set and re-seats the caret through the
|
|
6
|
+
* reveal road. The two `$effect`s stay in `Editor.svelte` as the mode read plus one call each.
|
|
7
|
+
*/
|
|
8
|
+
import type { EdgeAffinityState } from '../cursor/edge-affinity';
|
|
9
|
+
import type { HeightOracle } from '../cursor/height-oracle';
|
|
10
|
+
import type { EditorEvents } from '../editor-events';
|
|
11
|
+
import type { BlockElLookup } from '../editor-keys';
|
|
12
|
+
import type { PresentationMode } from '../presentation-mode';
|
|
13
|
+
import type { EditorSelection } from '../selection/primitives';
|
|
14
|
+
import type { SelectionState } from '../selection/selection-state.svelte';
|
|
15
|
+
export interface ModeFlipDeps {
|
|
16
|
+
/** Getters, never values: the root binds after construction and the mode moves. */
|
|
17
|
+
get editorEl(): HTMLElement | undefined;
|
|
18
|
+
get mode(): PresentationMode;
|
|
19
|
+
selection: Pick<SelectionState, 'isCrossBlock' | 'gapCaret' | 'clearGapCaret'>;
|
|
20
|
+
/** The public snapshot, which also answers for a native caret no block reports. */
|
|
21
|
+
getSelection(): EditorSelection | null;
|
|
22
|
+
getBlockElByPath: BlockElLookup;
|
|
23
|
+
isHostChrome(node: Node | null): boolean;
|
|
24
|
+
edgeAffinity: Pick<EdgeAffinityState, 'reset'>;
|
|
25
|
+
heightOracle: Pick<HeightOracle, 'dropMeasured'>;
|
|
26
|
+
events: EditorEvents;
|
|
27
|
+
/** The bare-mount restore road: a flip is a view operation and writes no scrollport. */
|
|
28
|
+
restoreCaret(path: number[], offset: number): Promise<unknown>;
|
|
29
|
+
}
|
|
30
|
+
export interface ModeFlip {
|
|
31
|
+
/** The `$effect.pre` half, run untracked: capture and blur while the outgoing mode owns the DOM. */
|
|
32
|
+
beforeFlip(to: PresentationMode): void;
|
|
33
|
+
/** The `$effect` half: reset mode-bound geometry, then restore the caret after the flush. */
|
|
34
|
+
afterFlip(to: PresentationMode): void;
|
|
35
|
+
}
|
|
36
|
+
export declare function createModeFlip(deps: ModeFlipDeps): ModeFlip;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor-root presentation-mode flip: carry the caret across a mode change. The pre phase is
|
|
3
|
+
* the last moment the outgoing mode owns the DOM (past it the mode's render key has rebuilt
|
|
4
|
+
* every block from its own CST bytes), so the caret is captured and the surface blurred there;
|
|
5
|
+
* the post phase drops the geometry the old markers set and re-seats the caret through the
|
|
6
|
+
* reveal road. The two `$effect`s stay in `Editor.svelte` as the mode read plus one call each.
|
|
7
|
+
*/
|
|
8
|
+
import { tick } from 'svelte';
|
|
9
|
+
import { isTextEntrySurface } from '../active-editor';
|
|
10
|
+
import { ambientLengthOf } from '../ambient/ambient-dom';
|
|
11
|
+
import { toClampedRawOffset } from '../cursor/coordinate-spaces';
|
|
12
|
+
import { domTextOffsetAtNode } from '../cursor/widget-offset';
|
|
13
|
+
import { findSurfacePathForElement } from '../selection/path-lookup';
|
|
14
|
+
export function createModeFlip(deps) {
|
|
15
|
+
let flipCaret = null;
|
|
16
|
+
let preFlipSeenMode = deps.mode;
|
|
17
|
+
let lastEffectiveMode = deps.mode;
|
|
18
|
+
/** The focused leaf's caret as (path, raw offset): the per-backend road while a leaf holds
|
|
19
|
+
* focus, else the native range a toggle click leaves behind while chrome takes focus. */
|
|
20
|
+
function captureCaret() {
|
|
21
|
+
if (deps.selection.isCrossBlock || deps.selection.gapCaret)
|
|
22
|
+
return null;
|
|
23
|
+
const focused = deps.getSelection()?.focus;
|
|
24
|
+
if (focused)
|
|
25
|
+
return { path: focused.path, offset: focused.offset };
|
|
26
|
+
const sel = window.getSelection();
|
|
27
|
+
const node = sel?.focusNode;
|
|
28
|
+
if (!node || !deps.editorEl?.contains(node) || deps.isHostChrome(node))
|
|
29
|
+
return null;
|
|
30
|
+
const el = node instanceof Element ? node : node.parentElement;
|
|
31
|
+
const path = findSurfacePathForElement(el);
|
|
32
|
+
if (!path)
|
|
33
|
+
return null;
|
|
34
|
+
const contentEl = deps.getBlockElByPath(path);
|
|
35
|
+
if (!contentEl?.contains(node))
|
|
36
|
+
return null;
|
|
37
|
+
const offset = toClampedRawOffset(domTextOffsetAtNode(contentEl, node, sel.focusOffset), ambientLengthOf(contentEl));
|
|
38
|
+
return { path, offset };
|
|
39
|
+
}
|
|
40
|
+
// A post-tick focus like every structural op's; the road clamps the saved offset into the
|
|
41
|
+
// destination mode's landable range. Yielding to a focused text-entry surface keeps the
|
|
42
|
+
// restore from stealing a host field mid-typing.
|
|
43
|
+
async function restoreAfterFlush(caret, to) {
|
|
44
|
+
await tick();
|
|
45
|
+
if (deps.mode !== to || isTextEntrySurface(document.activeElement))
|
|
46
|
+
return;
|
|
47
|
+
await deps.restoreCaret(caret.path, caret.offset);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
beforeFlip(to) {
|
|
51
|
+
if (to === preFlipSeenMode)
|
|
52
|
+
return;
|
|
53
|
+
const from = preFlipSeenMode;
|
|
54
|
+
preFlipSeenMode = to;
|
|
55
|
+
// Reading keeps its entry snapshot: it has no caret of its own to recapture on the way out.
|
|
56
|
+
if (from !== 'reading')
|
|
57
|
+
flipCaret = captureCaret();
|
|
58
|
+
// A flip is a blur-class event: a live reveal or composition folds through the existing
|
|
59
|
+
// blur choke point. Host chrome is exempt, or a mode toggle blurs a title field mid-edit.
|
|
60
|
+
const active = document.activeElement;
|
|
61
|
+
if (active instanceof HTMLElement &&
|
|
62
|
+
deps.editorEl?.contains(active) &&
|
|
63
|
+
!deps.isHostChrome(active)) {
|
|
64
|
+
active.blur();
|
|
65
|
+
// A blur the editor performs announces the selection it drops: the document listener
|
|
66
|
+
// only reports a range the browser still anchors in the root, and this one is gone.
|
|
67
|
+
deps.events.emit('selectionChange', deps.getSelection());
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
afterFlip(to) {
|
|
71
|
+
if (to === lastEffectiveMode)
|
|
72
|
+
return;
|
|
73
|
+
lastEffectiveMode = to;
|
|
74
|
+
// Which markers paint just changed: a side recorded against the old geometry no longer
|
|
75
|
+
// names the offset the user meant, and every measured height is the other mode's. Not
|
|
76
|
+
// paired with a width bump, since the flip has blurred and a rebuild would lose the pin.
|
|
77
|
+
deps.edgeAffinity.reset();
|
|
78
|
+
deps.heightOracle.dropMeasured();
|
|
79
|
+
if (to === 'reading') {
|
|
80
|
+
// The gap is an editor-owned caret no DOM blur can reach, so the flip clears it
|
|
81
|
+
// rather than each arrival path.
|
|
82
|
+
deps.selection.clearGapCaret();
|
|
83
|
+
}
|
|
84
|
+
else if (flipCaret) {
|
|
85
|
+
const caret = flipCaret;
|
|
86
|
+
flipCaret = null;
|
|
87
|
+
void restoreAfterFlush(caret, to);
|
|
88
|
+
}
|
|
89
|
+
deps.events.emit('presentationModeChange', to);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
} = $props();
|
|
42
42
|
|
|
43
43
|
let imageOverlayEl: HTMLDivElement | undefined = $state();
|
|
44
|
+
// The crop session owns the pointer over the image; the resize grip steps aside meanwhile.
|
|
45
|
+
let cropping = $state(false);
|
|
44
46
|
|
|
45
47
|
const linkRef = getContext<EditorDoc | undefined>(EDITOR_DOC_KEY)?.linkRef;
|
|
46
48
|
|
|
@@ -95,19 +97,25 @@
|
|
|
95
97
|
{@const ctx = imageEdit.getSelectedImageFields()}
|
|
96
98
|
{#if ctx?.widgetEl}
|
|
97
99
|
<div bind:this={imageOverlayEl} class="md-image-overlay" data-image-overlay>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
{#if !cropping}
|
|
101
|
+
<ImageResizeHandles
|
|
102
|
+
getWidgetEl={() => imageEdit.getSelectedImageFields()?.widgetEl ?? null}
|
|
103
|
+
editorContentWidth={imageEdit.getEditorContentWidth()}
|
|
104
|
+
editorEvents={events}
|
|
105
|
+
onCommit={imageEdit.commitImageResize}
|
|
106
|
+
/>
|
|
107
|
+
{/if}
|
|
104
108
|
{#key `${sel.paragraphPath.join(',')}@${sel.sourceStart}`}
|
|
105
109
|
<ImageProperties
|
|
106
110
|
target={sel}
|
|
107
111
|
fields={imageFieldsFromInline(ctx.image)}
|
|
112
|
+
getWidgetEl={() => imageEdit.getSelectedImageFields()?.widgetEl ?? null}
|
|
108
113
|
buildBytes={imageEdit.buildEditBytes}
|
|
109
114
|
onCommit={imageEdit.commitImageEdit}
|
|
115
|
+
onRemove={imageEdit.removeImage}
|
|
110
116
|
onDismiss={imageEdit.dismissImagePopover}
|
|
117
|
+
maxFrameWidth={imageEdit.getEditorContentWidth}
|
|
118
|
+
bind:cropping
|
|
111
119
|
/>
|
|
112
120
|
{/key}
|
|
113
121
|
</div>
|