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
|
@@ -11,8 +11,7 @@ export const caretRect = (edytor) => {
|
|
|
11
11
|
if (selection?.rangeCount && editor.contains(selection.anchorNode)) {
|
|
12
12
|
const range = selection.getRangeAt(0).cloneRange();
|
|
13
13
|
range.collapse(false);
|
|
14
|
-
|
|
15
|
-
rect = range.getBoundingClientRect();
|
|
14
|
+
rect = range.getBoundingClientRect();
|
|
16
15
|
}
|
|
17
16
|
if (!rect || (!rect.width && !rect.height)) {
|
|
18
17
|
rect = edytor.selection.textRange().startText?.node?.getBoundingClientRect();
|
|
@@ -9,9 +9,14 @@
|
|
|
9
9
|
* none of it.
|
|
10
10
|
*/
|
|
11
11
|
import type { Edytor } from '../../edytor.svelte.js';
|
|
12
|
-
import type { InitializedPlugin, InputRule, Trigger } from '../../plugins.js';
|
|
12
|
+
import type { InitializedPlugin, InputRule, TextRuleContext, Trigger } from '../../plugins.js';
|
|
13
13
|
import { type TextTriggerController } from './TriggerController.svelte.js';
|
|
14
14
|
import { type HeldList } from '../list.svelte.js';
|
|
15
|
+
/**
|
|
16
|
+
* Write `atom` where the matched text starts (`ctx.from`: a trigger's pick,
|
|
17
|
+
* an input rule's replacement), the caret after it; whether it was written.
|
|
18
|
+
*/
|
|
19
|
+
export declare const placeAtom: ({ edytor, block, from, caret }: TextRuleContext, atom: Parameters<Edytor["insertInline"]>[2]) => boolean;
|
|
15
20
|
/**
|
|
16
21
|
* `plugin` with `inputRules` (text typed at the caret that a rule
|
|
17
22
|
* replaces: markdown, `:smile:` → 😄) and `triggers` (characters that open
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { inputRulesHook } from '../../session/inputRules.js';
|
|
2
2
|
import { TriggerMenuController } from './TriggerController.svelte.js';
|
|
3
|
-
import { caretRect } from './place.js';
|
|
4
|
-
import { placeNear } from '../../surface/overlay.js';
|
|
5
3
|
import { listHotkeys } from '../list.svelte.js';
|
|
6
4
|
import { mountChrome } from '../chrome.js';
|
|
7
5
|
/** Two bindings of one chord, `first`'s claim ending the key. */
|
|
@@ -11,6 +9,16 @@ const both = (first, then) => (payload) => {
|
|
|
11
9
|
};
|
|
12
10
|
/** The triggers each view numbered so far: a trigger's popup owner name. */
|
|
13
11
|
const counts = new WeakMap();
|
|
12
|
+
/**
|
|
13
|
+
* Write `atom` where the matched text starts (`ctx.from`: a trigger's pick,
|
|
14
|
+
* an input rule's replacement), the caret after it; whether it was written.
|
|
15
|
+
*/
|
|
16
|
+
export const placeAtom = ({ edytor, block, from, caret }, atom) => {
|
|
17
|
+
const written = edytor.insertInline(block.id, from, atom).status === 'applied';
|
|
18
|
+
if (written)
|
|
19
|
+
caret(from + 1);
|
|
20
|
+
return written;
|
|
21
|
+
};
|
|
14
22
|
/**
|
|
15
23
|
* `plugin` with `inputRules` (text typed at the caret that a rule
|
|
16
24
|
* replaces: markdown, `:smile:` → 😄) and `triggers` (characters that open
|
|
@@ -47,17 +55,8 @@ export const withRulesAndTriggers = (edytor, plugin, { inputRules = [], triggers
|
|
|
47
55
|
: undefined,
|
|
48
56
|
close: () => controller.close()
|
|
49
57
|
}),
|
|
50
|
-
//
|
|
51
|
-
(controller) => mountChrome(edytor, controller.trigger.menu, controller, () => controller.isOpen, {
|
|
52
|
-
name: 'edytor-trigger-menu-host',
|
|
53
|
-
zIndex: 50,
|
|
54
|
-
measure: (host) => {
|
|
55
|
-
if (!controller.isOpen)
|
|
56
|
-
return;
|
|
57
|
-
const rect = caretRect(edytor);
|
|
58
|
-
return rect && placeNear(host, rect, [280, 240]);
|
|
59
|
-
}
|
|
60
|
-
}, () => controller.close()));
|
|
58
|
+
// Its markup places itself by the controller's `anchor` (the caret).
|
|
59
|
+
(controller) => mountChrome(edytor, controller.trigger.menu, controller, () => controller.isOpen, { name: 'edytor-trigger-menu-host', zIndex: 50 }, () => controller.close()));
|
|
61
60
|
};
|
|
62
61
|
/**
|
|
63
62
|
* `plugin` with trigger menus wired into its hooks (a plugin's `triggers`,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { Edytor } from '../edytor.svelte.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { PluginDefinitions, PluginOperations } from '../plugins.js';
|
|
3
|
+
import type { DropAt } from '../session/attempt.js';
|
|
4
|
+
import { type ViewUploads } from '../session/uploads.js';
|
|
5
|
+
import { FileDrag } from './fileDrop.js';
|
|
3
6
|
/** What an `upload` may report while it runs. */
|
|
4
7
|
export type UploadReport = {
|
|
5
8
|
/** The share sent so far, `0` to `1`: the block shows it as a bar. */
|
|
@@ -17,8 +20,14 @@ export type Uploader = (file: File, report: UploadReport) => Promise<string>;
|
|
|
17
20
|
*/
|
|
18
21
|
export type FileUpload = {
|
|
19
22
|
readonly status: 'uploading';
|
|
20
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* The file, shown while it uploads: its object URL (`blob:`), this
|
|
25
|
+
* view's only, never stored; `null` where there is none.
|
|
26
|
+
*/
|
|
21
27
|
readonly preview: string | null;
|
|
28
|
+
/** The file's name and size in bytes, shown at once. */
|
|
29
|
+
readonly name: string;
|
|
30
|
+
readonly size: number;
|
|
22
31
|
/** The share sent, `0` to `1`; `null` until `upload` reports one. */
|
|
23
32
|
readonly progress: number | null;
|
|
24
33
|
} | {
|
|
@@ -29,10 +38,12 @@ export type FileUpload = {
|
|
|
29
38
|
};
|
|
30
39
|
/**
|
|
31
40
|
* What files a kind takes (its record's `upload`, `BlockDefinition`): which,
|
|
32
|
-
*
|
|
33
|
-
*
|
|
41
|
+
* who sends them (`send`, its plugin's `upload` option) and the data an
|
|
42
|
+
* upload's answer writes. A kind without it takes no files.
|
|
34
43
|
*/
|
|
35
44
|
export type KindUpload = {
|
|
45
|
+
/** Upload a file of this kind and answer its URL (its plugin's `upload`). */
|
|
46
|
+
send: Uploader;
|
|
36
47
|
/** Whether it takes `file`. */
|
|
37
48
|
accepts: (file: File) => boolean;
|
|
38
49
|
/** It takes any file no other kind takes (the file block): asked last. */
|
|
@@ -41,14 +52,13 @@ export type KindUpload = {
|
|
|
41
52
|
data: (file: File, answer: string) => Record<string, unknown> | null;
|
|
42
53
|
/** Whether the block has a source already (a peer's, a link's): its upload then writes nothing. */
|
|
43
54
|
filled: (data: Record<string, unknown>) => boolean;
|
|
44
|
-
/** Show the file while it uploads (an image's `blob:` preview). */
|
|
45
|
-
preview?: boolean;
|
|
46
55
|
};
|
|
47
56
|
/**
|
|
48
|
-
* The view's uploads (`media.files`), one per view, the
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
* takes files by its record's `upload
|
|
57
|
+
* The view's uploads (`media.files`), one per view, claimed by the first
|
|
58
|
+
* media plugin listed with an `upload`: the uploads of pasted, dropped and
|
|
59
|
+
* picked files (an empty block's Upload), session state, never in the
|
|
60
|
+
* document. A kind takes files by its record's `upload`, and sends them
|
|
61
|
+
* with its `send` (its plugin's uploader); a paste or a drop (a drop replays
|
|
52
62
|
* the paste hooks) of files the kinds take places one empty block per
|
|
53
63
|
* file, each of the kind that takes it (`fallback` last), in the
|
|
54
64
|
* clipboard's order, as one paste (one command, one undo step), and each
|
|
@@ -62,14 +72,16 @@ export type KindUpload = {
|
|
|
62
72
|
* (`fill`, asked by the empty block's render), so a redo brings it back
|
|
63
73
|
* whole.
|
|
64
74
|
*/
|
|
65
|
-
export declare class FileUploads {
|
|
75
|
+
export declare class FileUploads implements ViewUploads {
|
|
66
76
|
#private;
|
|
67
77
|
private edytor;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/** The
|
|
72
|
-
|
|
78
|
+
constructor(edytor: Edytor);
|
|
79
|
+
/** The kind that takes `file`, if any. */
|
|
80
|
+
kindOf: (file: File) => string | undefined;
|
|
81
|
+
/** The types of the kinds that take files. */
|
|
82
|
+
get types(): string[];
|
|
83
|
+
/** Files dragged from outside: the drop indicator and the placement (`fileDrop.ts`). */
|
|
84
|
+
readonly drop: FileDrag;
|
|
73
85
|
/** The upload of block `id` in this view (reactive), if any. */
|
|
74
86
|
of: (id: string) => FileUpload | undefined;
|
|
75
87
|
/**
|
|
@@ -78,8 +90,20 @@ export declare class FileUploads {
|
|
|
78
90
|
* clipboard).
|
|
79
91
|
*/
|
|
80
92
|
paste: NonNullable<PluginOperations['onPaste']>;
|
|
81
|
-
/**
|
|
82
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Place a block per file at the selection (one paste), or at a drop's
|
|
95
|
+
* placement (`at`: beside, before, after or inside a block, one command),
|
|
96
|
+
* then upload each.
|
|
97
|
+
*/
|
|
98
|
+
insert: (files: readonly File[], at?: DropAt) => void;
|
|
99
|
+
/**
|
|
100
|
+
* Upload `file`, picked on the empty block `id` (its Upload), with its
|
|
101
|
+
* kind's uploader, the block showing it meanwhile, and hand the answer to
|
|
102
|
+
* `land` before the preview goes (so the block never shows nothing
|
|
103
|
+
* between them): `false` when it failed (the block then shows no
|
|
104
|
+
* upload; the caller says why).
|
|
105
|
+
*/
|
|
106
|
+
pick: (id: string, file: File, land: (answer: string) => void) => Promise<boolean>;
|
|
83
107
|
/** Forget the block's upload (a source was embedded: its error, or held data, no longer applies). */
|
|
84
108
|
clear: (id: string) => void;
|
|
85
109
|
/**
|
|
@@ -91,20 +115,12 @@ export declare class FileUploads {
|
|
|
91
115
|
/** Release every preview. */
|
|
92
116
|
destroy: () => void;
|
|
93
117
|
}
|
|
94
|
-
/** The view's uploads, when
|
|
118
|
+
/** The view's uploads, when a media plugin with an `upload` is listed. */
|
|
95
119
|
export declare const fileUploads: (edytor: Edytor | undefined) => FileUploads | undefined;
|
|
96
|
-
export type UploadsOptions = {
|
|
97
|
-
/**
|
|
98
|
-
* Upload a file and answer its URL: an empty image or media block's
|
|
99
|
-
* Upload, and the pasted and dropped files a kind takes, each placed as a
|
|
100
|
-
* block of that kind at once (one undo step) and filled with the URL this
|
|
101
|
-
* answers (the block shows the progress it reports).
|
|
102
|
-
*/
|
|
103
|
-
upload: Uploader;
|
|
104
|
-
};
|
|
105
120
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
121
|
+
* The hooks of a media plugin given an `upload` (its kind's record carries
|
|
122
|
+
* it, `KindUpload.send`): the first such plugin listed in a view claims the
|
|
123
|
+
* view's one `FileUploads` and runs its paste hook (a drop replays it) and
|
|
124
|
+
* teardown; the others add none.
|
|
109
125
|
*/
|
|
110
|
-
export declare const
|
|
126
|
+
export declare const uploadHooks: (edytor: Edytor | undefined, upload: Uploader | undefined) => PluginOperations & PluginDefinitions;
|
|
@@ -2,11 +2,20 @@ import { SvelteMap } from 'svelte/reactivity';
|
|
|
2
2
|
import { pasteFlow } from '../clipboard/insertClipboardFragment.js';
|
|
3
3
|
import { jsonBlockToSpec } from '../utils/json.js';
|
|
4
4
|
import { id as mint } from '../utils.js';
|
|
5
|
+
import { placeNew } from '../session/moves.js';
|
|
6
|
+
import { claimUploads, viewUploads } from '../session/uploads.js';
|
|
7
|
+
import { FileDrag } from './fileDrop.js';
|
|
8
|
+
/** An upload's object URL, while it uploads, released. */
|
|
9
|
+
const revoke = (upload) => {
|
|
10
|
+
if (upload?.status === 'uploading' && upload.preview)
|
|
11
|
+
URL.revokeObjectURL(upload.preview);
|
|
12
|
+
};
|
|
5
13
|
/**
|
|
6
|
-
* The view's uploads (`media.files`), one per view, the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* takes files by its record's `upload
|
|
14
|
+
* The view's uploads (`media.files`), one per view, claimed by the first
|
|
15
|
+
* media plugin listed with an `upload`: the uploads of pasted, dropped and
|
|
16
|
+
* picked files (an empty block's Upload), session state, never in the
|
|
17
|
+
* document. A kind takes files by its record's `upload`, and sends them
|
|
18
|
+
* with its `send` (its plugin's uploader); a paste or a drop (a drop replays
|
|
10
19
|
* the paste hooks) of files the kinds take places one empty block per
|
|
11
20
|
* file, each of the kind that takes it (`fallback` last), in the
|
|
12
21
|
* clipboard's order, as one paste (one command, one undo step), and each
|
|
@@ -22,13 +31,10 @@ import { id as mint } from '../utils.js';
|
|
|
22
31
|
*/
|
|
23
32
|
export class FileUploads {
|
|
24
33
|
edytor;
|
|
25
|
-
upload;
|
|
26
34
|
#uploads = new SvelteMap();
|
|
27
|
-
constructor(edytor
|
|
28
|
-
/** The view's uploader. */
|
|
29
|
-
upload) {
|
|
35
|
+
constructor(edytor) {
|
|
30
36
|
this.edytor = edytor;
|
|
31
|
-
this.
|
|
37
|
+
this.drop = new FileDrag(edytor, this);
|
|
32
38
|
}
|
|
33
39
|
/** The kinds that take files (their records' `upload`), in registration order. */
|
|
34
40
|
get #kinds() {
|
|
@@ -37,6 +43,14 @@ export class FileUploads {
|
|
|
37
43
|
/** The kind that takes `file`, if any. */
|
|
38
44
|
#kindOf = (file) => this.#kinds.find((kind) => !kind.fallback && kind.accepts(file)) ??
|
|
39
45
|
this.#kinds.find((kind) => kind.fallback && kind.accepts(file));
|
|
46
|
+
/** The kind that takes `file`, if any. */
|
|
47
|
+
kindOf = (file) => this.#kindOf(file)?.type;
|
|
48
|
+
/** The types of the kinds that take files. */
|
|
49
|
+
get types() {
|
|
50
|
+
return this.#kinds.map((kind) => kind.type);
|
|
51
|
+
}
|
|
52
|
+
/** Files dragged from outside: the drop indicator and the placement (`fileDrop.ts`). */
|
|
53
|
+
drop;
|
|
40
54
|
/** The upload of block `id` in this view (reactive), if any. */
|
|
41
55
|
of = (id) => this.#uploads.get(id);
|
|
42
56
|
/**
|
|
@@ -44,15 +58,19 @@ export class FileUploads {
|
|
|
44
58
|
* shows text (an office app puts a picture of copied text on the
|
|
45
59
|
* clipboard).
|
|
46
60
|
*/
|
|
47
|
-
paste = ({ e, prevent }) => {
|
|
61
|
+
paste = ({ e, prevent, at }) => {
|
|
48
62
|
if (this.edytor.readonly)
|
|
49
63
|
return;
|
|
50
64
|
const files = Array.from(e.clipboardData?.files ?? []).filter((file) => this.#kindOf(file));
|
|
51
65
|
if (files.length && !showsText(e.clipboardData?.getData('text/html')))
|
|
52
|
-
prevent(() => this.insert(files));
|
|
66
|
+
prevent(() => this.insert(files, at));
|
|
53
67
|
};
|
|
54
|
-
/**
|
|
55
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Place a block per file at the selection (one paste), or at a drop's
|
|
70
|
+
* placement (`at`: beside, before, after or inside a block, one command),
|
|
71
|
+
* then upload each.
|
|
72
|
+
*/
|
|
73
|
+
insert = (files, at) => {
|
|
56
74
|
const { edytor } = this;
|
|
57
75
|
const placed = files.flatMap((file) => {
|
|
58
76
|
const kind = this.#kindOf(file);
|
|
@@ -61,16 +79,38 @@ export class FileUploads {
|
|
|
61
79
|
if (!placed.length)
|
|
62
80
|
return;
|
|
63
81
|
const lines = placed.map(({ kind, id }) => jsonBlockToSpec({ id, type: kind.type, data: {}, content: [] }));
|
|
64
|
-
|
|
82
|
+
const target = at && edytor.idToBlock.get(at.block);
|
|
83
|
+
if (at && target)
|
|
84
|
+
edytor.dispatcher.run('insertFromDrop', () => placeNew(edytor, placed.map(({ kind, id }) => ({ id, type: kind.type })), target, at.position));
|
|
85
|
+
else
|
|
86
|
+
edytor.dispatcher.run('insertFromPaste', () => pasteFlow(edytor, { lines }));
|
|
65
87
|
for (const { file, kind, id } of placed) {
|
|
66
88
|
// Refused, or placed as no such block (a code line takes plain lines, `flow.lines`).
|
|
67
89
|
if (!edytor.facade.isVisibleBlock(id) || edytor.facade.blockTypeOf(id) !== kind.type)
|
|
68
90
|
continue;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
void this.#send(id, kind, file);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Upload `file`, picked on the empty block `id` (its Upload), with its
|
|
96
|
+
* kind's uploader, the block showing it meanwhile, and hand the answer to
|
|
97
|
+
* `land` before the preview goes (so the block never shows nothing
|
|
98
|
+
* between them): `false` when it failed (the block then shows no
|
|
99
|
+
* upload; the caller says why).
|
|
100
|
+
*/
|
|
101
|
+
pick = async (id, file, land) => {
|
|
102
|
+
const kind = this.edytor.idToBlock.get(id)?.definition.upload;
|
|
103
|
+
if (!kind)
|
|
104
|
+
return false;
|
|
105
|
+
try {
|
|
106
|
+
land(await this.#run(id, kind, file));
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
this.#settle(id);
|
|
74
114
|
}
|
|
75
115
|
};
|
|
76
116
|
/** Forget the block's upload (a source was embedded: its error, or held data, no longer applies). */
|
|
@@ -99,12 +139,24 @@ export class FileUploads {
|
|
|
99
139
|
};
|
|
100
140
|
/** Release every preview. */
|
|
101
141
|
destroy = () => {
|
|
102
|
-
|
|
103
|
-
if (upload.status === 'uploading' && upload.preview)
|
|
104
|
-
URL.revokeObjectURL(upload.preview);
|
|
142
|
+
this.#uploads.forEach(revoke);
|
|
105
143
|
this.#uploads.clear();
|
|
106
144
|
};
|
|
107
|
-
|
|
145
|
+
/**
|
|
146
|
+
* Send `file` with the kind's uploader, block `id` showing it meanwhile
|
|
147
|
+
* (`uploading`: its object URL, name, size and progress): the answer.
|
|
148
|
+
* The object URL stays until `#settle` (after the answer is stored, so
|
|
149
|
+
* the block never shows nothing between them).
|
|
150
|
+
*/
|
|
151
|
+
#run = (id, kind, file) => {
|
|
152
|
+
const preview = typeof URL.createObjectURL === 'function' ? URL.createObjectURL(file) : null;
|
|
153
|
+
this.#uploads.set(id, {
|
|
154
|
+
status: 'uploading',
|
|
155
|
+
preview,
|
|
156
|
+
name: file.name,
|
|
157
|
+
size: file.size,
|
|
158
|
+
progress: null
|
|
159
|
+
});
|
|
108
160
|
const report = {
|
|
109
161
|
progress: (fraction) => {
|
|
110
162
|
const upload = this.#uploads.get(id);
|
|
@@ -113,28 +165,40 @@ export class FileUploads {
|
|
|
113
165
|
this.#uploads.set(id, { ...upload, progress: Math.min(1, Math.max(0, fraction)) });
|
|
114
166
|
}
|
|
115
167
|
};
|
|
168
|
+
return kind.send(file, report);
|
|
169
|
+
};
|
|
170
|
+
/** The upload of `id` ended: revoke its object URL and forget it, if it still runs. */
|
|
171
|
+
#settle = (id) => {
|
|
172
|
+
const upload = this.#uploads.get(id);
|
|
173
|
+
if (upload?.status !== 'uploading')
|
|
174
|
+
return;
|
|
175
|
+
revoke(upload);
|
|
176
|
+
this.#uploads.delete(id);
|
|
177
|
+
};
|
|
178
|
+
#send = async (id, kind, file) => {
|
|
116
179
|
let data;
|
|
117
180
|
try {
|
|
118
|
-
data = kind.data(file, await this
|
|
181
|
+
data = kind.data(file, await this.#run(id, kind, file));
|
|
119
182
|
}
|
|
120
183
|
catch {
|
|
121
184
|
data = null;
|
|
122
185
|
}
|
|
123
186
|
const { edytor } = this;
|
|
124
|
-
if (preview)
|
|
125
|
-
URL.revokeObjectURL(preview);
|
|
126
187
|
if (edytor.destroyed)
|
|
127
188
|
return;
|
|
128
189
|
const block = edytor.idToBlock.get(id);
|
|
190
|
+
const upload = this.#uploads.get(id);
|
|
129
191
|
// Given a source meanwhile (a peer, the link field): nothing to fill.
|
|
130
192
|
if (block?.isInTree && kind.filled(block.data))
|
|
131
|
-
|
|
132
|
-
if (!data)
|
|
133
|
-
|
|
193
|
+
this.#uploads.delete(id);
|
|
194
|
+
else if (!data)
|
|
195
|
+
this.#uploads.set(id, { status: 'failed' });
|
|
134
196
|
// Not shown (undone, deleted): held until it shows again (`fill`).
|
|
135
|
-
if (!block?.isInTree)
|
|
136
|
-
|
|
137
|
-
|
|
197
|
+
else if (!block?.isInTree)
|
|
198
|
+
this.#uploads.set(id, { status: 'landed', data });
|
|
199
|
+
else
|
|
200
|
+
this.#write(id, data);
|
|
201
|
+
revoke(upload);
|
|
138
202
|
};
|
|
139
203
|
/** The data's write, outside the history; refused (readonly), it is held for the next show. */
|
|
140
204
|
#write = (id, data) => {
|
|
@@ -150,18 +214,30 @@ export class FileUploads {
|
|
|
150
214
|
const showsText = (html) => !!html &&
|
|
151
215
|
typeof DOMParser !== 'undefined' &&
|
|
152
216
|
!!new DOMParser().parseFromString(html, 'text/html').body.textContent?.trim();
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
217
|
+
/** The view's uploads, when a media plugin with an `upload` is listed. */
|
|
218
|
+
export const fileUploads = (edytor) => {
|
|
219
|
+
const uploads = viewUploads(edytor);
|
|
220
|
+
return uploads instanceof FileUploads ? uploads : undefined;
|
|
221
|
+
};
|
|
156
222
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
223
|
+
* The hooks of a media plugin given an `upload` (its kind's record carries
|
|
224
|
+
* it, `KindUpload.send`): the first such plugin listed in a view claims the
|
|
225
|
+
* view's one `FileUploads` and runs its paste hook (a drop replays it) and
|
|
226
|
+
* teardown; the others add none.
|
|
160
227
|
*/
|
|
161
|
-
export const
|
|
162
|
-
if (!edytor ||
|
|
228
|
+
export const uploadHooks = (edytor, upload) => {
|
|
229
|
+
if (!edytor || !upload || viewUploads(edytor))
|
|
163
230
|
return {};
|
|
164
|
-
const uploads = new FileUploads(edytor
|
|
165
|
-
|
|
166
|
-
return {
|
|
231
|
+
const uploads = new FileUploads(edytor);
|
|
232
|
+
claimUploads(edytor, uploads);
|
|
233
|
+
return {
|
|
234
|
+
onPaste: uploads.paste,
|
|
235
|
+
onEdytorAttached: ({ node }) => {
|
|
236
|
+
const detach = uploads.drop.attach(node);
|
|
237
|
+
return () => {
|
|
238
|
+
detach();
|
|
239
|
+
uploads.destroy();
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
};
|
|
167
243
|
};
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { Edytor } from './edytor.svelte.js';
|
|
3
3
|
import type { Block } from './block/block.svelte.js';
|
|
4
|
+
import type { BlockMovePosition } from './session/moves.js';
|
|
4
5
|
import type { JSONBlock, JSONDoc, JSONInlineBlock, JSONText } from './utils/json.js';
|
|
5
6
|
import type { Text } from './text/text.svelte.js';
|
|
6
7
|
import type { TextTransform } from './surface/cells.js';
|
|
@@ -42,7 +43,12 @@ export type { Prevent };
|
|
|
42
43
|
export type BlockView<D = Record<string, any>> = {
|
|
43
44
|
readonly id: string;
|
|
44
45
|
readonly type: string;
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
* The block's data as plain state (`block.data`): reactive at any depth,
|
|
48
|
+
* and writable, each write a dispatched `patchData`. In a suggestion's
|
|
49
|
+
* preview it is a copy: a write there changes nothing.
|
|
50
|
+
*/
|
|
51
|
+
readonly data: D;
|
|
46
52
|
readonly selected: boolean;
|
|
47
53
|
readonly focused: boolean;
|
|
48
54
|
/**
|
|
@@ -241,7 +247,10 @@ export type TextRuleContext = {
|
|
|
241
247
|
};
|
|
242
248
|
/** What an input rule's `replace` receives besides the match. */
|
|
243
249
|
export type InputRuleContext = TextRuleContext & {
|
|
244
|
-
/**
|
|
250
|
+
/**
|
|
251
|
+
* The typed text that completed the match: the end of the match, already
|
|
252
|
+
* in the document as its own undo step (`to` is after it).
|
|
253
|
+
*/
|
|
245
254
|
typed: string;
|
|
246
255
|
/**
|
|
247
256
|
* Remove the matched text (`from` to `to`) in the same plan as the first
|
|
@@ -267,13 +276,14 @@ export type InputRule = {
|
|
|
267
276
|
*/
|
|
268
277
|
find: RegExp;
|
|
269
278
|
/**
|
|
270
|
-
* The replacement
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
279
|
+
* The replacement, run once the typed text is in the document (its own
|
|
280
|
+
* undo step, so the first undo gives the typed text back). Return a
|
|
281
|
+
* string to replace the whole match with it (one command and one undo
|
|
282
|
+
* step with the matched text's removal; the caret after it), or write it
|
|
283
|
+
* yourself and return `true`, usually through `ctx.remove(() => …)`.
|
|
284
|
+
* Return `false` or `null`, writing nothing, to decline: the next rule is
|
|
285
|
+
* asked, and the typed text stays as typed, as it does when the
|
|
286
|
+
* replacement is refused (by the document, readonly or a plugin's veto).
|
|
277
287
|
*/
|
|
278
288
|
replace: (match: RegExpExecArray, ctx: InputRuleContext) => string | boolean | null | void;
|
|
279
289
|
};
|
|
@@ -292,6 +302,11 @@ export type TriggerContext = TextRuleContext & {
|
|
|
292
302
|
export type Trigger<T = any> = {
|
|
293
303
|
/** The text that opens the menu (one or two characters: `@`, `[[`). */
|
|
294
304
|
char: string;
|
|
305
|
+
/**
|
|
306
|
+
* The menu's name, read by its markup as `controller.trigger.name` (the
|
|
307
|
+
* mention plugin's `kind`: one menu snippet can serve several triggers).
|
|
308
|
+
*/
|
|
309
|
+
name?: string;
|
|
295
310
|
/** The rows for `query`, at once or as a promise (a search); the newest query wins. */
|
|
296
311
|
items: (query: string, ctx: TriggerContext) => readonly T[] | Promise<readonly T[]>;
|
|
297
312
|
/**
|
|
@@ -397,10 +412,18 @@ export type PluginOperations = {
|
|
|
397
412
|
prevent: Prevent;
|
|
398
413
|
e: ClipboardEvent;
|
|
399
414
|
}) => void;
|
|
400
|
-
/**
|
|
415
|
+
/**
|
|
416
|
+
* Called when a paste event is detected, and for a drop (its data as the
|
|
417
|
+
* clipboard's). `at`: where files dropped from outside land, the drop
|
|
418
|
+
* indicator's placement (beside, before, after or inside `block`).
|
|
419
|
+
*/
|
|
401
420
|
onPaste?: (payload: {
|
|
402
421
|
prevent: Prevent;
|
|
403
422
|
e: ClipboardEvent;
|
|
423
|
+
at?: {
|
|
424
|
+
block: string;
|
|
425
|
+
position: BlockMovePosition;
|
|
426
|
+
};
|
|
404
427
|
}) => void;
|
|
405
428
|
};
|
|
406
429
|
/**
|
|
@@ -493,9 +516,11 @@ export type BlockDefinition = {
|
|
|
493
516
|
*/
|
|
494
517
|
rendersContent?: boolean;
|
|
495
518
|
/**
|
|
496
|
-
* The files the kind takes (the image's `image/*`, the file block's any)
|
|
497
|
-
*
|
|
498
|
-
*
|
|
519
|
+
* The files the kind takes (the image's `image/*`, the file block's any)
|
|
520
|
+
* and who sends them (`send`: the media plugins set it from their
|
|
521
|
+
* `upload` option): an empty block's Upload sends one, and a pasted or
|
|
522
|
+
* dropped file becomes a block of this kind (`media.files`). A kind
|
|
523
|
+
* without it takes no files.
|
|
499
524
|
*/
|
|
500
525
|
upload?: KindUpload;
|
|
501
526
|
/**
|
|
@@ -556,6 +581,15 @@ export type BlockDefinition = {
|
|
|
556
581
|
html?: string | ((block: JSONBlock, content: string, children: string) => string);
|
|
557
582
|
/** Clipboard plain-text form; default content then children, one per line. */
|
|
558
583
|
plain?: (block: JSONBlock, content: string, children: string) => string;
|
|
584
|
+
/**
|
|
585
|
+
* Markdown form (`toMarkdown`): `content` is the block's text as inline
|
|
586
|
+
* markdown (marks by their own `markdown` forms), `children` its children's
|
|
587
|
+
* markdown, blocks apart by a blank line (indent them yourself:
|
|
588
|
+
* {@link MarkdownContext.indent}). Default: the bundled kind's of that
|
|
589
|
+
* name (`edytor/export` holds them; the bundled records carry none), else
|
|
590
|
+
* the `plain` form when the kind has one, else the content, then the children.
|
|
591
|
+
*/
|
|
592
|
+
markdown?: (block: JSONBlock, content: string, children: string, context: MarkdownContext) => string;
|
|
559
593
|
/**
|
|
560
594
|
* HTML import: the block's data when `element` is this kind, else
|
|
561
595
|
* `undefined`. Checked before the tag tables (a preset's export form or
|
|
@@ -609,7 +643,8 @@ export type BlockDefinition = {
|
|
|
609
643
|
export type InlineBlockView<D = Record<string, any>> = {
|
|
610
644
|
readonly id: string;
|
|
611
645
|
readonly type: string;
|
|
612
|
-
|
|
646
|
+
/** The atom's data as plain state (`atom.data`), writable; a suggested atom's is a copy. */
|
|
647
|
+
readonly data: D;
|
|
613
648
|
readonly selected: boolean;
|
|
614
649
|
/** The atom's id-only handle; a suggested atom has none. */
|
|
615
650
|
readonly handle: InlineBlock | undefined;
|
|
@@ -626,6 +661,8 @@ export type InlineBlockDefinition = {
|
|
|
626
661
|
* an empty `<span data-edytor-inline-block>`.
|
|
627
662
|
*/
|
|
628
663
|
html?: (data: JSONInlineBlock['data']) => string;
|
|
664
|
+
/** Markdown form of the atom (`toMarkdown`, escaped by you); default its `plain` form. */
|
|
665
|
+
markdown?: (data: JSONInlineBlock['data']) => string;
|
|
629
666
|
/**
|
|
630
667
|
* HTML import: the atom's data when `element`, met in a line's inline
|
|
631
668
|
* content, is this atom, else `undefined`. Its contents are then not read
|
|
@@ -652,6 +689,12 @@ export type MarkDefinition = {
|
|
|
652
689
|
* matches its bare `tag` (value `true`).
|
|
653
690
|
*/
|
|
654
691
|
parse?: (element: HTMLElement) => SerializableContent | undefined;
|
|
692
|
+
/**
|
|
693
|
+
* Markdown form (`toMarkdown`): the delimiters around the marked text
|
|
694
|
+
* (`['**', '**']`), or a function of the value giving them (a link's
|
|
695
|
+
* `['[', '](href)']`). Without one the text is exported unmarked.
|
|
696
|
+
*/
|
|
697
|
+
markdown?: readonly [string, string] | ((value: any) => readonly [string, string] | undefined);
|
|
655
698
|
void?: boolean;
|
|
656
699
|
/** Whether typing at the mark's edges extends it (`marksForInsertion`); default `inclusive`. */
|
|
657
700
|
edge?: MarkEdge;
|
|
@@ -729,3 +772,12 @@ export type EditorCommand = {
|
|
|
729
772
|
searchOnly?: boolean;
|
|
730
773
|
};
|
|
731
774
|
export type InitializedPlugin = ReturnType<Plugin>;
|
|
775
|
+
/** What a kind's `markdown` form is given beside its block, content and children. */
|
|
776
|
+
export type MarkdownContext = {
|
|
777
|
+
/** The block's position in its run of same-kind siblings, from 1 (a numbered item's number). */
|
|
778
|
+
index: number;
|
|
779
|
+
/** Every line of `text` prefixed with `prefix` (`' '` under a list item, `'> '` in a quote). */
|
|
780
|
+
indent: (text: string, prefix: string) => string;
|
|
781
|
+
/** Rich text as inline markdown, by the marks' and atoms' forms (a table cell's). */
|
|
782
|
+
inline: (content: JSONBlock['content']) => string;
|
|
783
|
+
};
|
package/dist/plugins.js
CHANGED
|
@@ -1,25 +1 @@
|
|
|
1
1
|
export {};
|
|
2
|
-
// Extension-surface note (replaces stale TODOs): the gaps this used to
|
|
3
|
-
// list are all covered by the contract above —
|
|
4
|
-
// * `onBeforeInput` is a PluginOperations hook (line ~111).
|
|
5
|
-
// * Block focus/selection lifecycle is `onFocus`/`onBlur`/`onSelect`/
|
|
6
|
-
// `onDeselect` on BlockDefinition.
|
|
7
|
-
// * Arrow-key and select-all behavior are hotkeys, not dedicated
|
|
8
|
-
// events: `mod+a` select-all lives in hotkeys.ts and arrow handling
|
|
9
|
-
// ships in the arrowMove plugin — plugins can override either via
|
|
10
|
-
// the `hotkeys` definition map (prevention is first-win).
|
|
11
|
-
// A dedicated raw-key event hook does not exist; add one only if a real
|
|
12
|
-
// consumer needs more than hotkey overrides.
|
|
13
|
-
// ISLAND BLOCKS
|
|
14
|
-
// Island blocks are blocks that are independent of the document.
|
|
15
|
-
// They are editable
|
|
16
|
-
// They can be nested inside other blocks
|
|
17
|
-
// But their children can not be nested or merged with other blocks
|
|
18
|
-
// Their structure will remain as if or will not.
|
|
19
|
-
// In case of a merge operation, the island block will be merged with the parent block and all its children will ne unnest and set to the default block type without being added as children of the merge destination block. The same wil happen to the island's content.
|
|
20
|
-
// VOID BLOCKS
|
|
21
|
-
// Void blocks are blocks that are not editable
|
|
22
|
-
// They can be nested inside other blocks
|
|
23
|
-
// They can not accept children nor be merged with other blocks
|
|
24
|
-
// If the content of the void block is rendered, it will still be editable. This to allow for rendering caption of the void block. I
|
|
25
|
-
// In void blocks the content is treated as an island
|