@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
package/dist/editor-keys.d.ts
CHANGED
|
@@ -45,6 +45,35 @@ export interface PastedImage {
|
|
|
45
45
|
/** Host import hook for pasted images: resolves to the markdown to insert, or null
|
|
46
46
|
* to skip that image. Called once per image file, in clipboard order. */
|
|
47
47
|
export type PasteImageHook = (image: PastedImage) => Promise<string | null>;
|
|
48
|
+
/** What a code block hands its host when the run affordance is pressed. */
|
|
49
|
+
export interface CodeRunRequest {
|
|
50
|
+
/** The fence body alone — opener and closer lines excluded. */
|
|
51
|
+
code: string;
|
|
52
|
+
/** The opener's full info string, untrimmed of trailing attributes (`py {1-3}`). */
|
|
53
|
+
info: string;
|
|
54
|
+
/** Child indices from the document root to this block. */
|
|
55
|
+
path: number[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Host hook for executing a code block. The editor runs nothing itself: installing this is
|
|
59
|
+
* what puts the run affordance on the block's rail, and the host owns everything after —
|
|
60
|
+
* the engine, the result, and where output goes. Absent, no run affordance renders.
|
|
61
|
+
*/
|
|
62
|
+
export type RunCodeHook = (request: CodeRunRequest) => void;
|
|
63
|
+
/** One entry in a code block's overflow menu. `run` is called with the menu already closed. */
|
|
64
|
+
export interface CodeMenuItem {
|
|
65
|
+
id: string;
|
|
66
|
+
label: string;
|
|
67
|
+
run: () => void;
|
|
68
|
+
/** Renders dimmed and refuses activation. */
|
|
69
|
+
disabled?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Host hook for the code block's overflow menu, consulted each time the menu opens so the
|
|
73
|
+
* items can read live state. Absent — or returning nothing — renders no overflow affordance:
|
|
74
|
+
* the editor has no app-level actions of its own to put there.
|
|
75
|
+
*/
|
|
76
|
+
export type CodeMenuItemsHook = (request: CodeRunRequest) => readonly CodeMenuItem[];
|
|
48
77
|
export type PresentationModeGetter = () => PresentationMode;
|
|
49
78
|
/** The editor's theme name, as reflected to `data-editor-theme`. An open string:
|
|
50
79
|
* built-ins are `'dark'`/`'light'`, and a consumer may name its own. */
|
|
@@ -150,6 +179,10 @@ export interface EditorPolicies {
|
|
|
150
179
|
/** Set-once host import hook for image-bearing pastes. Required-nullable: a mount must
|
|
151
180
|
* answer, and `undefined` deliberately leaves the paste on the text/plain path. */
|
|
152
181
|
onPasteImage: PasteImageHook | undefined;
|
|
182
|
+
/** Set-once host execution hook; its presence is what renders the run affordance. */
|
|
183
|
+
onRunCode: RunCodeHook | undefined;
|
|
184
|
+
/** Set-once host menu hook; its presence is what renders the overflow affordance. */
|
|
185
|
+
codeMenuItems: CodeMenuItemsHook | undefined;
|
|
153
186
|
/** Resolved image URLs that failed to load this session. One Set per instance, so a
|
|
154
187
|
* failed load never suppresses another editor's broken-state recompute
|
|
155
188
|
* (`components/image/widget-dom.ts`). */
|
package/dist/editor-props.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
6
6
|
import type { AnyBlockKind } from './core/nodes';
|
|
7
|
-
import type { PasteImageHook, ResolveImageUrl, ResolveLinkUrl } from './editor-keys';
|
|
7
|
+
import type { CodeMenuItemsHook, PasteImageHook, ResolveImageUrl, ResolveLinkUrl, RunCodeHook } from './editor-keys';
|
|
8
8
|
import type { ImageLoadPolicy } from './core/inline-render';
|
|
9
9
|
import type { PresentationMode } from './presentation-mode';
|
|
10
10
|
import type { KeybindingOverride } from './schema/keybinding-overrides';
|
|
@@ -27,14 +27,24 @@ export interface EditorProps {
|
|
|
27
27
|
* in order and the markdown returned is inserted at the caret; `null` skips it.
|
|
28
28
|
* Installing it takes the WHOLE paste — the clipboard's `text/plain` is not pasted. */
|
|
29
29
|
onPasteImage?: PasteImageHook;
|
|
30
|
+
/** Execution hook for code blocks, set once at mount. The editor runs nothing itself:
|
|
31
|
+
* installing this is what puts the run affordance on a code block's rail, and the host
|
|
32
|
+
* owns the engine, the result, and where output goes. Absent, no run affordance renders. */
|
|
33
|
+
onRunCode?: RunCodeHook;
|
|
34
|
+
/** Overflow-menu hook for code blocks, set once at mount and consulted each time a menu
|
|
35
|
+
* opens so items can read live state. Absent, or returning nothing, renders no overflow
|
|
36
|
+
* affordance — the editor has no app-level actions of its own to offer there. */
|
|
37
|
+
codeMenuItems?: CodeMenuItemsHook;
|
|
30
38
|
/** Host chrome rendered INSIDE the editor's scroll container, above the first block
|
|
31
39
|
* (a title, properties panel, tag row). It scrolls away with the document rather than
|
|
32
40
|
* pinning, which is what lets the editor keep its own scrollport and windowing. */
|
|
33
41
|
header?: Snippet;
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
42
|
+
/** The block drag handle (default on; reading mode never shows it). A hover reveals it;
|
|
43
|
+
* touch, which has none, shows it outright. Only the blocks a reader picks up whole carry
|
|
44
|
+
* one — code, tables, equations, diagrams, pictures, list items, dividers, cards — never
|
|
45
|
+
* prose (paragraph, heading, quote, note). `false` removes them, except on a picture,
|
|
46
|
+
* whose grip is the only pointer road to move it. Keyboard reorder (Alt+Arrow) is always
|
|
47
|
+
* available, as is the table's right-click cell menu. */
|
|
38
48
|
blockDragHandles?: boolean;
|
|
39
49
|
searchBar?: boolean;
|
|
40
50
|
/** Where the editor's own find/replace bar renders. Default (absent) keeps it pinned inside
|
|
@@ -102,14 +112,13 @@ export interface EditorInstance {
|
|
|
102
112
|
*/
|
|
103
113
|
insertMarkdown(md: string): boolean;
|
|
104
114
|
/**
|
|
105
|
-
* Run a command by id at the focused surface,
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* on an unknown id, in reading mode, with nothing focused, and on the link editor over a range.
|
|
115
|
+
* Run a command by id at the focused surface, or across a painted range where the id has a
|
|
116
|
+
* cross-block arm (a format toggle marks every block it touches, a table by its cells). False,
|
|
117
|
+
* and nothing mutates, on an unknown id, in reading mode, with nothing focused, and on the link
|
|
118
|
+
* editor over a range. `arg` reaches the arm as a keybinding's argument would (`heading.cycle`
|
|
119
|
+
* takes the level, 0 for plain text); an arm that takes none ignores it.
|
|
111
120
|
*/
|
|
112
|
-
runCommand(commandId: string): boolean;
|
|
121
|
+
runCommand(commandId: string, arg?: unknown): boolean;
|
|
113
122
|
/**
|
|
114
123
|
* Whether `runCommand(id)` would reach that command's arm right now, asked at the seam that
|
|
115
124
|
* would run it, so a host can grey a toolbar button out instead of hiding the affordance.
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { default as Editor } from './components/Editor.svelte';
|
|
|
2
2
|
export type { EditorProps, EditorInstance } from './editor-props';
|
|
3
3
|
export type { EditorDiagnostics, InteractionTraceEntry } from './editor-props';
|
|
4
4
|
export type { BlockComponent } from './block-component';
|
|
5
|
-
export type { ResolveImageUrl, ResolveLinkUrl, PastedImage, PasteImageHook } from './editor-keys';
|
|
5
|
+
export type { ResolveImageUrl, ResolveLinkUrl, PastedImage, PasteImageHook, CodeRunRequest, RunCodeHook, CodeMenuItem, CodeMenuItemsHook } from './editor-keys';
|
|
6
6
|
export type { ImageLoadPolicy } from './core/inline-render';
|
|
7
7
|
export type { PresentationMode } from './presentation-mode';
|
|
8
8
|
export type { SearchState, SearchOptions } from './search/search-state.svelte';
|
package/dist/plugin.d.ts
CHANGED
|
@@ -29,9 +29,15 @@ export type { OuterBlockScan } from './schema/block-openers';
|
|
|
29
29
|
export { OPENER_PRIORITIES } from './schema/opener-priorities';
|
|
30
30
|
export { registerBlockCompleter } from './schema/block-completions';
|
|
31
31
|
export type { BlockCompleter, CompletionResult } from './schema/block-completions';
|
|
32
|
+
export { registerLanguage, listLanguages } from './components/blocks/code/code-languages';
|
|
33
|
+
export type { LanguageGrammar } from './components/blocks/code/code-languages';
|
|
34
|
+
export { tokenizeBody as highlightCode } from './components/blocks/code/code-renderer';
|
|
35
|
+
export type { LanguageFn } from 'highlight.js';
|
|
32
36
|
export type { CommandId } from './schema/commands';
|
|
33
37
|
export type { KeyBinding } from './schema/keybindings';
|
|
34
38
|
export { registerBlockCommand } from './schema/block-commands';
|
|
39
|
+
export { registerBlockContextActions } from './schema/context-actions';
|
|
40
|
+
export type { BlockContextAction, BlockActionContext, BlockContextActionProvider } from './schema/context-actions';
|
|
35
41
|
export type { BlockCommandContext, BlockCommandHandler } from './schema/block-commands';
|
|
36
42
|
export type { PluginCommandId, AnyCommandId } from './schema/command-id';
|
|
37
43
|
export { registerGlobalCommand } from './schema/global-commands';
|
package/dist/plugin.js
CHANGED
|
@@ -38,9 +38,20 @@ export { OPENER_PRIORITIES } from './schema/opener-priorities';
|
|
|
38
38
|
// never type into existence: a completer reads one typed line and answers the lines that
|
|
39
39
|
// complete it, plus where the caret seats inside the mint.
|
|
40
40
|
export { registerBlockCompleter } from './schema/block-completions';
|
|
41
|
+
// ── Code-block languages (pre-freeze) ────────────────────────────────────────
|
|
42
|
+
// The registry behind fenced-code highlighting. The editor bootstraps a curated set (every
|
|
43
|
+
// grammar is bundle weight for every consumer), so a host needing more registers them itself,
|
|
44
|
+
// BEFORE mounting an editor: a block on screen re-tokenizes only when its bytes next change.
|
|
45
|
+
// An unregistered language is not an error — the fence still round-trips, just untokenized.
|
|
46
|
+
export { registerLanguage, listLanguages } from './components/blocks/code/code-languages';
|
|
47
|
+
// The code block's own tokenizer, for a plugin whose source surface wants the same highlighting
|
|
48
|
+
// (block math paints its LaTeX with it). Text-preserving: the fragment's textContent is `body`.
|
|
49
|
+
export { tokenizeBody as highlightCode } from './components/blocks/code/code-renderer';
|
|
41
50
|
// ── Command mint (pre-freeze) ────────────────────────────────────────────────
|
|
42
51
|
// A (kind, name) block-command mints a PluginCommandId; AnyCommandId spans built-in and minted.
|
|
43
52
|
export { registerBlockCommand } from './schema/block-commands';
|
|
53
|
+
// The block context menu: a kind's right-click actions, empty unless something registers them.
|
|
54
|
+
export { registerBlockContextActions } from './schema/context-actions';
|
|
44
55
|
// A global command is process-wide but runs against the dispatching instance's EditorContext.
|
|
45
56
|
export { registerGlobalCommand } from './schema/global-commands';
|
|
46
57
|
// ── Parse / serialize helpers (pre-freeze) ───────────────────────────────────
|
|
@@ -8,10 +8,32 @@
|
|
|
8
8
|
// Render-primary editable leaf: all editing behavior lives in `createEditableLeaf`,
|
|
9
9
|
// so this component owns only the render↔source swap visuals.
|
|
10
10
|
import { createEditableLeaf, type BlockComponent, type NodeView } from '../../plugin';
|
|
11
|
+
// Plugin-local like the other labels here: bundled plugins import only the public barrel.
|
|
12
|
+
// The three ways the source and its preview can share the block while editing (see
|
|
13
|
+
// `math-layout.ts`); the host's plugin options pick the starting one. The toggle cycles
|
|
14
|
+
// them and names the NEXT layout.
|
|
15
|
+
type MathLayout = MathBlockLayout;
|
|
16
|
+
const LAYOUT_NEXT: Record<MathLayout, MathLayout> = {
|
|
17
|
+
split: 'stacked',
|
|
18
|
+
stacked: 'source',
|
|
19
|
+
source: 'split'
|
|
20
|
+
};
|
|
21
|
+
const LAYOUT_TITLE: Record<MathLayout, string> = {
|
|
22
|
+
split: 'Preview beside the source',
|
|
23
|
+
stacked: 'Preview below the source',
|
|
24
|
+
source: 'Source only'
|
|
25
|
+
};
|
|
11
26
|
import { renderDisplayMath } from './math-renderer';
|
|
12
27
|
import { mathDisplaySource } from './latex-kind';
|
|
28
|
+
import { completeBareMathSource, mathBodySpan, renderMathSource } from './math-source';
|
|
29
|
+
import { resolveDefaultLayout, type MathBlockLayout } from './math-layout';
|
|
13
30
|
|
|
14
|
-
let {
|
|
31
|
+
let {
|
|
32
|
+
node,
|
|
33
|
+
index,
|
|
34
|
+
myPath = [],
|
|
35
|
+
blockLayout = 'split'
|
|
36
|
+
}: { node: NodeView; index: number; myPath?: number[]; blockLayout?: MathBlockLayout } = $props();
|
|
15
37
|
|
|
16
38
|
// eslint-disable-next-line no-useless-assignment -- <script module> counter read by the next instance mount
|
|
17
39
|
const mountId = nextMountId++;
|
|
@@ -20,6 +42,9 @@
|
|
|
20
42
|
let sourceEl: HTMLDivElement | undefined = $state();
|
|
21
43
|
let renderEl: HTMLDivElement | undefined = $state();
|
|
22
44
|
let revealed = $state(false);
|
|
45
|
+
// The in-flight source while revealed: a render-primary edit reaches the CST only on blur,
|
|
46
|
+
// so the live preview reads the surface, not the node. Null when nothing is in flight.
|
|
47
|
+
let draft = $state<string | null>(null);
|
|
23
48
|
|
|
24
49
|
const leaf = createEditableLeaf({
|
|
25
50
|
getNode: () => node,
|
|
@@ -30,16 +55,58 @@
|
|
|
30
55
|
isRevealed: () => revealed,
|
|
31
56
|
setRevealed: (value) => {
|
|
32
57
|
revealed = value;
|
|
58
|
+
draft = null;
|
|
59
|
+
},
|
|
60
|
+
renderSource: renderMathSource,
|
|
61
|
+
completeBareSource: completeBareMathSource,
|
|
62
|
+
onSourceEdit: (text) => {
|
|
63
|
+
draft = text;
|
|
33
64
|
}
|
|
34
65
|
});
|
|
35
66
|
|
|
67
|
+
// While editing, the preview is a sibling of the surface that holds focus: its scrollbar (a
|
|
68
|
+
// wide equation overflows the half-width card) must not take that focus, since losing it is
|
|
69
|
+
// what folds the editor. Same device as the eye button; a fold click is a click while folded.
|
|
70
|
+
function keepSourceFocus(e: MouseEvent): void {
|
|
71
|
+
if (revealed) e.preventDefault();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Per-instance and per-session: a reader who changes the layout is asking about THIS equation
|
|
75
|
+
// while they edit it, not setting a preference for the document. The starting layout is the
|
|
76
|
+
// host's: this editor's plugin options, else the factory's default.
|
|
77
|
+
// svelte-ignore state_referenced_locally
|
|
78
|
+
let layout = $state<MathLayout>(resolveDefaultLayout(leaf.getOptions(), blockLayout));
|
|
79
|
+
const previewOpen = $derived(layout !== 'source');
|
|
80
|
+
|
|
81
|
+
// The edits the leaf applies itself report through `onSourceEdit`; this is the native path
|
|
82
|
+
// (a composition's commit), where the highlight goes stale until repainted. The leaf's own
|
|
83
|
+
// handler runs first so the IME bookkeeping it owns is untouched.
|
|
84
|
+
function onSourceInput(e: Event): void {
|
|
85
|
+
leaf.surfaceProps.oninput();
|
|
86
|
+
if ((e as InputEvent).isComposing) return;
|
|
87
|
+
leaf.repaintSource();
|
|
88
|
+
draft = sourceEl?.textContent ?? null;
|
|
89
|
+
}
|
|
90
|
+
|
|
36
91
|
// ── View rendering ──────────────────────────────────────────────────────────
|
|
37
92
|
|
|
38
93
|
// Re-runs on every remount of the render div and on any source change; the
|
|
39
94
|
// document-wide memo clones a cached node, so a repeat formula is cheap.
|
|
40
95
|
$effect(() => {
|
|
41
|
-
if (
|
|
42
|
-
|
|
96
|
+
if (!renderEl) return;
|
|
97
|
+
// Runs while REVEALED as well: the split keeps a live preview beside the source, so the
|
|
98
|
+
// equation re-renders as it is typed rather than only when the source folds away.
|
|
99
|
+
const text = draft ?? leaf.sourceText;
|
|
100
|
+
const source = mathDisplaySource(text);
|
|
101
|
+
renderEl.replaceChildren(renderDisplayMath(source).dom);
|
|
102
|
+
// The span a press on the glyphs lands in: the descriptor's `caretTargetAtPoint` reads the
|
|
103
|
+
// rendered element alone and has no other way to the bytes behind it.
|
|
104
|
+
const body = mathBodySpan(text);
|
|
105
|
+
renderEl.dataset.bodyStart = String(body.start);
|
|
106
|
+
renderEl.dataset.bodyEnd = String(body.end);
|
|
107
|
+
// An equation with nothing in it renders nothing, which folded would be an invisible block
|
|
108
|
+
// the user cannot find to delete; it keeps the card's fill instead, like an empty fence.
|
|
109
|
+
renderEl.toggleAttribute('data-empty', source.trim() === '');
|
|
43
110
|
renderCount += 1;
|
|
44
111
|
renderEl.dataset.renderCount = String(renderCount);
|
|
45
112
|
});
|
|
@@ -74,46 +141,214 @@
|
|
|
74
141
|
} satisfies BlockComponent);
|
|
75
142
|
</script>
|
|
76
143
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
{
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
144
|
+
<!-- Editing shows source AND render side by side rather than swapping one for the other: the
|
|
145
|
+
swap re-flowed the whole document on every click, and a preview that only appears after you
|
|
146
|
+
stop editing is the one you needed while typing. Each half is a card, and the eye sits in the
|
|
147
|
+
top-right of whichever card is showing. -->
|
|
148
|
+
<div
|
|
149
|
+
class="math-block"
|
|
150
|
+
class:math-block-editing={revealed}
|
|
151
|
+
class:math-block-split={revealed && layout === 'split'}
|
|
152
|
+
class:math-block-stacked={revealed && layout === 'stacked'}
|
|
153
|
+
>
|
|
154
|
+
{#if revealed}
|
|
155
|
+
<div class="math-block-card">
|
|
156
|
+
<div
|
|
157
|
+
bind:this={sourceEl}
|
|
158
|
+
{...leaf.surfaceProps}
|
|
159
|
+
oninput={onSourceInput}
|
|
160
|
+
class="math-block-source md-source-surface"
|
|
161
|
+
aria-label="Math source"
|
|
162
|
+
></div>
|
|
163
|
+
<!-- The toggle keeps one seat: the top-right card. Side by side that is the preview; stacked
|
|
164
|
+
and source-only it is this card. -->
|
|
165
|
+
{#if layout !== 'split'}
|
|
166
|
+
{@render layoutToggle()}
|
|
167
|
+
{/if}
|
|
168
|
+
</div>
|
|
169
|
+
{/if}
|
|
170
|
+
{#if !revealed || previewOpen}
|
|
171
|
+
<div class="math-block-card">
|
|
172
|
+
<div
|
|
173
|
+
bind:this={renderEl}
|
|
174
|
+
class="math-block-render"
|
|
175
|
+
data-mount-id={mountId}
|
|
176
|
+
role="button"
|
|
177
|
+
tabindex="-1"
|
|
178
|
+
aria-label="Math (click to edit)"
|
|
179
|
+
{...leaf.renderProps}
|
|
180
|
+
onmousedown={keepSourceFocus}
|
|
181
|
+
></div>
|
|
182
|
+
{#if revealed && layout === 'split'}
|
|
183
|
+
{@render layoutToggle()}
|
|
184
|
+
{/if}
|
|
185
|
+
</div>
|
|
186
|
+
{/if}
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
{#snippet layoutToggle()}
|
|
190
|
+
{@const next = LAYOUT_NEXT[layout]}
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
class="math-preview-toggle"
|
|
194
|
+
aria-label={LAYOUT_TITLE[next]}
|
|
195
|
+
title={LAYOUT_TITLE[next]}
|
|
196
|
+
onmousedown={(e) => e.preventDefault()}
|
|
197
|
+
onclick={() => (layout = next)}
|
|
198
|
+
>
|
|
199
|
+
<svg
|
|
200
|
+
viewBox="0 0 24 24"
|
|
201
|
+
width="13"
|
|
202
|
+
height="13"
|
|
203
|
+
fill="none"
|
|
204
|
+
stroke="currentColor"
|
|
205
|
+
stroke-width="1.75"
|
|
206
|
+
stroke-linecap="round"
|
|
207
|
+
stroke-linejoin="round"
|
|
208
|
+
aria-hidden="true"
|
|
209
|
+
>
|
|
210
|
+
{#if layout === 'split'}
|
|
211
|
+
<rect width="18" height="18" x="3" y="3" rx="2" /><path d="M12 3v18" />
|
|
212
|
+
{:else if layout === 'stacked'}
|
|
213
|
+
<rect width="18" height="18" x="3" y="3" rx="2" /><path d="M3 12h18" />
|
|
214
|
+
{:else}
|
|
215
|
+
<path
|
|
216
|
+
d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49"
|
|
217
|
+
/><path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" /><path
|
|
218
|
+
d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143"
|
|
219
|
+
/><path d="m2 2 20 20" />
|
|
220
|
+
{/if}
|
|
221
|
+
</svg>
|
|
222
|
+
</button>
|
|
223
|
+
{/snippet}
|
|
95
224
|
|
|
96
225
|
<style>
|
|
97
|
-
/*
|
|
226
|
+
/* At rest the render alone, laid out as a plain block so it centres exactly as it did
|
|
227
|
+
before any of this existed. Editing turns the block into two equal cards. */
|
|
228
|
+
.math-block-split {
|
|
229
|
+
display: grid;
|
|
230
|
+
grid-template-columns: 1fr 1fr;
|
|
231
|
+
align-items: stretch;
|
|
232
|
+
gap: 6px;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Stacked: the source over its preview, each the block's full width — for the equation too
|
|
236
|
+
long to read at half width. Not the default: it grows the block and reshuffles the page. */
|
|
237
|
+
.math-block-stacked {
|
|
238
|
+
display: grid;
|
|
239
|
+
grid-template-columns: 1fr;
|
|
240
|
+
gap: 6px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* The equation's cards are boxes like a code block's, and take the same stand-off from their
|
|
244
|
+
neighbours (editor.css, fencedCode). Padding, not margin: the height model measures the
|
|
245
|
+
host's box. */
|
|
246
|
+
:global(.block-host[data-block-kind='mathBlock']) {
|
|
247
|
+
padding-block: 6px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Each half is its own card, and the containing block for its eye. */
|
|
251
|
+
.math-block-card {
|
|
252
|
+
position: relative;
|
|
253
|
+
min-width: 0;
|
|
254
|
+
border-radius: 8px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/* Keyed on EDITING, not on the split: folding the preview away leaves one card, and a card
|
|
258
|
+
that lost its fill the moment it stood alone would read as having left edit mode. */
|
|
259
|
+
.math-block-editing .math-block-card {
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: center;
|
|
262
|
+
background: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Deltas over the shared .md-source-surface (editor.css) — including its accent border,
|
|
266
|
+
which announced a state the card's own fill already carries. */
|
|
98
267
|
.math-block-source {
|
|
99
268
|
outline: none;
|
|
100
|
-
padding: 12px;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
269
|
+
padding: 10px 12px;
|
|
270
|
+
/* WRAP, rather than scroll sideways. The card is half the block's width, so any real
|
|
271
|
+
formula overflows it, and a horizontal scrollbar hides the very text being edited.
|
|
272
|
+
`pre-wrap` keeps the author's own line breaks and wraps only what is too long;
|
|
273
|
+
LaTeX carries no indentation structure for wrapping to destroy. */
|
|
274
|
+
white-space: pre-wrap;
|
|
275
|
+
overflow-wrap: anywhere;
|
|
276
|
+
/* LEFT, like every other source surface. Centring gave each line a different starting
|
|
277
|
+
x, which is exactly what makes multi-line LaTeX unreadable; the RENDER is the half
|
|
278
|
+
that is genuinely centred, and the pairing is what Overleaf and friends do. */
|
|
279
|
+
text-align: left;
|
|
280
|
+
background: transparent;
|
|
281
|
+
border-color: transparent;
|
|
282
|
+
border-radius: 8px;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/* The card stretches the source so its box fills the column; without it a short formula's
|
|
286
|
+
surface shrink-wraps and the caret only lands where the text is. */
|
|
287
|
+
.math-block-editing .math-block-card > .math-block-source {
|
|
288
|
+
flex: 1;
|
|
289
|
+
min-width: 0;
|
|
104
290
|
}
|
|
105
291
|
|
|
106
292
|
.math-block-render {
|
|
107
293
|
display: block;
|
|
108
|
-
|
|
294
|
+
width: 100%;
|
|
295
|
+
padding: 10px 12px;
|
|
109
296
|
text-align: center;
|
|
110
297
|
cursor: text;
|
|
111
298
|
border: 1px solid transparent;
|
|
112
|
-
border-radius:
|
|
299
|
+
border-radius: 8px;
|
|
113
300
|
overflow-x: auto;
|
|
114
301
|
}
|
|
115
302
|
|
|
116
|
-
|
|
303
|
+
/* Folded and empty: the fill stays, so there is a box to see and click into. */
|
|
304
|
+
.math-block:not(.math-block-editing) .math-block-render[data-empty] {
|
|
117
305
|
background: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
|
|
306
|
+
/* The editing card's height exactly — one source line at its size, plus its padding and
|
|
307
|
+
hairline — so folding and unfolding an empty equation moves nothing. */
|
|
308
|
+
min-height: calc(0.9em * 1.5 + 22px);
|
|
309
|
+
box-sizing: border-box;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/* At rest the render is the whole block and a hover tint is its only affordance; inside a
|
|
313
|
+
card the fill is already there, so the tint would double it. */
|
|
314
|
+
.math-block:not(.math-block-editing) .math-block-render:hover {
|
|
315
|
+
background: var(--color-bg-secondary, rgba(128, 128, 128, 0.12));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.math-preview-toggle {
|
|
319
|
+
position: absolute;
|
|
320
|
+
top: 4px;
|
|
321
|
+
right: 4px;
|
|
322
|
+
display: flex;
|
|
323
|
+
align-items: center;
|
|
324
|
+
justify-content: center;
|
|
325
|
+
width: 22px;
|
|
326
|
+
height: 22px;
|
|
327
|
+
padding: 0;
|
|
328
|
+
border: none;
|
|
329
|
+
border-radius: 5px;
|
|
330
|
+
background: transparent;
|
|
331
|
+
color: var(--color-ui-muted, #8f8f89);
|
|
332
|
+
cursor: pointer;
|
|
333
|
+
opacity: 0;
|
|
334
|
+
transition: opacity 120ms ease-out;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/* Transient like every other affordance here: the pointer over the block, or the eye itself
|
|
338
|
+
holding focus. */
|
|
339
|
+
.math-block:hover .math-preview-toggle,
|
|
340
|
+
.math-preview-toggle:focus-visible {
|
|
341
|
+
opacity: 1;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.math-preview-toggle:hover {
|
|
345
|
+
background: var(--color-ui-faint, rgba(255, 255, 255, 0.07));
|
|
346
|
+
color: var(--color-text-secondary, #cfcfca);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
@media (prefers-reduced-motion: reduce) {
|
|
350
|
+
.math-preview-toggle {
|
|
351
|
+
transition: none;
|
|
352
|
+
}
|
|
118
353
|
}
|
|
119
354
|
</style>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type NodeView } from '../../plugin';
|
|
2
|
+
import { type MathBlockLayout } from './math-layout';
|
|
2
3
|
type $$ComponentProps = {
|
|
3
4
|
node: NodeView;
|
|
4
5
|
index: number;
|
|
5
6
|
myPath?: number[];
|
|
7
|
+
blockLayout?: MathBlockLayout;
|
|
6
8
|
};
|
|
7
9
|
declare const BlockMath: import("svelte").Component<$$ComponentProps, {
|
|
8
10
|
editable: true;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { latexPlugin } from './register';
|
|
1
|
+
export { latexPlugin, type LatexPluginOptions } from './register';
|
|
2
2
|
export { MATH_INLINE, MATH_BLOCK, MATH_FENCE } from './latex-kind';
|
|
3
3
|
export type { MathRenderer } from './math-renderer';
|
|
4
|
+
export type { MathBlockLayout } from './math-layout';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* never imported here. Recognition is gated on registration, so with no extension
|
|
5
5
|
* loaded parsing stays byte-identical to bare GFM.
|
|
6
6
|
*/
|
|
7
|
-
import { createScanIndex, declarePluginInlineKind, declarePluginKind, registerInlineSyntax, registerInlineWidgetKind, registerBlockKind, registerBlockOpener, isInlineKindDeclared, simpleLeafClosure, matchFenceOpen, matchFenceClose, OPENER_PRIORITIES } from '../../plugin';
|
|
7
|
+
import { caretOffsetAtPoint, createScanIndex, declarePluginInlineKind, declarePluginKind, registerInlineSyntax, registerInlineWidgetKind, registerBlockKind, registerBlockOpener, isInlineKindDeclared, simpleLeafClosure, matchFenceOpen, matchFenceClose, OPENER_PRIORITIES } from '../../plugin';
|
|
8
8
|
import MathInline from './MathInline.svelte';
|
|
9
9
|
import { registerMathBlockCompleter } from './math-completion';
|
|
10
10
|
export const MATH_INLINE = 'math';
|
|
@@ -35,6 +35,10 @@ function recognizeMath(raw, pos, end, kind) {
|
|
|
35
35
|
const opener = raw[afterOpen];
|
|
36
36
|
if (isWhitespace(opener) || isDigit(opener))
|
|
37
37
|
return null;
|
|
38
|
+
// `$$` is the display fence, or the empty pair a keystroke just closed: never an inline
|
|
39
|
+
// opener, or its closer search would jump to the far end of the next formula on the line.
|
|
40
|
+
if (opener === '$')
|
|
41
|
+
return null;
|
|
38
42
|
// The index spans the whole block, so `end` decides the claim: a closer past the
|
|
39
43
|
// scan range leaves the `$` literal.
|
|
40
44
|
const close = firstCloserFrom(raw, pos + 2);
|
|
@@ -49,11 +53,18 @@ export function registerMathInline() {
|
|
|
49
53
|
if (isInlineKindDeclared(MATH_INLINE))
|
|
50
54
|
return;
|
|
51
55
|
const kind = declarePluginInlineKind(MATH_INLINE);
|
|
52
|
-
registerInlineSyntax('$', (raw, pos, end) => recognizeMath(raw, pos, end, kind)
|
|
56
|
+
registerInlineSyntax('$', (raw, pos, end) => recognizeMath(raw, pos, end, kind), {
|
|
57
|
+
autoPair: true
|
|
58
|
+
});
|
|
53
59
|
registerInlineWidgetKind(kind, {
|
|
54
60
|
isWidget: () => true,
|
|
55
61
|
component: MathInline,
|
|
56
|
-
editing: {
|
|
62
|
+
editing: {
|
|
63
|
+
revealSource: true,
|
|
64
|
+
// `$…$`: one delimiter each side, so a revealing click seats the caret on the last
|
|
65
|
+
// character of the formula rather than past its closing `$`.
|
|
66
|
+
revealContentSpan: (source) => source.length >= 2 ? { start: 1, end: source.length - 1 } : null
|
|
67
|
+
}
|
|
57
68
|
});
|
|
58
69
|
}
|
|
59
70
|
// ── Rendered display source ────────────────────────────────────────────────────
|
|
@@ -101,6 +112,7 @@ export function registerMathBlock() {
|
|
|
101
112
|
// edge can grow a sibling.
|
|
102
113
|
gapEdges: 'both',
|
|
103
114
|
conformanceFixture: '$$\nx^2\n$$\n',
|
|
115
|
+
caretTargetAtPoint: mathCaretAtPoint,
|
|
104
116
|
closure: simpleLeafClosure({
|
|
105
117
|
focus: {
|
|
106
118
|
mode: 'implemented',
|
|
@@ -155,6 +167,26 @@ export function registerMathBlock() {
|
|
|
155
167
|
// Co-registered so one install teaches both forms (the admonition/githubAlert precedent).
|
|
156
168
|
registerMathFence();
|
|
157
169
|
}
|
|
170
|
+
/** Where a press on the folded equation puts the caret. KaTeX paints glyphs, not source bytes,
|
|
171
|
+
* so the press walks the body span in proportion to how far along the equation it fell; the
|
|
172
|
+
* fence lines carry no glyph of their own. */
|
|
173
|
+
function mathCaretAtPoint(blockEl, clientX, clientY) {
|
|
174
|
+
const render = blockEl.querySelector('.math-block-render');
|
|
175
|
+
if (!render)
|
|
176
|
+
return null;
|
|
177
|
+
const start = Number(render.dataset.bodyStart);
|
|
178
|
+
const end = Number(render.dataset.bodyEnd);
|
|
179
|
+
if (!Number.isFinite(start) || !Number.isFinite(end))
|
|
180
|
+
return null;
|
|
181
|
+
// `.katex-html` is the painted half: its MathML twin is clipped to a pixel, and the pair
|
|
182
|
+
// measured together answers for a point no reader aimed at.
|
|
183
|
+
const glyphs = render.querySelector('.katex-html');
|
|
184
|
+
const along = glyphs ? caretOffsetAtPoint(glyphs, clientX, clientY) : null;
|
|
185
|
+
const total = glyphs?.textContent?.length ?? 0;
|
|
186
|
+
if (along === null || total === 0)
|
|
187
|
+
return { path: [], offset: end };
|
|
188
|
+
return { path: [], offset: start + Math.round((along / total) * (end - start)) };
|
|
189
|
+
}
|
|
158
190
|
// ── Fenced ```math display math ─────────────────────────────────────────────────
|
|
159
191
|
// GitHub's third math form: a source-holding leaf like the `$$` block, rendered by
|
|
160
192
|
// the same component.
|
|
@@ -170,6 +202,7 @@ export function registerMathFence() {
|
|
|
170
202
|
editable: true,
|
|
171
203
|
supportsInline: false,
|
|
172
204
|
gapEdges: 'both',
|
|
205
|
+
caretTargetAtPoint: mathCaretAtPoint,
|
|
173
206
|
conformanceFixture: '```math\nx^2\n```\n',
|
|
174
207
|
closure: simpleLeafClosure({
|
|
175
208
|
focus: {
|
|
@@ -15,5 +15,8 @@ export function tryCompleteMathBlock(line) {
|
|
|
15
15
|
export function registerMathBlockCompleter(kind) {
|
|
16
16
|
if (isBlockCompleterRegistered(kind))
|
|
17
17
|
return;
|
|
18
|
-
|
|
18
|
+
// On type as well as at Enter: a lone `$$` can only be the pair's opener, so the block forms
|
|
19
|
+
// as the second `$` lands — the way a typed ` ``` ` is a fence at once — with the caret on
|
|
20
|
+
// the body line. The one-line `$$x$$` form is still reachable by typing into that body.
|
|
21
|
+
registerBlockCompleter(kind, { tryComplete: tryCompleteMathBlock, onType: true });
|
|
19
22
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a `$$` block lays out its source and preview while it is being edited. `split` (side by
|
|
3
|
+
* side) is the default because it moves the page least; `stacked` suits long equations; `source`
|
|
4
|
+
* shows no preview. A host picks the default through `latexPlugin({ blockLayout })`, and the
|
|
5
|
+
* block's own toggle still cycles from there.
|
|
6
|
+
*/
|
|
7
|
+
export type MathBlockLayout = 'split' | 'stacked' | 'source';
|
|
8
|
+
export declare const MATH_BLOCK_LAYOUTS: readonly MathBlockLayout[];
|
|
9
|
+
export declare function isMathBlockLayout(value: unknown): value is MathBlockLayout;
|
|
10
|
+
/**
|
|
11
|
+
* The starting layout: this editor's per-instance plugin options first (`{ plugin, options:
|
|
12
|
+
* { blockLayout } }`), then the factory's own default (`latexPlugin({ blockLayout })`), then
|
|
13
|
+
* `split`. An unknown value falls through rather than throwing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveDefaultLayout(options: unknown, fallback?: MathBlockLayout): MathBlockLayout;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const MATH_BLOCK_LAYOUTS = ['split', 'stacked', 'source'];
|
|
2
|
+
export function isMathBlockLayout(value) {
|
|
3
|
+
return MATH_BLOCK_LAYOUTS.includes(value);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The starting layout: this editor's per-instance plugin options first (`{ plugin, options:
|
|
7
|
+
* { blockLayout } }`), then the factory's own default (`latexPlugin({ blockLayout })`), then
|
|
8
|
+
* `split`. An unknown value falls through rather than throwing.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveDefaultLayout(options, fallback = 'split') {
|
|
11
|
+
const declared = options?.blockLayout;
|
|
12
|
+
return isMathBlockLayout(declared) ? declared : fallback;
|
|
13
|
+
}
|