@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
|
@@ -5,6 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { type EditorPlugin } from '../../plugin';
|
|
7
7
|
import { type MathRenderer } from './math-renderer';
|
|
8
|
-
|
|
8
|
+
import { type MathBlockLayout } from './math-layout';
|
|
9
|
+
export interface LatexPluginOptions {
|
|
9
10
|
renderer: MathRenderer;
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* How a `$$` block opens for editing: `split` (source beside the preview, the default),
|
|
13
|
+
* `stacked` (preview below), or `source` (no preview). Definition-time, so it is the
|
|
14
|
+
* bare-install default; an editor's `{ plugin, options: { blockLayout } }` entry overrides it
|
|
15
|
+
* per instance. The block's own toggle cycles from whichever applies.
|
|
16
|
+
*/
|
|
17
|
+
blockLayout?: MathBlockLayout;
|
|
18
|
+
}
|
|
19
|
+
export declare function latexPlugin(options: LatexPluginOptions): EditorPlugin;
|
|
@@ -6,8 +6,10 @@
|
|
|
6
6
|
import { definePlugin, registerBlockComponent, defineBlockComponent, declaredPluginKind } from '../../plugin';
|
|
7
7
|
import { registerMathInline, registerMathBlock, MATH_BLOCK, MATH_FENCE } from './latex-kind';
|
|
8
8
|
import { setMathRenderer } from './math-renderer';
|
|
9
|
+
import { isMathBlockLayout } from './math-layout';
|
|
9
10
|
import BlockMath from './BlockMath.svelte';
|
|
10
11
|
export function latexPlugin(options) {
|
|
12
|
+
const blockLayout = isMathBlockLayout(options.blockLayout) ? options.blockLayout : 'split';
|
|
11
13
|
return definePlugin({
|
|
12
14
|
name: 'latex',
|
|
13
15
|
setup() {
|
|
@@ -15,7 +17,7 @@ export function latexPlugin(options) {
|
|
|
15
17
|
registerMathInline();
|
|
16
18
|
// registerMathBlock co-registers the ```math fence kind; both render through BlockMath.
|
|
17
19
|
registerMathBlock();
|
|
18
|
-
const blockMath = defineBlockComponent(BlockMath);
|
|
20
|
+
const blockMath = defineBlockComponent(BlockMath, () => ({ blockLayout }));
|
|
19
21
|
registerBlockComponent(declaredPluginKind(MATH_BLOCK), blockMath);
|
|
20
22
|
registerBlockComponent(declaredPluginKind(MATH_FENCE), blockMath);
|
|
21
23
|
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import 'katex/dist/katex.min.css';
|
|
7
7
|
import type { MathRenderer } from './math-renderer';
|
|
8
8
|
/**
|
|
9
|
-
* `throwOnError: false` keeps a malformed formula from crashing the editor
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* `throwOnError: false` keeps a malformed formula from crashing the editor; its fallback is
|
|
10
|
+
* swapped for the source painted as an error — red, in the code face, the parser's message on
|
|
11
|
+
* hover — so a broken formula reads as what the author typed, not as a sentence about it.
|
|
12
12
|
*/
|
|
13
13
|
export declare const katexRenderer: MathRenderer;
|
|
@@ -8,9 +8,9 @@ import katex from 'katex';
|
|
|
8
8
|
// a `.katex-mathml` a11y tree this CSS clips to 1px, and unloaded every equation paints twice.
|
|
9
9
|
import 'katex/dist/katex.min.css';
|
|
10
10
|
/**
|
|
11
|
-
* `throwOnError: false` keeps a malformed formula from crashing the editor
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `throwOnError: false` keeps a malformed formula from crashing the editor; its fallback is
|
|
12
|
+
* swapped for the source painted as an error — red, in the code face, the parser's message on
|
|
13
|
+
* hover — so a broken formula reads as what the author typed, not as a sentence about it.
|
|
14
14
|
*/
|
|
15
15
|
export const katexRenderer = (source, { display }) => {
|
|
16
16
|
const container = document.createElement('span');
|
|
@@ -22,13 +22,20 @@ export const katexRenderer = (source, { display }) => {
|
|
|
22
22
|
const errorSpan = container.querySelector('.katex-error');
|
|
23
23
|
if (errorSpan) {
|
|
24
24
|
const message = (errorSpan.getAttribute('title') ?? 'invalid LaTeX').replace(/^ParseError:\s*/, '');
|
|
25
|
-
return { dom: buildErrorNode(message), error: message };
|
|
25
|
+
return { dom: buildErrorNode(source, message), error: message };
|
|
26
26
|
}
|
|
27
27
|
return { dom: container };
|
|
28
28
|
};
|
|
29
|
-
function buildErrorNode(message) {
|
|
29
|
+
function buildErrorNode(source, message) {
|
|
30
30
|
const span = document.createElement('span');
|
|
31
31
|
span.className = 'math-error';
|
|
32
|
-
span.textContent =
|
|
32
|
+
span.textContent = source;
|
|
33
|
+
span.title = message;
|
|
34
|
+
// Inline style rather than a stylesheet: the span lands inside any prose block, where no
|
|
35
|
+
// plugin sheet is guaranteed to be mounted.
|
|
36
|
+
span.style.color = 'var(--color-error, #d03025)';
|
|
37
|
+
span.style.fontFamily = 'var(--font-code, ui-monospace, monospace)';
|
|
38
|
+
span.style.fontSize = '0.9em';
|
|
39
|
+
span.style.cursor = 'help';
|
|
33
40
|
return span;
|
|
34
41
|
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
updateOwnMetadata,
|
|
33
33
|
handleKeydown,
|
|
34
34
|
moveFocusOut,
|
|
35
|
+
captureScrollPosition,
|
|
35
36
|
getPresentationMode,
|
|
36
37
|
getTheme
|
|
37
38
|
} = createContainerBlock({
|
|
@@ -142,22 +143,20 @@
|
|
|
142
143
|
const view = createPanZoom();
|
|
143
144
|
const overlayView = createPanZoom();
|
|
144
145
|
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
|
|
146
|
+
// Armed by focus, so an unfocused diagram hijacks neither the page nor the editor's own drag.
|
|
147
|
+
// Tracked rather than probed: the editor reads the same fact as markup, so there is one value.
|
|
148
|
+
let gestureArmed = $state(false);
|
|
148
149
|
|
|
149
150
|
function onViewportWheel(e: WheelEvent): void {
|
|
150
|
-
if (!
|
|
151
|
+
if (!gestureArmed || !(e.ctrlKey || e.metaKey)) return;
|
|
151
152
|
e.preventDefault();
|
|
152
153
|
e.stopPropagation();
|
|
153
154
|
view.zoomBy(e.deltaY);
|
|
154
155
|
}
|
|
155
156
|
|
|
156
157
|
function onViewportPointerDown(e: PointerEvent): void {
|
|
157
|
-
// Never preventDefault, so the browser's focus-on-mousedown still lands: the
|
|
158
|
-
// first click focuses, and only a drag on the now-focused block pans.
|
|
159
158
|
e.stopPropagation();
|
|
160
|
-
if (
|
|
159
|
+
if (gestureArmed) view.beginPan(e);
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
function onOverlayWheel(e: WheelEvent): void {
|
|
@@ -219,8 +218,11 @@
|
|
|
219
218
|
// there too, so this closes the command path.
|
|
220
219
|
if (isReading) return;
|
|
221
220
|
if (!editing) seedDraft();
|
|
221
|
+
// Captured BEFORE the flip: the card reaches its fitted height only after mounting, and
|
|
222
|
+
// a scrollport at the document's end clamps against the short layout in between.
|
|
223
|
+
const restoreScroll = captureScrollPosition();
|
|
222
224
|
editRequested = true;
|
|
223
|
-
void
|
|
225
|
+
void restoreScroll().then(() => textareaEl?.focus());
|
|
224
226
|
}
|
|
225
227
|
|
|
226
228
|
function cancelEdit(): void {
|
|
@@ -306,7 +308,13 @@
|
|
|
306
308
|
</script>
|
|
307
309
|
|
|
308
310
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
309
|
-
<div
|
|
311
|
+
<div
|
|
312
|
+
class="mermaid-block"
|
|
313
|
+
bind:this={boxEl}
|
|
314
|
+
onkeydown={handleKeydown}
|
|
315
|
+
onfocusin={() => (gestureArmed = true)}
|
|
316
|
+
onfocusout={(e) => (gestureArmed = boxEl?.contains(e.relatedTarget as Node | null) ?? false)}
|
|
317
|
+
>
|
|
310
318
|
{#if editing}
|
|
311
319
|
<textarea
|
|
312
320
|
bind:this={textareaEl}
|
|
@@ -330,6 +338,7 @@
|
|
|
330
338
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
331
339
|
<div
|
|
332
340
|
class="mermaid-viewport"
|
|
341
|
+
data-pointer-gesture={gestureArmed ? '' : undefined}
|
|
333
342
|
tabindex="0"
|
|
334
343
|
role="img"
|
|
335
344
|
aria-label="Mermaid diagram"
|
|
@@ -392,6 +401,7 @@
|
|
|
392
401
|
</div>
|
|
393
402
|
<div
|
|
394
403
|
class="mermaid-overlay-viewport"
|
|
404
|
+
data-pointer-gesture
|
|
395
405
|
onwheel={onOverlayWheel}
|
|
396
406
|
onpointerdown={(e) => {
|
|
397
407
|
e.stopPropagation();
|
|
@@ -273,7 +273,9 @@ cNd.........................................;lOc
|
|
|
273
273
|
font-size: 1.1em;
|
|
274
274
|
line-height: 1.1;
|
|
275
275
|
letter-spacing: 0.05em;
|
|
276
|
-
/* one frame tall, in the reel's own rows so a step lands on the next frame exactly
|
|
276
|
+
/* one frame tall, in the reel's own rows so a step lands on the next frame exactly; the
|
|
277
|
+
em line is the same height for engines without lh (Safari before 16.4) */
|
|
278
|
+
height: calc(var(--parrot-rows) * 1.1em);
|
|
277
279
|
height: calc(var(--parrot-rows) * 1lh);
|
|
278
280
|
/* wider than a phone column, and the editor root pans if it isn't contained; the bar
|
|
279
281
|
would sit across the bird, which is decoration rather than a pane to scroll */
|
|
@@ -63,15 +63,15 @@ export interface ListWindowing {
|
|
|
63
63
|
recordMeasuredChild(index: number, id: string, height: number): void;
|
|
64
64
|
/** A PROPAGATED child-container subtotal: oracle + model slot, addressed by index. No anchor correction. */
|
|
65
65
|
setChildSubtotal(index: number, total: number): void;
|
|
66
|
-
/** Enroll a child in this scope's batched measure pass
|
|
67
|
-
* the child's unmount. The scope reads ALL pending
|
|
68
|
-
* fling that mounts many costs one reflow. */
|
|
66
|
+
/** Enroll a child in this scope's batched measure pass, read after the flush that registered
|
|
67
|
+
* it; returns an unregister fn to call on the child's unmount. The scope reads ALL pending
|
|
68
|
+
* children before applying ANY write, so a fling that mounts many costs one reflow. */
|
|
69
69
|
registerChild(id: string, child: MeasureEntry): () => void;
|
|
70
70
|
/** Re-measure ONE registered child immediately (its content height changed on edit). */
|
|
71
71
|
measureChildNow(id: string): void;
|
|
72
|
-
/** ResizeObserver path: O(1)-gate `observedHeight` against the
|
|
73
|
-
* re-measure only on a genuine post-mount change, so the no-op mount resize
|
|
74
|
-
* costs no DOM read. */
|
|
72
|
+
/** ResizeObserver path: O(1)-gate `observedHeight` against the height this scope last
|
|
73
|
+
* applied and re-measure only on a genuine post-mount change, so the no-op mount resize
|
|
74
|
+
* on a fling costs no DOM read. */
|
|
75
75
|
measureChildOnResize(id: string, observedHeight: number): void;
|
|
76
76
|
/**
|
|
77
77
|
* True when the scroll position IS the reveal target's requested placement. Asked by
|
|
@@ -93,8 +93,8 @@ export interface ListWindowing {
|
|
|
93
93
|
* Resize-gate decision (pure, unit-tested). Keyed on the height DIFFERING from what is
|
|
94
94
|
* recorded, never on which callback delivered it: a cached remount can report the grown
|
|
95
95
|
* size in the very first callback, so a callback-order heuristic would drop it.
|
|
96
|
-
* `recorded === undefined` means the
|
|
97
|
-
* defer to
|
|
96
|
+
* `recorded === undefined` means the scope has applied no height for this block yet —
|
|
97
|
+
* defer to the batched pass rather than racing a lone read in on a fling-dirtied layout.
|
|
98
98
|
*/
|
|
99
99
|
export declare function shouldRemeasureOnResize(recorded: number | undefined, observed: number): boolean;
|
|
100
100
|
export declare function createListWindowing(deps: ListWindowingDeps): ListWindowing;
|
|
@@ -14,8 +14,8 @@ import { runMeasureBatch } from './measure-batch';
|
|
|
14
14
|
* Resize-gate decision (pure, unit-tested). Keyed on the height DIFFERING from what is
|
|
15
15
|
* recorded, never on which callback delivered it: a cached remount can report the grown
|
|
16
16
|
* size in the very first callback, so a callback-order heuristic would drop it.
|
|
17
|
-
* `recorded === undefined` means the
|
|
18
|
-
* defer to
|
|
17
|
+
* `recorded === undefined` means the scope has applied no height for this block yet —
|
|
18
|
+
* defer to the batched pass rather than racing a lone read in on a fling-dirtied layout.
|
|
19
19
|
*/
|
|
20
20
|
export function shouldRemeasureOnResize(recorded, observed) {
|
|
21
21
|
if (observed <= 0 || recorded === undefined)
|
|
@@ -43,7 +43,23 @@ export function createListWindowing(deps) {
|
|
|
43
43
|
// structural rebuild needs the OLD ordering to remap the anchor by id, and by then
|
|
44
44
|
// `getChildIds()` already reflects the new children. Copied — it is spliced in place.
|
|
45
45
|
let modelChildIds = [];
|
|
46
|
-
|
|
46
|
+
/** What the CURRENT model holds, keyed by id, for a rebuild to carry across. The two lengths
|
|
47
|
+
* diverge for one tick after a child change, and a slot past the model's end reads 0. */
|
|
48
|
+
function heightsById() {
|
|
49
|
+
const carried = new Map();
|
|
50
|
+
const known = Math.min(modelChildIds.length, model.size);
|
|
51
|
+
for (let i = 0; i < known; i++)
|
|
52
|
+
carried.set(modelChildIds[i], model.heightOf(i));
|
|
53
|
+
return carried;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A surviving block keeps the height the model measured (VR-15); `carried` is null only
|
|
57
|
+
* where a width or type-scale change has earned a reseed from the oracle. The cache behind
|
|
58
|
+
* those heights can be gone (a mode flip drops it, and a block whose box never moved reports
|
|
59
|
+
* no resize to put it back), so reseeding a structural rebuild would trade a whole document
|
|
60
|
+
* of measurements for estimates and scroll the reader by the difference.
|
|
61
|
+
*/
|
|
62
|
+
function buildModel(carried) {
|
|
47
63
|
const width = estimateWidth(deps.getListEl(), deps.getPort()?.contentWidth() ?? 0);
|
|
48
64
|
const children = deps.getChildren();
|
|
49
65
|
// Indexed, and off the snapshot: `map` pays a `has` trap beside every `get`, once per child.
|
|
@@ -51,15 +67,17 @@ export function createListWindowing(deps) {
|
|
|
51
67
|
const count = children.length;
|
|
52
68
|
const heights = new Array(count);
|
|
53
69
|
for (let i = 0; i < count; i++) {
|
|
54
|
-
|
|
70
|
+
const id = modelChildIds[i];
|
|
71
|
+
heights[i] =
|
|
72
|
+
deps.oracle.measured(id) ?? carried?.get(id) ?? deps.oracle.estimate(children[i], width);
|
|
55
73
|
}
|
|
56
74
|
return new HeightModel(heights);
|
|
57
75
|
}
|
|
58
|
-
let model = $state(buildModel());
|
|
76
|
+
let model = $state(buildModel(null));
|
|
59
77
|
let heightVersion = $state(0);
|
|
60
78
|
// One scope-owned batched pass rather than a per-child effect, which would interleave a
|
|
61
79
|
// layout read with the prior child's write → one forced reflow per mounted block on a
|
|
62
|
-
// fling. `pending` holds ids awaiting their first measure;
|
|
80
|
+
// fling. `pending` holds ids awaiting their first measure; registration drains it.
|
|
63
81
|
const registry = new Map();
|
|
64
82
|
const pending = new Set();
|
|
65
83
|
/**
|
|
@@ -123,6 +141,20 @@ export function createListWindowing(deps) {
|
|
|
123
141
|
placeRevealTarget();
|
|
124
142
|
return true;
|
|
125
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Which block this correction holds still: the top-of-viewport one by default, the FOCUSED
|
|
146
|
+
* one when it sits at or below the fold. Clicking a construct that reveals its source (a
|
|
147
|
+
* math block, a paragraph holding inline math) resizes that block and folds whichever was
|
|
148
|
+
* revealed before it; anchored on the viewport top, both slide the clicked block out from
|
|
149
|
+
* under the pointer. Anchoring on the focused block holds its start offset instead, so it
|
|
150
|
+
* stays put and only the content after it reflows.
|
|
151
|
+
*/
|
|
152
|
+
function anchorIndexFor(topIndex) {
|
|
153
|
+
const pinned = pinnedIndex();
|
|
154
|
+
if (pinned === null || pinned < topIndex || pinned >= model.size)
|
|
155
|
+
return topIndex;
|
|
156
|
+
return pinned;
|
|
157
|
+
}
|
|
126
158
|
// Hold the anchor block's screen position across a height mutation that would otherwise
|
|
127
159
|
// slide the visible content (VR-2) — native `overflow-anchor` is off wherever this runs, so
|
|
128
160
|
// nothing else holds the line. The delta comes from the Fenwick model, not
|
|
@@ -134,12 +166,12 @@ export function createListWindowing(deps) {
|
|
|
134
166
|
if (reassertRevealAnchor(mutate))
|
|
135
167
|
return;
|
|
136
168
|
const port = deps.getPort();
|
|
137
|
-
const anchorIndex = model.indexAtOffset(localScrollTop());
|
|
169
|
+
const anchorIndex = anchorIndexFor(model.indexAtOffset(localScrollTop()));
|
|
138
170
|
const before = model.offsetOf(anchorIndex);
|
|
139
171
|
mutate();
|
|
140
172
|
const delta = model.offsetOf(anchorIndex) - before;
|
|
141
173
|
if (delta !== 0 && port)
|
|
142
|
-
port.
|
|
174
|
+
port.scrollBy(delta);
|
|
143
175
|
}
|
|
144
176
|
// The structural-rebuild variant of correctAnchor. A count change shifts every index
|
|
145
177
|
// above it, so the numeric form would measure a DIFFERENT block at index N and mis-correct
|
|
@@ -168,7 +200,7 @@ export function createListWindowing(deps) {
|
|
|
168
200
|
return;
|
|
169
201
|
const delta = model.offsetOf(newIndex) - before;
|
|
170
202
|
if (delta !== 0 && port)
|
|
171
|
-
port.
|
|
203
|
+
port.scrollBy(delta);
|
|
172
204
|
}
|
|
173
205
|
let lastWidthVersion = deps.getWidthVersion();
|
|
174
206
|
// Rebuild on any structural child change or an editor WIDTH change, never per keystroke.
|
|
@@ -188,8 +220,10 @@ export function createListWindowing(deps) {
|
|
|
188
220
|
// the re-measure compares measured-before against all-estimate-after, and lands the
|
|
189
221
|
// anchor a block off wherever the poisoned model names a different top child (#188).
|
|
190
222
|
// Width only — re-measuring on a structural edit costs a reflow per split.
|
|
223
|
+
// Read off the OLD model, so the carry is taken before the rebuild replaces it.
|
|
224
|
+
const carried = widthChanged ? null : heightsById();
|
|
191
225
|
correctAnchorByStableId(() => {
|
|
192
|
-
model = buildModel();
|
|
226
|
+
model = buildModel(carried);
|
|
193
227
|
heightVersion++;
|
|
194
228
|
if (widthChanged)
|
|
195
229
|
remeasureMounted();
|
|
@@ -258,22 +292,23 @@ export function createListWindowing(deps) {
|
|
|
258
292
|
$effect(() => {
|
|
259
293
|
void heightVersion;
|
|
260
294
|
const el = deps.getOwnEl?.();
|
|
261
|
-
|
|
295
|
+
const report = deps.reportSelfHeight;
|
|
296
|
+
if (!el || !report)
|
|
262
297
|
return;
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
298
|
+
// After the flush, like the batch: at this effect's first run the children are unrendered.
|
|
299
|
+
let live = true;
|
|
300
|
+
void tick().then(() => {
|
|
301
|
+
if (!live)
|
|
302
|
+
return;
|
|
303
|
+
const h = el.getBoundingClientRect().height;
|
|
304
|
+
if (h > 0 && Math.abs(h - reportedSelfHeight) >= 1) {
|
|
305
|
+
reportedSelfHeight = h;
|
|
306
|
+
report(h);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
return () => {
|
|
310
|
+
live = false;
|
|
311
|
+
};
|
|
277
312
|
});
|
|
278
313
|
// The UNCORRECTED batch: the caller owns the anchor correction, because nesting a second
|
|
279
314
|
// one inside an outer `mutate` double-counts the delta.
|
|
@@ -348,11 +383,21 @@ export function createListWindowing(deps) {
|
|
|
348
383
|
if (!skipWhileHostAnchors(write) && !reassertRevealAnchor(write))
|
|
349
384
|
write();
|
|
350
385
|
},
|
|
351
|
-
//
|
|
352
|
-
//
|
|
386
|
+
// Read after the flush that mounted the child, not inside it: content can land later in
|
|
387
|
+
// that flush (an inline widget's root flushes after this one), and an empty-host read is
|
|
388
|
+
// a scroll correction the observer undoes a frame later. Still ahead of paint.
|
|
353
389
|
registerChild(id, child) {
|
|
354
|
-
|
|
390
|
+
const entry = {
|
|
391
|
+
readHeight: child.readHeight,
|
|
392
|
+
applyHeight: (h) => {
|
|
393
|
+
entry.applied = h;
|
|
394
|
+
child.applyHeight(h);
|
|
395
|
+
},
|
|
396
|
+
applied: undefined
|
|
397
|
+
};
|
|
398
|
+
registry.set(id, entry);
|
|
355
399
|
pending.add(id);
|
|
400
|
+
void tick().then(flushMeasurements);
|
|
356
401
|
return () => {
|
|
357
402
|
registry.delete(id);
|
|
358
403
|
pending.delete(id);
|
|
@@ -362,11 +407,11 @@ export function createListWindowing(deps) {
|
|
|
362
407
|
measureChildNow(id) {
|
|
363
408
|
measureOne(id);
|
|
364
409
|
},
|
|
365
|
-
// ResizeObserver path for async growth. The gate reads
|
|
410
|
+
// ResizeObserver path for async growth. The gate reads what this scope last applied
|
|
366
411
|
// (O(1), no DOM), so the no-op mount resize a fling fires for every newly-mounted
|
|
367
412
|
// block returns without a rect read on the spacer-dirtied layout (VR-4).
|
|
368
413
|
measureChildOnResize(id, observedHeight) {
|
|
369
|
-
if (shouldRemeasureOnResize(
|
|
414
|
+
if (shouldRemeasureOnResize(registry.get(id)?.applied, observedHeight))
|
|
370
415
|
measureOne(id);
|
|
371
416
|
},
|
|
372
417
|
revealHoldsScroll() {
|
|
@@ -22,10 +22,18 @@ export interface CompletionResult {
|
|
|
22
22
|
export interface BlockCompleter {
|
|
23
23
|
/** Attempt to complete `line`; null declines, as does a claim whose lines would paint nothing. */
|
|
24
24
|
tryComplete(line: string): CompletionResult | null;
|
|
25
|
+
/**
|
|
26
|
+
* Also consulted as the line is TYPED, not only at Enter — for a claim that can only ever mean
|
|
27
|
+
* one thing the moment it is complete (a lone `$$`), so the block forms as a ` ``` ` fence does.
|
|
28
|
+
* Off by default: a table's header row is a prefix of a longer row the user may still be typing.
|
|
29
|
+
*/
|
|
30
|
+
onType?: boolean;
|
|
25
31
|
}
|
|
26
32
|
export declare function registerBlockCompleter(kind: AnyBlockKind, completer: BlockCompleter): void;
|
|
27
33
|
/** The first claim on `line`, or null when no registered completer takes it. */
|
|
28
34
|
export declare function completeTypedLine(line: string): CompletionResult | null;
|
|
35
|
+
/** The first claim on `line` among the completers that answer as the line is typed. */
|
|
36
|
+
export declare function completeLineOnType(line: string): CompletionResult | null;
|
|
29
37
|
/** Whether `kind` already owns a completer — the probe a registrar re-run reads before it
|
|
30
38
|
* registers, so a re-installed plugin never trips the register-once throw. */
|
|
31
39
|
export declare function isBlockCompleterRegistered(kind: string): boolean;
|
|
@@ -33,6 +33,17 @@ export function completeTypedLine(line) {
|
|
|
33
33
|
}
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
|
+
/** The first claim on `line` among the completers that answer as the line is typed. */
|
|
37
|
+
export function completeLineOnType(line) {
|
|
38
|
+
for (const completer of ordered()) {
|
|
39
|
+
if (!completer.onType)
|
|
40
|
+
continue;
|
|
41
|
+
const claim = completer.tryComplete(line);
|
|
42
|
+
if (claim)
|
|
43
|
+
return claim;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
36
47
|
/** Whether `kind` already owns a completer — the probe a registrar re-run reads before it
|
|
37
48
|
* registers, so a re-installed plugin never trips the register-once throw. */
|
|
38
49
|
export function isBlockCompleterRegistered(kind) {
|
|
@@ -19,10 +19,10 @@ export type GlobalCommandId = (typeof GLOBAL_COMMAND_IDS)[number];
|
|
|
19
19
|
export type BlockCommandId = (typeof BLOCK_COMMAND_IDS)[number];
|
|
20
20
|
export type CommandId = GlobalCommandId | BlockCommandId;
|
|
21
21
|
/**
|
|
22
|
-
* Commands whose arms rewrite one block
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* Commands whose arms rewrite one block and have no cross-block reading: the dispatch seam
|
|
23
|
+
* declines them outright while a range is painted. Membership is the arm's shape, not the id's
|
|
24
|
+
* prefix: the link card mints over one block's offsets and a heading level belongs to one block,
|
|
25
|
+
* so a range spanning blocks leaves neither of them a subject to act on.
|
|
26
26
|
*/
|
|
27
27
|
export declare const RANGE_DECLINED_COMMAND_IDS: ReadonlySet<string>;
|
|
28
28
|
/**
|
|
@@ -34,7 +34,9 @@ export declare const RANGE_DECLINED_COMMAND_IDS: ReadonlySet<string>;
|
|
|
34
34
|
export declare const CROSS_BLOCK_RANGE_COMMAND_IDS: ReadonlySet<string>;
|
|
35
35
|
/**
|
|
36
36
|
* The command ids a host's selection toolbar invokes through `EditorInstance.runCommand`. The
|
|
37
|
-
* rest of the vocabulary stays internal until the command registry unifies it.
|
|
37
|
+
* rest of the vocabulary stays internal until the command registry unifies it. Every id here
|
|
38
|
+
* answers the range question in one of the two sets above, so `canRunCommand` tells a bar which
|
|
39
|
+
* of its buttons a cross-block selection leaves nothing to act on.
|
|
38
40
|
*/
|
|
39
41
|
export declare const TOOLBAR_COMMANDS: {
|
|
40
42
|
readonly toggleStrong: "format.toggleStrong";
|
|
@@ -43,6 +45,8 @@ export declare const TOOLBAR_COMMANDS: {
|
|
|
43
45
|
readonly toggleCode: "format.toggleCode";
|
|
44
46
|
/** The link editor Mod+K opens: over a selection it creates, inside a link it edits. */
|
|
45
47
|
readonly editLink: "link.openCard";
|
|
48
|
+
/** The heading picker's arm, taking the level as its argument; 0 is normal text. */
|
|
49
|
+
readonly setHeading: "heading.cycle";
|
|
46
50
|
};
|
|
47
51
|
/** Minimal context a global command needs; HistoryActions is structurally compatible. */
|
|
48
52
|
export interface GlobalCommandContext {
|
package/dist/schema/commands.js
CHANGED
|
@@ -44,13 +44,14 @@ export const BLOCK_COMMAND_IDS = [
|
|
|
44
44
|
'chrome.descendToBody'
|
|
45
45
|
];
|
|
46
46
|
/**
|
|
47
|
-
* Commands whose arms rewrite one block
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
47
|
+
* Commands whose arms rewrite one block and have no cross-block reading: the dispatch seam
|
|
48
|
+
* declines them outright while a range is painted. Membership is the arm's shape, not the id's
|
|
49
|
+
* prefix: the link card mints over one block's offsets and a heading level belongs to one block,
|
|
50
|
+
* so a range spanning blocks leaves neither of them a subject to act on.
|
|
51
51
|
*/
|
|
52
52
|
export const RANGE_DECLINED_COMMAND_IDS = new Set([
|
|
53
|
-
'link.openCard'
|
|
53
|
+
'link.openCard',
|
|
54
|
+
'heading.cycle'
|
|
54
55
|
]);
|
|
55
56
|
/**
|
|
56
57
|
* The same one-block arms, but with a cross-block one behind them
|
|
@@ -66,7 +67,9 @@ export const CROSS_BLOCK_RANGE_COMMAND_IDS = new Set([
|
|
|
66
67
|
]);
|
|
67
68
|
/**
|
|
68
69
|
* The command ids a host's selection toolbar invokes through `EditorInstance.runCommand`. The
|
|
69
|
-
* rest of the vocabulary stays internal until the command registry unifies it.
|
|
70
|
+
* rest of the vocabulary stays internal until the command registry unifies it. Every id here
|
|
71
|
+
* answers the range question in one of the two sets above, so `canRunCommand` tells a bar which
|
|
72
|
+
* of its buttons a cross-block selection leaves nothing to act on.
|
|
70
73
|
*/
|
|
71
74
|
export const TOOLBAR_COMMANDS = {
|
|
72
75
|
toggleStrong: 'format.toggleStrong',
|
|
@@ -74,7 +77,9 @@ export const TOOLBAR_COMMANDS = {
|
|
|
74
77
|
toggleStrikethrough: 'format.toggleStrikethrough',
|
|
75
78
|
toggleCode: 'format.toggleCode',
|
|
76
79
|
/** The link editor Mod+K opens: over a selection it creates, inside a link it edits. */
|
|
77
|
-
editLink: 'link.openCard'
|
|
80
|
+
editLink: 'link.openCard',
|
|
81
|
+
/** The heading picker's arm, taking the level as its argument; 0 is normal text. */
|
|
82
|
+
setHeading: 'heading.cycle'
|
|
78
83
|
};
|
|
79
84
|
const globalCommands = new Map();
|
|
80
85
|
export function registerCommand(id, run) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block context menu's vocabulary: what a right-click on a block of a given kind offers.
|
|
3
|
+
* A kind's own providers come first, then the ones registered for every kind (`'*'`: the
|
|
4
|
+
* editor's copy, replace and remove rows). Providers are consulted on every open, so they read
|
|
5
|
+
* the block as it is. The defaults register at bootstrap; a plugin registers through the barrel.
|
|
6
|
+
*/
|
|
7
|
+
export declare const EVERY_KIND = "*";
|
|
8
|
+
import type { NodeView } from '../core/node-views';
|
|
9
|
+
export interface BlockActionContext {
|
|
10
|
+
node: NodeView;
|
|
11
|
+
/** The block's path from the document root. Top-level blocks only, for now. */
|
|
12
|
+
path: number[];
|
|
13
|
+
/** Remove the block. */
|
|
14
|
+
deleteBlock(): Promise<void>;
|
|
15
|
+
/** Replace the block's bytes wholesale; the result reparses to whatever those bytes are. */
|
|
16
|
+
replaceRaw(raw: string): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface BlockContextAction {
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
/** A glyph name the menu knows (`components/menu/MenuIcon.svelte`); absent draws none. */
|
|
22
|
+
icon?: string;
|
|
23
|
+
danger?: boolean;
|
|
24
|
+
run(ctx: BlockActionContext): void | Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export type BlockContextActionProvider = (node: NodeView, path: number[]) => BlockContextAction[];
|
|
27
|
+
/** Add a provider for `kind`, or for every kind with `EVERY_KIND`. Several may stack. */
|
|
28
|
+
export declare function registerBlockContextActions(kind: string, provider: BlockContextActionProvider): void;
|
|
29
|
+
export declare function blockContextActionsFor(node: NodeView, path: number[]): BlockContextAction[];
|
|
30
|
+
/** Test-only. */
|
|
31
|
+
export declare function __resetBlockContextActionsForTests(): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The block context menu's vocabulary: what a right-click on a block of a given kind offers.
|
|
3
|
+
* A kind's own providers come first, then the ones registered for every kind (`'*'`: the
|
|
4
|
+
* editor's copy, replace and remove rows). Providers are consulted on every open, so they read
|
|
5
|
+
* the block as it is. The defaults register at bootstrap; a plugin registers through the barrel.
|
|
6
|
+
*/
|
|
7
|
+
export const EVERY_KIND = '*';
|
|
8
|
+
const providers = new Map();
|
|
9
|
+
/** Add a provider for `kind`, or for every kind with `EVERY_KIND`. Several may stack. */
|
|
10
|
+
export function registerBlockContextActions(kind, provider) {
|
|
11
|
+
const list = providers.get(kind) ?? [];
|
|
12
|
+
list.push(provider);
|
|
13
|
+
providers.set(kind, list);
|
|
14
|
+
}
|
|
15
|
+
export function blockContextActionsFor(node, path) {
|
|
16
|
+
const own = providers.get(node.kind) ?? [];
|
|
17
|
+
const every = providers.get(EVERY_KIND) ?? [];
|
|
18
|
+
return [...own, ...every].flatMap((provider) => provider(node, path));
|
|
19
|
+
}
|
|
20
|
+
/** Test-only. */
|
|
21
|
+
export function __resetBlockContextActionsForTests() {
|
|
22
|
+
providers.clear();
|
|
23
|
+
}
|
|
@@ -17,7 +17,8 @@ export function reconcileFenceWrite(input) {
|
|
|
17
17
|
const { fence, mode } = input;
|
|
18
18
|
if (!fence.closed && mode === 'authored')
|
|
19
19
|
return { display: input.display, caret: input.caret };
|
|
20
|
-
|
|
20
|
+
const unglued = separateGluedCloser(input);
|
|
21
|
+
return escalateFenceRuns({ ...input, ...sanitizeInfoString({ ...input, ...unglued }) });
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* A whole fencedCode `raw` made legal — the kind's `normalizeRawWrite`. Literal by
|
|
@@ -166,6 +167,35 @@ function countDroppedBefore(info, infoStart, caret) {
|
|
|
166
167
|
}
|
|
167
168
|
return dropped;
|
|
168
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Un-glue a closer the write landed on top of. A fence with NO body line — ```` ```\n``` ```` —
|
|
172
|
+
* has nothing between its two runs, so the body's only caret seat IS the closer's line start,
|
|
173
|
+
* and the first character typed there arrives ahead of the closer run: ```` ```\nAB``` ````,
|
|
174
|
+
* which no longer reads as closed and shows the run as body text. The fence says it is closed
|
|
175
|
+
* and the display no longer carries a closer line, so the write broke it; the missing line
|
|
176
|
+
* ending is what puts it back.
|
|
177
|
+
*/
|
|
178
|
+
function separateGluedCloser(input) {
|
|
179
|
+
const { display, caret, fence } = input;
|
|
180
|
+
if (!fence.closed)
|
|
181
|
+
return { display, caret };
|
|
182
|
+
const lines = display.split('\n');
|
|
183
|
+
// A closer still on its own line needs nothing; so does a display too short to hold one.
|
|
184
|
+
if (lines.length < 2 || lastCloserIndex(lines, fence) >= 1)
|
|
185
|
+
return { display, caret };
|
|
186
|
+
const run = new RegExp(`[${fence.marker}]{${fence.length},}[ \t]*$`).exec(lines[lines.length - 1]);
|
|
187
|
+
// `index === 0` is a bare run that some other rule owns, not a glued one.
|
|
188
|
+
if (!run || run.index === 0)
|
|
189
|
+
return { display, caret };
|
|
190
|
+
const at = display.length - run[0].length;
|
|
191
|
+
const ending = display.includes('\r\n') ? '\r\n' : '\n';
|
|
192
|
+
return {
|
|
193
|
+
display: display.slice(0, at) + ending + display.slice(at),
|
|
194
|
+
// The caret sits at the typed text's end, which is where the ending is inserted: it
|
|
195
|
+
// stays put, and only a caret already inside the closer run rides the shift.
|
|
196
|
+
caret: caret > at ? caret + ending.length : caret
|
|
197
|
+
};
|
|
198
|
+
}
|
|
169
199
|
function escalateFenceRuns(input) {
|
|
170
200
|
const { display, caret, fence } = input;
|
|
171
201
|
const lines = display.split('\n');
|