edytor 1.0.0 → 3.0.0
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 +26 -69
- package/dist/block/block.svelte.d.ts +6 -4
- package/dist/block/block.svelte.js +12 -8
- package/dist/block/inlineBlock.svelte.d.ts +6 -2
- package/dist/block/inlineBlock.svelte.js +7 -2
- package/dist/clipboard/htmlFlow.d.ts +20 -7
- package/dist/clipboard/htmlFlow.js +23 -15
- package/dist/clipboard/serializeClipboardFragment.d.ts +37 -4
- package/dist/clipboard/serializeClipboardFragment.js +6 -4
- package/dist/clipboard/textBlocks.d.ts +8 -0
- package/dist/clipboard/textBlocks.js +6 -2
- package/dist/cloudflare/room/admission.js +3 -3
- package/dist/cloudflare/room/context.d.ts +7 -1
- package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
- package/dist/cloudflare/routeDocumentSocket.js +53 -0
- package/dist/collaboration/awarenessSelection.d.ts +22 -1
- package/dist/collaboration/awarenessSelection.js +42 -0
- package/dist/collaboration/comments/client.d.ts +1 -1
- package/dist/collaboration/comments/client.js +1 -1
- package/dist/collaboration/remoteSelection.js +3 -3
- package/dist/components/Block.svelte +3 -1
- package/dist/components/Edytor.svelte +38 -20
- package/dist/components/Edytor.svelte.d.ts +9 -4
- package/dist/components/InlineBlock.svelte +1 -1
- package/dist/crdt/admission.d.ts +5 -10
- package/dist/crdt/admission.js +5 -11
- package/dist/crdt/data.d.ts +10 -0
- package/dist/crdt/data.js +20 -2
- package/dist/crdt/doc/funnel.d.ts +7 -6
- package/dist/crdt/doc/funnel.js +10 -9
- package/dist/crdt/doc/gate.d.ts +0 -17
- package/dist/crdt/doc/gate.js +2 -41
- package/dist/crdt/doc/history.js +2 -2
- package/dist/crdt/doc/reads.d.ts +7 -0
- package/dist/crdt/doc/reads.js +11 -1
- package/dist/crdt/document.d.ts +13 -14
- package/dist/crdt/document.js +17 -19
- package/dist/crdt/edytor-doc.d.ts +22 -4
- package/dist/crdt/edytor-doc.js +5 -8
- package/dist/crdt/index.d.ts +9 -3
- package/dist/crdt/index.js +2 -2
- package/dist/crdt/operations.d.ts +1 -1
- package/dist/crdt/operations.js +1 -0
- package/dist/crdt/props.d.ts +32 -0
- package/dist/crdt/props.js +172 -0
- package/dist/crdt/protocol.d.ts +2 -2
- package/dist/crdt/protocol.js +2 -2
- package/dist/crdt/providers/index.d.ts +1 -1
- package/dist/crdt/providers/room.d.ts +10 -9
- package/dist/crdt/providers/room.js +13 -10
- package/dist/crdt/providers/websocket.d.ts +1 -1
- package/dist/crdt/providers/websocket.js +28 -29
- package/dist/crdt/semantics.js +3 -1
- package/dist/dnd/autoScroll.d.ts +15 -7
- package/dist/dnd/autoScroll.js +10 -15
- package/dist/{plugins/blockHandles → dnd}/dropIndicator.d.ts +2 -2
- package/dist/{plugins/blockHandles → dnd}/dropIndicator.js +2 -5
- package/dist/dnd/external.d.ts +6 -0
- package/dist/dnd/external.js +17 -0
- package/dist/{plugins/blockHandles → dnd}/geometry.d.ts +4 -2
- package/dist/{plugins/blockHandles → dnd}/geometry.js +3 -4
- package/dist/dnd/placement.d.ts +200 -0
- package/dist/dnd/placement.js +426 -0
- package/dist/dnd/pragmatic.d.ts +3 -1
- package/dist/dnd/pragmatic.js +12 -2
- package/dist/dnd/vendor/pragmatic/UPSTREAM.md +51 -18
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/drop-target-for-external.js +6 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.d.ts +0 -2
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/element-adapter.js +4 -22
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter-types.d.ts +26 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.d.ts +30 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/external-adapter.js +203 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-items.js +13 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/get-available-types.js +9 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/is-an-available-type.js +19 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/adapter/monitor-for-external.js +6 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/honey-pot-fix/make-honey-pot-fix.js +4 -16
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/internal-types.d.ts +1 -8
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-drop-target.js +5 -6
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/make-adapter/make-monitor.js +2 -3
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.d.ts +1 -1
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/element/custom-native-drag-preview/set-custom-native-drag-preview.js +6 -24
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.d.ts +2 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/contains-files.js +5 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.d.ts +3 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/get-files.js +12 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/public-utils/external/native-types.d.ts +12 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window-in-safari.js +10 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/changing-window/is-entering-window.js +55 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.d.ts +4 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/entry-point/external.js +8 -0
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/internal-types.d.ts +0 -4
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/get-scroll-by.js +16 -72
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/over-element/try-scroll.js +1 -9
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/configuration.js +4 -29
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/get-scroll-change.js +2 -3
- package/dist/edytor.svelte.d.ts +19 -9
- package/dist/edytor.svelte.js +28 -33
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +8 -4
- package/dist/events/onBeforeInput.js +1 -1
- package/dist/events/onDrop.js +25 -10
- package/dist/events/onInput.d.ts +0 -12
- package/dist/events/onInput.js +1 -67
- package/dist/events/onPaste.d.ts +2 -1
- package/dist/events/onPaste.js +8 -3
- package/dist/export/forms.d.ts +627 -0
- package/dist/export/forms.js +749 -0
- package/dist/export/index.d.ts +170 -0
- package/dist/export/index.js +161 -0
- package/dist/export/markdown.d.ts +41 -0
- package/dist/export/markdown.js +585 -0
- package/dist/export/markdownWriter.d.ts +9 -0
- package/dist/export/markdownWriter.js +133 -0
- package/dist/export/mdx.d.ts +15 -0
- package/dist/export/mdx.js +33 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +2 -2
- package/dist/kinds.js +1 -1
- package/dist/labels.d.ts +2 -0
- package/dist/labels.js +2 -1
- package/dist/plugins/anchored.svelte.d.ts +21 -25
- package/dist/plugins/anchored.svelte.js +23 -39
- package/dist/plugins/arrowMove/arrowMove.d.ts +1 -1
- package/dist/plugins/arrowMove/arrowMove.js +1 -1
- package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +2 -122
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +48 -414
- package/dist/plugins/blockHandles/BlockHandles.svelte +14 -32
- package/dist/plugins/blockHandles/blockHandlesPlugin.js +2 -1
- package/dist/plugins/blockHandles/dragPreview.d.ts +26 -7
- package/dist/plugins/blockHandles/dragPreview.js +33 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +5 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.js +7 -2
- package/dist/plugins/blockMenu/blockMenuPlugin.js +5 -50
- package/dist/plugins/chrome.d.ts +27 -5
- package/dist/plugins/chrome.js +24 -9
- package/dist/plugins/code/CodeHeader.svelte +5 -3
- package/dist/plugins/code/CodePlugin.svelte +15 -12
- package/dist/plugins/code/header.svelte.js +7 -11
- package/dist/plugins/code/languageMenu.svelte.d.ts +5 -2
- package/dist/plugins/code/languageMenu.svelte.js +13 -20
- package/dist/plugins/code/languages.d.ts +0 -3
- package/dist/plugins/code/languages.js +0 -2
- package/dist/plugins/columns/ColumnResize.svelte +3 -2
- package/dist/plugins/columns/ColumnsPlugin.svelte +8 -21
- package/dist/plugins/columns/gaps.d.ts +7 -5
- package/dist/plugins/columns/gaps.js +11 -6
- package/dist/plugins/columns/resize.svelte.d.ts +5 -6
- package/dist/plugins/columns/resize.svelte.js +12 -14
- package/dist/plugins/comments/CommentsController.svelte.d.ts +6 -0
- package/dist/plugins/comments/CommentsController.svelte.js +20 -0
- package/dist/plugins/equation/EquationPlugin.svelte +13 -30
- package/dist/plugins/equation/EquationPlugin.svelte.d.ts +1 -0
- package/dist/plugins/equation/equation.css +7 -0
- package/dist/plugins/equation/equation.svelte.d.ts +7 -15
- package/dist/plugins/equation/equation.svelte.js +13 -50
- package/dist/plugins/equation/tex.d.ts +17 -0
- package/dist/plugins/equation/tex.js +42 -0
- package/dist/plugins/fileDrop.d.ts +43 -0
- package/dist/plugins/fileDrop.js +122 -0
- package/dist/plugins/find/FindController.svelte.d.ts +5 -0
- package/dist/plugins/find/FindController.svelte.js +14 -14
- package/dist/plugins/find/findPlugin.js +1 -15
- package/dist/plugins/image/ImageChrome.svelte +8 -2
- package/dist/plugins/image/ImagePlugin.svelte +56 -84
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +13 -1
- package/dist/plugins/image/controls.svelte.js +16 -2
- package/dist/plugins/image/image.css +17 -0
- package/dist/plugins/image/toolbar.svelte.d.ts +5 -4
- package/dist/plugins/image/toolbar.svelte.js +21 -31
- package/dist/plugins/index.d.ts +4 -5
- package/dist/plugins/index.js +0 -1
- package/dist/plugins/list.svelte.d.ts +9 -0
- package/dist/plugins/list.svelte.js +18 -0
- package/dist/plugins/markdownShortcuts.js +5 -5
- package/dist/plugins/marquee/marqueePlugin.js +0 -4
- package/dist/plugins/media/AudioPlugin.svelte +13 -1
- package/dist/plugins/media/AudioPlugin.svelte.d.ts +11 -1
- package/dist/plugins/media/BookmarkPlugin.svelte +6 -97
- package/dist/plugins/media/EmbedPlugin.svelte +5 -90
- package/dist/plugins/media/EmptySlot.svelte +36 -20
- package/dist/plugins/media/EmptySlot.svelte.d.ts +6 -15
- package/dist/plugins/media/FilePlugin.svelte +12 -20
- package/dist/plugins/media/FilePlugin.svelte.d.ts +9 -0
- package/dist/plugins/media/MediaEmpty.svelte +2 -55
- package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
- package/dist/plugins/media/SourceBlock.svelte +178 -52
- package/dist/plugins/media/SourceBlock.svelte.d.ts +13 -6
- package/dist/plugins/media/UploadPreview.svelte +32 -0
- package/dist/plugins/media/UploadPreview.svelte.d.ts +11 -0
- package/dist/plugins/media/VideoPlugin.svelte +13 -1
- package/dist/plugins/media/VideoPlugin.svelte.d.ts +11 -1
- package/dist/plugins/media/empty.svelte.d.ts +13 -4
- package/dist/plugins/media/empty.svelte.js +32 -26
- package/dist/plugins/media/media.d.ts +9 -63
- package/dist/plugins/media/media.js +1 -194
- package/dist/plugins/media/sources.d.ts +60 -0
- package/dist/plugins/media/sources.js +165 -0
- package/dist/plugins/media/upload.css +23 -0
- package/dist/plugins/media/urlPaste.svelte.d.ts +2 -1
- package/dist/plugins/media/urlPaste.svelte.js +12 -21
- package/dist/plugins/mention/MentionPlugin.svelte +50 -38
- package/dist/plugins/mention/MentionPlugin.svelte.d.ts +11 -2
- package/dist/plugins/mention/mention.css +6 -0
- package/dist/plugins/page/PagePlugin.svelte +2 -9
- package/dist/plugins/page/page.js +1 -1
- package/dist/plugins/pageLink/PageLinkPlugin.svelte +126 -28
- package/dist/plugins/pageLink/PageLinkPlugin.svelte.d.ts +13 -1
- package/dist/plugins/pageLink/pageLink.css +6 -0
- package/dist/plugins/richtext/RichTextPlugin.svelte +99 -204
- package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +1 -0
- package/dist/plugins/richtext/body.js +1 -1
- package/dist/plugins/richtext/calloutIcons.svelte.d.ts +2 -2
- package/dist/plugins/richtext/calloutIcons.svelte.js +5 -7
- package/dist/plugins/richtext/richTextOperations.d.ts +1 -19
- package/dist/plugins/richtext/richTextOperations.js +3 -49
- package/dist/plugins/richtext/richtext.css +6 -0
- package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +2 -0
- package/dist/plugins/slashMenu/SlashMenuController.svelte.js +7 -0
- package/dist/plugins/slashMenu/slashMenuPlugin.js +5 -14
- package/dist/plugins/table/TableChrome.svelte +2 -3
- package/dist/plugins/table/TablePlugin.svelte +20 -54
- package/dist/plugins/table/chrome.svelte.d.ts +5 -2
- package/dist/plugins/table/chrome.svelte.js +33 -56
- package/dist/plugins/table/table.js +3 -3
- package/dist/plugins/toc/TocPlugin.svelte +2 -4
- package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +7 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.js +20 -13
- package/dist/plugins/toolbar/toolbarPlugin.js +19 -53
- package/dist/plugins/triggers/TriggerController.svelte.d.ts +7 -2
- package/dist/plugins/triggers/TriggerController.svelte.js +11 -4
- package/dist/plugins/triggers/place.js +1 -2
- package/dist/plugins/triggers/triggers.d.ts +6 -1
- package/dist/plugins/triggers/triggers.js +12 -13
- package/dist/plugins/uploads.svelte.d.ts +48 -32
- package/dist/plugins/uploads.svelte.js +119 -43
- package/dist/plugins.d.ts +66 -14
- package/dist/plugins.js +0 -24
- package/dist/selection/replaceSelection.js +2 -2
- package/dist/selection/selection.svelte.d.ts +4 -10
- package/dist/selection/selection.svelte.js +7 -11
- package/dist/session/attempt.d.ts +16 -6
- package/dist/session/attempt.js +1 -0
- package/dist/session/bindings.js +35 -32
- package/dist/session/commands.d.ts +7 -11
- package/dist/session/commands.js +15 -19
- package/dist/session/history.js +1 -0
- package/dist/session/inputRules.d.ts +6 -3
- package/dist/session/inputRules.js +10 -6
- package/dist/session/moves.d.ts +33 -0
- package/dist/session/moves.js +71 -1
- package/dist/session/navigation.d.ts +7 -0
- package/dist/session/navigation.js +20 -11
- package/dist/session/props.d.ts +28 -0
- package/dist/session/props.js +65 -0
- package/dist/session/suggestions.svelte.d.ts +0 -11
- package/dist/session/suggestions.svelte.js +15 -3
- package/dist/session/uploads.d.ts +29 -0
- package/dist/session/uploads.js +10 -0
- package/dist/surface/lines.js +1 -3
- package/dist/surface/observer.svelte.d.ts +17 -1
- package/dist/surface/observer.svelte.js +72 -17
- package/dist/surface/overlay.d.ts +0 -14
- package/dist/surface/overlay.js +0 -25
- package/dist/surface/projector.svelte.js +1 -1
- package/dist/text/text.svelte.js +3 -13
- package/dist/themes/notion.css +9 -0
- package/dist/utils/sanitize.d.ts +38 -0
- package/dist/utils/sanitize.js +82 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +5 -0
- package/package.json +18 -1
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/max-z-index.js +0 -3
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop/util/supports-popover.js +0 -16
- package/dist/dnd/vendor/pragmatic/pragmatic-drag-and-drop-auto-scroll/shared/is-axis-allowed.js +0 -3
- package/dist/plugins/code/LanguageMenu.svelte +0 -25
- package/dist/plugins/code/LanguageMenu.svelte.d.ts +0 -9
- package/dist/plugins/equation/EquationEditor.svelte +0 -27
- package/dist/plugins/equation/EquationEditor.svelte.d.ts +0 -9
- package/dist/plugins/image/ImageToolbarHost.svelte +0 -37
- package/dist/plugins/image/ImageToolbarHost.svelte.d.ts +0 -9
- package/dist/plugins/richtext/CalloutIconPicker.svelte +0 -26
- package/dist/plugins/richtext/CalloutIconPicker.svelte.d.ts +0 -9
- /package/dist/{plugins/columns → dnd}/stacking.d.ts +0 -0
- /package/dist/{plugins/columns → dnd}/stacking.js +0 -0
|
@@ -340,7 +340,7 @@ export const deleteSelectedBlocks = (edytor, blocks = selectedMembers(edytor)) =
|
|
|
340
340
|
...beside(blocks[0], 'blockBefore', blocks),
|
|
341
341
|
...beside(blocks.at(-1), 'blockAfter', blocks)
|
|
342
342
|
];
|
|
343
|
-
const deleted = edytor.dispatcher.
|
|
343
|
+
const deleted = edytor.dispatcher.caretIn(at?.text, at?.offset ?? 0, () => edytor.deleteBlocks(blocks.map((block) => block.id)));
|
|
344
344
|
if (deleted?.status !== 'applied')
|
|
345
345
|
return null;
|
|
346
346
|
edytor.announcer.deleted(what);
|
|
@@ -354,7 +354,7 @@ export const deleteSelectedBlocks = (edytor, blocks = selectedMembers(edytor)) =
|
|
|
354
354
|
const text = virtual ? edytor.idToBlock.get(virtual)?.firstText : undefined;
|
|
355
355
|
const survivor = besides.find((block) => block.isInTree);
|
|
356
356
|
if (text)
|
|
357
|
-
edytor.dispatcher.
|
|
357
|
+
edytor.dispatcher.caretIn(text, 0);
|
|
358
358
|
else if (survivor)
|
|
359
359
|
edytor.selection.select(blockSelection([survivor.id]));
|
|
360
360
|
return text ?? null;
|
|
@@ -94,7 +94,7 @@ export declare class EdytorSelection {
|
|
|
94
94
|
* the start binds right (boundary inserts stay outside), the end and a
|
|
95
95
|
* caret bind left; `none` when the start text is not bound to the document.
|
|
96
96
|
*/
|
|
97
|
-
textValue: (startText: Text, yStart: number, endText?: Text, yEnd?: number, isReversed?: boolean, startAffinity?: "left" | "right"
|
|
97
|
+
textValue: (startText: Text, yStart: number, endText?: Text, yEnd?: number, isReversed?: boolean, startAffinity?: "left" | "right") => SelectionValue;
|
|
98
98
|
/**
|
|
99
99
|
* A pointer drag is in progress (a press in the host, until its release):
|
|
100
100
|
* the projector does not display under it, and the overlay chrome
|
|
@@ -123,9 +123,9 @@ export declare class EdytorSelection {
|
|
|
123
123
|
* facade maps to the owning backing text — content routed from other
|
|
124
124
|
* blocks' backing texts (merges/splits) anchors correctly.
|
|
125
125
|
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
126
|
+
* Minted from the text's block record, live or not (the atoms of a text
|
|
127
|
+
* that died to a merge live on in the block that claimed them); `null`
|
|
128
|
+
* for the root's own text.
|
|
129
129
|
*/
|
|
130
130
|
createTextAnchor: (text: Text, offset: number, affinity?: "left" | "right") => TextAnchor | null;
|
|
131
131
|
/**
|
|
@@ -148,12 +148,6 @@ export declare class EdytorSelection {
|
|
|
148
148
|
* never mounts.
|
|
149
149
|
*/
|
|
150
150
|
displayable: (id: string) => boolean;
|
|
151
|
-
/** The view's visibility rule (`selection/visibility.ts`), for extensions: hidden by view state, and the shown neighbour. */
|
|
152
|
-
hidden: (block: Block, removed?: ReadonlySet<Block>) => boolean;
|
|
153
|
-
shown: (block: Block, step: "blockBefore" | "blockAfter", { sealed, removed }?: {
|
|
154
|
-
sealed?: boolean;
|
|
155
|
-
removed?: ReadonlySet<Block>;
|
|
156
|
-
}) => Block | null;
|
|
157
151
|
/** The text's computed direction (a Surface fact): arrow and word keys are visual. */
|
|
158
152
|
rtl: (text: Text) => boolean;
|
|
159
153
|
}
|
|
@@ -389,7 +389,8 @@ export class EdytorSelection {
|
|
|
389
389
|
* the start binds right (boundary inserts stay outside), the end and a
|
|
390
390
|
* caret bind left; `none` when the start text is not bound to the document.
|
|
391
391
|
*/
|
|
392
|
-
textValue = (startText, yStart, endText = startText, yEnd = yStart, isReversed = false, startAffinity
|
|
392
|
+
textValue = (startText, yStart, endText = startText, yEnd = yStart, isReversed = false, startAffinity) => {
|
|
393
|
+
const mint = this.createTextAnchor;
|
|
393
394
|
const collapsed = startText === endText && yStart === yEnd;
|
|
394
395
|
const start = mint(startText, yStart, startAffinity ?? (collapsed ? 'left' : 'right'));
|
|
395
396
|
if (!start)
|
|
@@ -413,9 +414,7 @@ export class EdytorSelection {
|
|
|
413
414
|
if (!startText || !endText)
|
|
414
415
|
return;
|
|
415
416
|
const at = (text, offset) => Math.min(Math.max(offset, 0), text.length);
|
|
416
|
-
const value = this.textValue(startText, at(startText, yStart), endText, at(endText, yEnd), isReversed
|
|
417
|
-
? null
|
|
418
|
-
: this.edytor.facade.anchorAt(text.blockId, text.segStart + offset, side));
|
|
417
|
+
const value = this.textValue(startText, at(startText, yStart), endText, at(endText, yEnd), isReversed);
|
|
419
418
|
const target = project(value, this.edytor.facade).start
|
|
420
419
|
? value
|
|
421
420
|
: this.#seamValue(startText.parent?.id ?? null);
|
|
@@ -652,12 +651,12 @@ export class EdytorSelection {
|
|
|
652
651
|
* facade maps to the owning backing text — content routed from other
|
|
653
652
|
* blocks' backing texts (merges/splits) anchors correctly.
|
|
654
653
|
*
|
|
655
|
-
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
654
|
+
* Minted from the text's block record, live or not (the atoms of a text
|
|
655
|
+
* that died to a merge live on in the block that claimed them); `null`
|
|
656
|
+
* for the root's own text.
|
|
658
657
|
*/
|
|
659
658
|
createTextAnchor = (text, offset, affinity = 'left') => {
|
|
660
|
-
if (text.parent.isRoot
|
|
659
|
+
if (text.parent.isRoot)
|
|
661
660
|
return null;
|
|
662
661
|
return this.edytor.facade.anchorAt(text.blockId, text.segStart + offset, affinity);
|
|
663
662
|
};
|
|
@@ -688,9 +687,6 @@ export class EdytorSelection {
|
|
|
688
687
|
?.content.find((part) => part instanceof Text && part.node != null)?.node;
|
|
689
688
|
return !!node && !node.closest(visibility.HIDDEN);
|
|
690
689
|
};
|
|
691
|
-
/** The view's visibility rule (`selection/visibility.ts`), for extensions: hidden by view state, and the shown neighbour. */
|
|
692
|
-
hidden = visibility.hidden;
|
|
693
|
-
shown = visibility.shown;
|
|
694
690
|
/** The text's computed direction (a Surface fact): arrow and word keys are visual. */
|
|
695
691
|
rtl = (text) => !!text.node?.isConnected && getComputedStyle(text.node).direction === 'rtl';
|
|
696
692
|
/** A caret at the seam `dead` vacated (`doc/anchors`); `null` when nothing displays. */
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { Edytor } from '../edytor.svelte.js';
|
|
20
20
|
import type { Text } from '../text/text.svelte.js';
|
|
21
|
+
import type { BlockMovePosition } from './moves.js';
|
|
21
22
|
type Kind = 'text' | 'composition' | 'payload' | 'break' | 'delete' | 'history';
|
|
22
23
|
export type IntentRow = {
|
|
23
24
|
kind: Kind;
|
|
@@ -63,10 +64,10 @@ export declare const untargeted: (edytor: Edytor) => boolean;
|
|
|
63
64
|
* void's own field.
|
|
64
65
|
*/
|
|
65
66
|
declare const facts: (edytor: Edytor) => {
|
|
66
|
-
projection: import("./selection").SelectionProjection;
|
|
67
|
+
projection: import("./selection.js").SelectionProjection;
|
|
67
68
|
isCollapsed: boolean;
|
|
68
69
|
/** The admitted mark-edge side of a caret. */
|
|
69
|
-
edge: import("./editing/text").EdgeSide | undefined;
|
|
70
|
+
edge: import("./editing/text.js").EdgeSide | undefined;
|
|
70
71
|
isVoidEditableElement: boolean;
|
|
71
72
|
startText: Text | null;
|
|
72
73
|
endText: Text | null;
|
|
@@ -74,7 +75,7 @@ declare const facts: (edytor: Edytor) => {
|
|
|
74
75
|
yEnd: number;
|
|
75
76
|
texts: Text[];
|
|
76
77
|
/** The anchored target, fixed at admission. */
|
|
77
|
-
target: import("./selection").SelectionValue;
|
|
78
|
+
target: import("./selection.js").SelectionValue;
|
|
78
79
|
};
|
|
79
80
|
/**
|
|
80
81
|
* How DOM drift around a model-owned attempt is repaired: the observer
|
|
@@ -107,9 +108,16 @@ export type Occurrence = {
|
|
|
107
108
|
dataTransfer?: DataTransfer | null;
|
|
108
109
|
/** The range the browser declared (`getTargetRanges()`, a drop point). */
|
|
109
110
|
declared?: StaticRange | null;
|
|
111
|
+
/** Where a file drop lands, the indicator's placement (`plugins/fileDrop.ts`). */
|
|
112
|
+
placement?: DropAt | null;
|
|
110
113
|
cancelable: boolean;
|
|
111
114
|
event?: InputEvent;
|
|
112
115
|
};
|
|
116
|
+
/** A drop's placement in block terms: beside, before, after or inside `block`. */
|
|
117
|
+
export type DropAt = {
|
|
118
|
+
block: string;
|
|
119
|
+
position: BlockMovePosition;
|
|
120
|
+
};
|
|
113
121
|
export type Attempt = ReturnType<typeof facts> & {
|
|
114
122
|
id: number;
|
|
115
123
|
/** The intent, fixed at admission. */
|
|
@@ -121,6 +129,7 @@ export type Attempt = ReturnType<typeof facts> & {
|
|
|
121
129
|
dataTransfer: DataTransfer | null;
|
|
122
130
|
hasDataTransferTextPayload: boolean;
|
|
123
131
|
declared: StaticRange | null;
|
|
132
|
+
placement: DropAt | null;
|
|
124
133
|
/** Admitted from a keydown (the key's intent wins; the browser may never announce it). */
|
|
125
134
|
isStructuralKeyFallback: boolean;
|
|
126
135
|
owner: 'model' | 'browser';
|
|
@@ -135,10 +144,10 @@ export declare const intentSnapshot: (edytor: Edytor, inputType: string) => Atte
|
|
|
135
144
|
export declare const caretAt: (edytor: Edytor, text: Text | null | undefined, offset: number) => void;
|
|
136
145
|
/** Re-project an attempt's anchored target when it runs later than its admission. */
|
|
137
146
|
export declare const reproject: (edytor: Edytor, attempt: Attempt) => {
|
|
138
|
-
projection: import("./selection").SelectionProjection;
|
|
147
|
+
projection: import("./selection.js").SelectionProjection;
|
|
139
148
|
isCollapsed: boolean;
|
|
140
149
|
/** The admitted mark-edge side of a caret. */
|
|
141
|
-
edge: import("./editing/text").EdgeSide | undefined;
|
|
150
|
+
edge: import("./editing/text.js").EdgeSide | undefined;
|
|
142
151
|
isVoidEditableElement: boolean;
|
|
143
152
|
startText: Text | null;
|
|
144
153
|
endText: Text | null;
|
|
@@ -146,7 +155,7 @@ export declare const reproject: (edytor: Edytor, attempt: Attempt) => {
|
|
|
146
155
|
yEnd: number;
|
|
147
156
|
texts: Text[];
|
|
148
157
|
/** The anchored target, fixed at admission. */
|
|
149
|
-
target: import("./selection").SelectionValue;
|
|
158
|
+
target: import("./selection.js").SelectionValue;
|
|
150
159
|
} & {
|
|
151
160
|
id: number;
|
|
152
161
|
/** The intent, fixed at admission. */
|
|
@@ -158,6 +167,7 @@ export declare const reproject: (edytor: Edytor, attempt: Attempt) => {
|
|
|
158
167
|
dataTransfer: DataTransfer | null;
|
|
159
168
|
hasDataTransferTextPayload: boolean;
|
|
160
169
|
declared: StaticRange | null;
|
|
170
|
+
placement: DropAt | null;
|
|
161
171
|
/** Admitted from a keydown (the key's intent wins; the browser may never announce it). */
|
|
162
172
|
isStructuralKeyFallback: boolean;
|
|
163
173
|
owner: "model" | "browser";
|
package/dist/session/attempt.js
CHANGED
|
@@ -119,6 +119,7 @@ export const attemptOf = (edytor, occurrence, keyIntent) => {
|
|
|
119
119
|
dataTransfer: occurrence.dataTransfer ?? null,
|
|
120
120
|
hasDataTransferTextPayload: payload !== null,
|
|
121
121
|
declared: occurrence.declared ?? null,
|
|
122
|
+
placement: occurrence.placement ?? null,
|
|
122
123
|
isStructuralKeyFallback: Boolean(keyIntent),
|
|
123
124
|
owner: 'model',
|
|
124
125
|
expect: null,
|
package/dist/session/bindings.js
CHANGED
|
@@ -24,7 +24,7 @@ export const replaceSelectedAtom = (edytor, value = '') => {
|
|
|
24
24
|
edytor.removeInline(parent.id, selected.id);
|
|
25
25
|
if (value)
|
|
26
26
|
edytor.insertText(parent.id, text.segStart + offset, value);
|
|
27
|
-
edytor.dispatcher.
|
|
27
|
+
edytor.dispatcher.caretIn(text, offset + value.length);
|
|
28
28
|
return true;
|
|
29
29
|
};
|
|
30
30
|
/** The selection is the editor's own: an inline atom or blocks (no DOM range shows it). */
|
|
@@ -119,41 +119,44 @@ const moveRoots = (edytor, blocks, direction) => {
|
|
|
119
119
|
.flatMap((moved) => moved ?? []);
|
|
120
120
|
};
|
|
121
121
|
/**
|
|
122
|
-
* Tab / Shift+Tab
|
|
122
|
+
* Tab / Shift+Tab, with `allowNesting`: nest or unnest the selected block, or the caret's block.
|
|
123
123
|
* Selected blocks, or the blocks a text range spans, move one level per
|
|
124
124
|
* group of siblings, a selected block's selected descendants with it, as
|
|
125
125
|
* one command; the selection stays (its anchors moved with the blocks).
|
|
126
126
|
*/
|
|
127
|
-
const nest = (operation) => ({ edytor, prevent }) =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
127
|
+
const nest = (operation) => ({ edytor, prevent }) =>
|
|
128
|
+
// Nesting off (`allowNesting`, the default): the key is not the editor's, the browser moves the focus on.
|
|
129
|
+
edytor.allowNesting &&
|
|
130
|
+
prevent(() => {
|
|
131
|
+
const { selection } = edytor;
|
|
132
|
+
const { value } = selection;
|
|
133
|
+
const direction = operation === 'nestBlock' ? 'in' : 'out';
|
|
134
|
+
const blocks = getSelectionBlocks(edytor);
|
|
135
|
+
if (blocks.length > 1) {
|
|
136
|
+
if (moveRoots(edytor, blocks, direction).length)
|
|
137
|
+
selection.select(value);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const [selected] = selection.selectedBlocks;
|
|
141
|
+
const at = selection.textAt(selection.projection.start);
|
|
142
|
+
const block = selected ?? at?.text.parent;
|
|
143
|
+
if (!block)
|
|
144
|
+
return;
|
|
145
|
+
// Into the block before it; the view's move reveals a closed toggle it lands in or that adopts.
|
|
146
|
+
const into = block.previousBlock;
|
|
147
|
+
if (operation === 'nestBlock' && !into)
|
|
148
|
+
return edytor.dispatcher.record({ operation, status: 'refused' });
|
|
149
|
+
const result = operation === 'unNestBlock'
|
|
150
|
+
? edytor.unNestBlock(block.id)
|
|
151
|
+
: edytor.nestBlock(block.id, into.id);
|
|
152
|
+
if (result.status !== 'applied')
|
|
153
|
+
return;
|
|
154
|
+
edytor.announcer.moved({ blocks: [block], direction }, [block]);
|
|
155
|
+
if (selected)
|
|
156
|
+
selection.select(blockSelection([block.id]));
|
|
157
|
+
else if (at)
|
|
158
|
+
caretAt(edytor, block.content[at.text.index], at.offset);
|
|
159
|
+
});
|
|
157
160
|
/**
|
|
158
161
|
* Backspace/Delete over an editor-owned selection: the selected atom, or
|
|
159
162
|
* the selected blocks (the caret lands at the end of the block before them).
|
|
@@ -128,18 +128,14 @@ export declare class Dispatcher {
|
|
|
128
128
|
taken: boolean;
|
|
129
129
|
};
|
|
130
130
|
/**
|
|
131
|
-
* A command's result caret
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* block offsets (an inline atom counting 1).
|
|
131
|
+
* A command's result caret, `{ block, offset }` in block offsets (an
|
|
132
|
+
* inline atom counting 1): selected once and recorded on the result;
|
|
133
|
+
* the projector displays it after the flush. With `ops`, the caret is
|
|
134
|
+
* declared before the command's operations run — minted while its text
|
|
135
|
+
* is live, so it survives them — and written only when they applied;
|
|
136
|
+
* meanwhile the seam repair leaves this view's endpoints to it.
|
|
138
137
|
*/
|
|
139
|
-
caret:
|
|
140
|
-
(text: Text | null | undefined, offset: number, ops?: () => OpResult): OpResult | undefined;
|
|
141
|
-
(caret: Caret | DocPosition | null | undefined, ops?: () => OpResult): OpResult | undefined;
|
|
142
|
-
};
|
|
138
|
+
caret: (caret: Caret | DocPosition | null | undefined, ops?: () => OpResult) => OpResult | undefined;
|
|
143
139
|
/**
|
|
144
140
|
* The cut an operation applies as it writes. Inside a user command none
|
|
145
141
|
* (the command cut); outside one, its bare policy. A led operation is a
|
package/dist/session/commands.js
CHANGED
|
@@ -458,6 +458,8 @@ export class Dispatcher {
|
|
|
458
458
|
*/
|
|
459
459
|
operate = (operation, args) => {
|
|
460
460
|
const { edytor } = this;
|
|
461
|
+
// The property writes queued in this task go first (`session/props`).
|
|
462
|
+
edytor.dataWrites.flush();
|
|
461
463
|
const own = edytor.facade.prepare[operation];
|
|
462
464
|
const prepare = (a) => {
|
|
463
465
|
if (operation === 'insertText' && a[3] === undefined)
|
|
@@ -564,27 +566,20 @@ export class Dispatcher {
|
|
|
564
566
|
return this.running ? this.kind : null;
|
|
565
567
|
}
|
|
566
568
|
/**
|
|
567
|
-
* A command's result caret
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
* block offsets (an inline atom counting 1).
|
|
569
|
+
* A command's result caret, `{ block, offset }` in block offsets (an
|
|
570
|
+
* inline atom counting 1): selected once and recorded on the result;
|
|
571
|
+
* the projector displays it after the flush. With `ops`, the caret is
|
|
572
|
+
* declared before the command's operations run — minted while its text
|
|
573
|
+
* is live, so it survives them — and written only when they applied;
|
|
574
|
+
* meanwhile the seam repair leaves this view's endpoints to it.
|
|
574
575
|
*/
|
|
575
|
-
caret = (
|
|
576
|
-
// The form is the arguments', not the target's: a nullish caret still runs its `ops`.
|
|
577
|
-
if (typeof rest[0] === 'number') {
|
|
578
|
-
return this.#caret(target, rest[0], rest[1]);
|
|
579
|
-
}
|
|
580
|
-
const ops = rest[0];
|
|
581
|
-
const caret = target;
|
|
582
|
-
// `{ block, offset }`: block offsets (a handle or an id), mapped to the text segment that shows the offset.
|
|
576
|
+
caret = (caret, ops) => {
|
|
583
577
|
const block = typeof caret?.block === 'string' ? this.edytor.idToBlock.get(caret.block) : caret?.block;
|
|
584
578
|
const at = block?.textAtOffset(Math.max(0, caret.offset));
|
|
585
|
-
return this
|
|
579
|
+
return this.caretIn(at?.text, at?.offset ?? 0, ops);
|
|
586
580
|
};
|
|
587
|
-
|
|
581
|
+
/** @internal `caret` at an offset inside a text segment. */
|
|
582
|
+
caretIn = (text, offset, ops) => {
|
|
588
583
|
const selection = this.edytor.selection;
|
|
589
584
|
const at = text ? Math.max(0, Math.min(offset, text.length)) : 0;
|
|
590
585
|
const value = text ? selection.textValue(text, at) : null;
|
|
@@ -640,7 +635,8 @@ export class Dispatcher {
|
|
|
640
635
|
try {
|
|
641
636
|
for (let i = 0; i < this.queue.length; i++) {
|
|
642
637
|
if (i === 1000) {
|
|
643
|
-
|
|
638
|
+
if (DEV)
|
|
639
|
+
console.warn('[edytor] normalization is not converging; skipping further passes');
|
|
644
640
|
break;
|
|
645
641
|
}
|
|
646
642
|
const entry = this.queue[i];
|
|
@@ -652,7 +648,7 @@ export class Dispatcher {
|
|
|
652
648
|
const count = (counts.get(id) ?? 0) + 1;
|
|
653
649
|
counts.set(id, count);
|
|
654
650
|
if (count > MAX_PASSES) {
|
|
655
|
-
if (count === MAX_PASSES + 1)
|
|
651
|
+
if (DEV && count === MAX_PASSES + 1)
|
|
656
652
|
console.warn(`edytor: ${normalize} on block "${id}" exceeded ${MAX_PASSES - 1} passes — a plugin normalizer is not converging; skipping further passes`);
|
|
657
653
|
continue;
|
|
658
654
|
}
|
package/dist/session/history.js
CHANGED
|
@@ -114,6 +114,7 @@ export class History {
|
|
|
114
114
|
replay = (command) => !this.edytor.dispatcher.permits() ? 'refused' : this.#run(command) ? 'applied' : 'noop';
|
|
115
115
|
#run(command) {
|
|
116
116
|
const { undoManager: um, transaction: key, selection, facade } = this.edytor;
|
|
117
|
+
this.edytor.dataWrites.flush(); // this task's property writes are a step first
|
|
117
118
|
this.#open = null;
|
|
118
119
|
// Other writers (remote-apply and repair restores) stand aside while the
|
|
119
120
|
// replay commits: this view's recorded value decides.
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
* (`## ` → a heading, `:smile:` → 😄). The one engine behind every
|
|
4
4
|
* plugin's `inputRules`: it reads the typed text and the caret, finds the
|
|
5
5
|
* first rule whose `find` ends at the typed text, and replaces the typed
|
|
6
|
-
* `insertText` with
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* `insertText` with two steps: the typed text as typed (an insertion, which
|
|
7
|
+
* may continue the typing before it), then the rule's replacement, a command
|
|
8
|
+
* of its own whose first operation the matched text's removal leads
|
|
9
|
+
* (`dispatcher.lead`), so a refusal of either writes neither and the typed
|
|
10
|
+
* text stays as typed. The first undo gives the typed text back, the
|
|
11
|
+
* caret after it (as the autolink does).
|
|
9
12
|
*/
|
|
10
13
|
import type { Block } from '../block/block.svelte.js';
|
|
11
14
|
import type { Edytor } from '../edytor.svelte.js';
|
|
@@ -47,7 +47,7 @@ export const inputRulesHook = (edytor, rules) => {
|
|
|
47
47
|
/** The typed text lands as typed, the caret after it. */
|
|
48
48
|
const typeAsIs = () => {
|
|
49
49
|
edytor.insertText(...payload);
|
|
50
|
-
dispatcher.
|
|
50
|
+
dispatcher.caretIn(text, yStart + value.length);
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
53
|
* Run `rule` over `match`: whether it wrote, or `null` when it declined
|
|
@@ -57,7 +57,8 @@ export const inputRulesHook = (edytor, rules) => {
|
|
|
57
57
|
/** The match's start in the segment; the match in the block, `from` to the caret. */
|
|
58
58
|
const start = match.index;
|
|
59
59
|
const from = text.segStart + start;
|
|
60
|
-
|
|
60
|
+
// The typed text is in the document: the match ends after it.
|
|
61
|
+
const to = text.segStart + yStart + value.length;
|
|
61
62
|
let removed = null;
|
|
62
63
|
/** Remove the match in the same plan as `then`'s first operation: `ctx.remove`. */
|
|
63
64
|
const remove = (then) => {
|
|
@@ -104,12 +105,15 @@ export const inputRulesHook = (edytor, rules) => {
|
|
|
104
105
|
prevent(() => {
|
|
105
106
|
running = true;
|
|
106
107
|
try {
|
|
108
|
+
// The typed text first, a step the replacement does not join.
|
|
109
|
+
const last = dispatcher.last;
|
|
110
|
+
typeAsIs();
|
|
111
|
+
if (dispatcher.last === last || dispatcher.last?.status !== 'applied')
|
|
112
|
+
return;
|
|
107
113
|
for (const { rule, match } of matches) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return written || typeAsIs();
|
|
114
|
+
if (apply(rule, match) !== null)
|
|
115
|
+
return;
|
|
111
116
|
}
|
|
112
|
-
typeAsIs();
|
|
113
117
|
}
|
|
114
118
|
finally {
|
|
115
119
|
running = false;
|
package/dist/session/moves.d.ts
CHANGED
|
@@ -25,6 +25,17 @@ export type BlockMoveRequest = {
|
|
|
25
25
|
} | {
|
|
26
26
|
direction: BlockMoveDirection;
|
|
27
27
|
});
|
|
28
|
+
/**
|
|
29
|
+
* The view's nesting rule (`allowNesting`, off by default): whether a user
|
|
30
|
+
* gesture may place `blocks` under `parent`. With nesting off, blocks land
|
|
31
|
+
* only at the root or in a block made to hold children — a container (a
|
|
32
|
+
* list, a column, a toggle's or a callout's body: `isContainer`, the kind's
|
|
33
|
+
* `container`), an island (a code block's lines) — or stay under the parent
|
|
34
|
+
* they already have (a document holding nested blocks still edits them).
|
|
35
|
+
* Every move (`destination`: keys, drops, menus, `edytor.move`) and every
|
|
36
|
+
* new block a drop places (`canPlaceNew`) reads it.
|
|
37
|
+
*/
|
|
38
|
+
export declare const nestsInto: (edytor: Edytor, parent: Block, blocks?: readonly Block[]) => boolean;
|
|
28
39
|
/** Whether the move is allowed: for runs (`parts`), whether one of them may move. */
|
|
29
40
|
export declare const canMove: (edytor: Edytor, request: BlockMoveRequest) => boolean;
|
|
30
41
|
/**
|
|
@@ -38,3 +49,25 @@ export declare const canMove: (edytor: Edytor, request: BlockMoveRequest) => boo
|
|
|
38
49
|
* (`announcer.moved`).
|
|
39
50
|
*/
|
|
40
51
|
export declare const move: (edytor: Edytor, request: BlockMoveRequest) => Block[];
|
|
52
|
+
/** A new block a drop places (`placeNew`): its kind, and its id when the caller names it. */
|
|
53
|
+
export type NewBlock = {
|
|
54
|
+
type: string;
|
|
55
|
+
id?: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Whether new blocks of `kinds` may land at `target`'s `position` (a file
|
|
59
|
+
* dropped there, `placeNew`): the move's places (`destination`) asked of
|
|
60
|
+
* blocks not yet in the document — the container rule (`fits`) of the
|
|
61
|
+
* parent they land in, never inside a void or an island; beside, a layout
|
|
62
|
+
* kind and a target outside any island (`placeBeside` takes any block a
|
|
63
|
+
* column holds).
|
|
64
|
+
*/
|
|
65
|
+
export declare const canPlaceNew: (edytor: Edytor, kinds: readonly string[], target: Block, position: BlockMovePosition) => boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Insert new blocks at `target`'s `position` (a file drop's placement):
|
|
68
|
+
* before or after it, inside it (its last children), or beside it — inserted
|
|
69
|
+
* after the block it stands beside (`besideAt`) and moved there by the one
|
|
70
|
+
* move path (`layout.place-beside`) — in one transaction. The caller's
|
|
71
|
+
* command makes it one undo step. Answers the ids placed (none: refused).
|
|
72
|
+
*/
|
|
73
|
+
export declare const placeNew: (edytor: Edytor, blocks: readonly NewBlock[], target: Block, position: BlockMovePosition) => string[];
|
package/dist/session/moves.js
CHANGED
|
@@ -24,6 +24,22 @@ const placement = (edytor, request) => {
|
|
|
24
24
|
return step?.[0] ? { blocks, target: step[0], position: step[1] } : null;
|
|
25
25
|
};
|
|
26
26
|
const POSITIONS = ['before', 'after', 'inside', ...BESIDE];
|
|
27
|
+
/**
|
|
28
|
+
* The view's nesting rule (`allowNesting`, off by default): whether a user
|
|
29
|
+
* gesture may place `blocks` under `parent`. With nesting off, blocks land
|
|
30
|
+
* only at the root or in a block made to hold children — a container (a
|
|
31
|
+
* list, a column, a toggle's or a callout's body: `isContainer`, the kind's
|
|
32
|
+
* `container`), an island (a code block's lines) — or stay under the parent
|
|
33
|
+
* they already have (a document holding nested blocks still edits them).
|
|
34
|
+
* Every move (`destination`: keys, drops, menus, `edytor.move`) and every
|
|
35
|
+
* new block a drop places (`canPlaceNew`) reads it.
|
|
36
|
+
*/
|
|
37
|
+
export const nestsInto = (edytor, parent, blocks = []) => edytor.allowNesting ||
|
|
38
|
+
parent.isRoot ||
|
|
39
|
+
parent.isContainer ||
|
|
40
|
+
!!parent.definition.container ||
|
|
41
|
+
edytor.facade.isIsland(parent.id) ||
|
|
42
|
+
(blocks.length > 0 && blocks.every((block) => block.parent === parent));
|
|
27
43
|
/**
|
|
28
44
|
* The document write a move request is, or `null` when refused: a
|
|
29
45
|
* well-formed request, then the document's one structural answer
|
|
@@ -59,7 +75,7 @@ const destination = (edytor, request) => {
|
|
|
59
75
|
? { blocks, write: () => edytor.unNestBlocks(ids) }
|
|
60
76
|
: null;
|
|
61
77
|
const to = parent?.isRoot ? null : parent?.id;
|
|
62
|
-
if (!parent || !edytor.facade.canPlace(ids, to))
|
|
78
|
+
if (!parent || !nestsInto(edytor, parent, blocks) || !edytor.facade.canPlace(ids, to))
|
|
63
79
|
return null;
|
|
64
80
|
const at = position === 'inside' ? parent.children.length : target.index + (position === 'after' ? 1 : 0);
|
|
65
81
|
// The document counts the destination's children without the moved blocks.
|
|
@@ -122,3 +138,57 @@ const place = (edytor, request) => {
|
|
|
122
138
|
const result = move.write();
|
|
123
139
|
return result.status === 'applied' ? move.blocks : [];
|
|
124
140
|
};
|
|
141
|
+
/**
|
|
142
|
+
* Whether new blocks of `kinds` may land at `target`'s `position` (a file
|
|
143
|
+
* dropped there, `placeNew`): the move's places (`destination`) asked of
|
|
144
|
+
* blocks not yet in the document — the container rule (`fits`) of the
|
|
145
|
+
* parent they land in, never inside a void or an island; beside, a layout
|
|
146
|
+
* kind and a target outside any island (`placeBeside` takes any block a
|
|
147
|
+
* column holds).
|
|
148
|
+
*/
|
|
149
|
+
export const canPlaceNew = (edytor, kinds, target, position) => {
|
|
150
|
+
const { facade } = edytor;
|
|
151
|
+
if (edytor.readonly || !kinds.length || !target.isInTree || target.isRoot)
|
|
152
|
+
return false;
|
|
153
|
+
if (!POSITIONS.includes(position) || facade.insideIsland(target.id))
|
|
154
|
+
return false;
|
|
155
|
+
if (isBeside(position))
|
|
156
|
+
return ([...edytor.blocks.values()].some((definition) => definition.layout) &&
|
|
157
|
+
!facade.isIsland(target.id));
|
|
158
|
+
const parent = position === 'inside' ? target : target.parent;
|
|
159
|
+
if (!parent || !nestsInto(edytor, parent))
|
|
160
|
+
return false;
|
|
161
|
+
const to = parent.isRoot ? null : parent.id;
|
|
162
|
+
if (to !== null && (facade.isVoid(to) || facade.isIsland(to)))
|
|
163
|
+
return false;
|
|
164
|
+
return kinds.every((kind) => facade.fits(to, kind));
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Insert new blocks at `target`'s `position` (a file drop's placement):
|
|
168
|
+
* before or after it, inside it (its last children), or beside it — inserted
|
|
169
|
+
* after the block it stands beside (`besideAt`) and moved there by the one
|
|
170
|
+
* move path (`layout.place-beside`) — in one transaction. The caller's
|
|
171
|
+
* command makes it one undo step. Answers the ids placed (none: refused).
|
|
172
|
+
*/
|
|
173
|
+
export const placeNew = (edytor, blocks, target, position) => {
|
|
174
|
+
const kinds = blocks.map((block) => block.type);
|
|
175
|
+
if (!canPlaceNew(edytor, kinds, target, position))
|
|
176
|
+
return [];
|
|
177
|
+
const beside = isBeside(position);
|
|
178
|
+
const at = beside ? (edytor.idToBlock.get(edytor.facade.besideAt(target.id)) ?? target) : target;
|
|
179
|
+
const parent = position === 'inside' ? target : at.parent;
|
|
180
|
+
const destination = position === 'inside'
|
|
181
|
+
? { parent: target.id, index: target.children.length }
|
|
182
|
+
: {
|
|
183
|
+
parent: !parent || parent.isRoot ? null : parent.id,
|
|
184
|
+
index: at.index + (position === 'before' ? 0 : 1)
|
|
185
|
+
};
|
|
186
|
+
return edytor.transact(() => {
|
|
187
|
+
const { ids } = edytor.insertBlocks(destination, blocks.map((block) => ({ ...block })));
|
|
188
|
+
if (!beside || !ids.length)
|
|
189
|
+
return [...ids];
|
|
190
|
+
const placed = ids.flatMap((id) => edytor.idToBlock.get(id) ?? []);
|
|
191
|
+
move(edytor, { blocks: placed, target, position });
|
|
192
|
+
return [...ids];
|
|
193
|
+
});
|
|
194
|
+
};
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* lands it on no caret stop (`landed`).
|
|
24
24
|
*/
|
|
25
25
|
import type { Edytor } from '../edytor.svelte.js';
|
|
26
|
+
import type { Block } from '../block/block.svelte.js';
|
|
26
27
|
import type { HotKey } from './keymap.js';
|
|
27
28
|
/**
|
|
28
29
|
* With no target (`untargeted`: the value is `none`, no gesture placed the
|
|
@@ -48,6 +49,12 @@ export declare const move: (edytor: Edytor, unit: Unit, key: Dir | "left" | "rig
|
|
|
48
49
|
* moves keep the goal column. Answers whether a text selection moved.
|
|
49
50
|
*/
|
|
50
51
|
export declare const extendVertically: (edytor: Edytor, dir: Dir) => boolean;
|
|
52
|
+
/**
|
|
53
|
+
* The block a collapsed text caret sits on the last line of (a line box
|
|
54
|
+
* where the view lays one out, else a line break), if it does: where
|
|
55
|
+
* ArrowDown leaves the block (`nav.trailing.exit`, `body.down`).
|
|
56
|
+
*/
|
|
57
|
+
export declare const onLastLine: (edytor: Edytor) => Block | undefined;
|
|
51
58
|
/**
|
|
52
59
|
* The trailing paragraph (`nav.trailing`, Notion): the caret goes to an
|
|
53
60
|
* empty top-level block of the root's default kind after the document's
|