edytor 2.0.0 → 4.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 +19 -4
- package/dist/block/block.svelte.js +2 -4
- 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/DocumentRoom.d.ts +13 -9
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/room/admission.js +3 -3
- package/dist/cloudflare/room/context.d.ts +28 -27
- package/dist/cloudflare/room/context.js +2 -2
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +44 -19
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/cloudflare/routeDocumentSocket.d.ts +17 -0
- package/dist/cloudflare/routeDocumentSocket.js +53 -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/Edytor.svelte +2 -10
- package/dist/components/Edytor.svelte.d.ts +1 -3
- package/dist/crdt/admission.d.ts +5 -10
- package/dist/crdt/admission.js +5 -11
- package/dist/crdt/attribution/attribution.d.ts +31 -12
- package/dist/crdt/attribution/attribution.js +56 -18
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +7 -6
- package/dist/crdt/doc/funnel.js +39 -33
- package/dist/crdt/doc/gate.d.ts +3 -20
- package/dist/crdt/doc/gate.js +2 -41
- package/dist/crdt/doc/history.d.ts +25 -9
- package/dist/crdt/doc/history.js +152 -42
- package/dist/crdt/doc/layout.js +11 -22
- package/dist/crdt/doc/meta.js +12 -17
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.js +2 -2
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +52 -122
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +21 -19
- package/dist/crdt/document.js +26 -23
- package/dist/crdt/edytor-doc.d.ts +66 -68
- package/dist/crdt/edytor-doc.js +21 -20
- package/dist/crdt/engine-api.d.ts +33 -1
- package/dist/crdt/engine.d.ts +0 -1
- package/dist/crdt/engine.js +1 -4
- package/dist/crdt/flow.d.ts +1 -1
- package/dist/crdt/flow.js +25 -27
- package/dist/crdt/index.d.ts +29 -28
- package/dist/crdt/index.js +3 -3
- package/dist/crdt/placement/display.d.ts +5 -30
- package/dist/crdt/placement/display.js +39 -144
- package/dist/crdt/placement/keys.d.ts +58 -0
- package/dist/crdt/placement/keys.js +90 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +187 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -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 +26 -11
- package/dist/crdt/providers/room.js +77 -27
- package/dist/crdt/providers/websocket.d.ts +1 -1
- package/dist/crdt/providers/websocket.js +33 -32
- package/dist/crdt/purge.d.ts +17 -8
- package/dist/crdt/purge.js +236 -126
- package/dist/crdt/restore.d.ts +2 -2
- package/dist/crdt/restore.js +18 -34
- package/dist/crdt/schema.d.ts +22 -21
- package/dist/crdt/schema.js +29 -23
- package/dist/crdt/semantics.js +3 -1
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +15 -15
- package/dist/crdt/structs.js +8 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +12 -0
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.js +5 -8
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +404 -138
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +12 -3
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +77 -62
- package/dist/crdt/text/model.js +84 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +17 -12
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
- package/dist/dnd/autoScroll.d.ts +15 -7
- package/dist/dnd/autoScroll.js +10 -15
- package/dist/dnd/dropIndicator.js +2 -5
- package/dist/dnd/external.d.ts +6 -0
- package/dist/dnd/external.js +17 -0
- package/dist/dnd/geometry.d.ts +2 -0
- package/dist/dnd/geometry.js +3 -4
- package/dist/dnd/placement.d.ts +1 -1
- 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 +2 -5
- package/dist/edytor.svelte.js +21 -32
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/onBeforeInput.js +1 -1
- package/dist/events/onDrop.d.ts +0 -6
- package/dist/events/onDrop.js +10 -23
- package/dist/events/onInput.d.ts +0 -12
- package/dist/events/onInput.js +1 -67
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- 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 +3 -3
- package/dist/index.js +1 -1
- package/dist/kinds.js +1 -1
- package/dist/plugins/anchored.svelte.d.ts +21 -25
- package/dist/plugins/anchored.svelte.js +23 -39
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +24 -32
- 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/CodePlugin.svelte +12 -9
- 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/columns/ColumnsPlugin.svelte +7 -20
- package/dist/plugins/columns/resize.svelte.d.ts +2 -3
- package/dist/plugins/columns/resize.svelte.js +2 -5
- 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 +12 -22
- 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 +38 -23
- package/dist/plugins/fileDrop.js +82 -76
- 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 +15 -52
- package/dist/plugins/image/controls.svelte.js +16 -2
- 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 +1 -1
- package/dist/plugins/list.svelte.d.ts +9 -0
- package/dist/plugins/list.svelte.js +18 -0
- package/dist/plugins/marquee/marqueePlugin.js +0 -4
- package/dist/plugins/media/AudioPlugin.svelte +1 -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 +1 -19
- package/dist/plugins/media/MediaEmpty.svelte +2 -55
- package/dist/plugins/media/MediaEmpty.svelte.d.ts +18 -14
- package/dist/plugins/media/SourceBlock.svelte +142 -52
- package/dist/plugins/media/SourceBlock.svelte.d.ts +10 -4
- package/dist/plugins/media/VideoPlugin.svelte +1 -1
- package/dist/plugins/media/empty.svelte.js +2 -7
- package/dist/plugins/media/media.d.ts +2 -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/urlPaste.svelte.d.ts +2 -1
- package/dist/plugins/media/urlPaste.svelte.js +12 -21
- package/dist/plugins/mention/MentionPlugin.svelte +8 -35
- package/dist/plugins/page/PagePlugin.svelte +2 -9
- package/dist/plugins/page/page.js +1 -1
- package/dist/plugins/pageLink/PageLinkPlugin.svelte +6 -13
- package/dist/plugins/richtext/RichTextPlugin.svelte +85 -197
- 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/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 +30 -54
- 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 +5 -2
- package/dist/plugins/uploads.svelte.js +25 -15
- package/dist/plugins.d.ts +26 -0
- 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/bindings.js +1 -1
- package/dist/session/commands.d.ts +7 -11
- package/dist/session/commands.js +13 -19
- package/dist/session/inputRules.js +1 -1
- package/dist/session/navigation.js +1 -1
- package/dist/session/suggestions.svelte.d.ts +0 -11
- package/dist/session/suggestions.svelte.js +15 -3
- package/dist/session/uploads.d.ts +8 -8
- package/dist/surface/lines.js +1 -3
- package/dist/surface/observer.svelte.d.ts +17 -1
- package/dist/surface/observer.svelte.js +74 -19
- package/dist/surface/overlay.d.ts +0 -14
- package/dist/surface/overlay.js +0 -25
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +60 -9
- package/dist/text/text.svelte.js +3 -13
- 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 +19 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
- 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
|
@@ -38,7 +38,9 @@ import { diffText } from '../utils/diffText.js';
|
|
|
38
38
|
import { jsonEquals } from '../utils/json.js';
|
|
39
39
|
import { activeMarks, marksForInsertion } from '../session/editing/text.js';
|
|
40
40
|
import { INTENTS } from '../session/attempt.js';
|
|
41
|
-
import {
|
|
41
|
+
import { runOccurrence } from '../events/onBeforeInput.js';
|
|
42
|
+
import { getDomSelectionSnapshot } from '../selection/domSelection.js';
|
|
43
|
+
import { domOffsetIn } from '../selection/selection.utils.js';
|
|
42
44
|
import { diverges, heal, ownedOf } from './attributes.js';
|
|
43
45
|
import { getTextContentOffsetAtPoint } from '../events/domTextOffset.js';
|
|
44
46
|
import { rendersMark } from './cells.js';
|
|
@@ -67,22 +69,68 @@ const textOf = (cell) => cell.runs
|
|
|
67
69
|
.replace(ZWSP, '');
|
|
68
70
|
/** Svelte's anchors: comments and empty text nodes are never input and never removed. */
|
|
69
71
|
const isAnchor = (node) => node.nodeType === Node.COMMENT_NODE || (node.nodeType === Node.TEXT_NODE && !node.nodeValue);
|
|
70
|
-
/**
|
|
71
|
-
const
|
|
72
|
+
/** The `nth` non-empty text node under `node`. */
|
|
73
|
+
const textLeaf = (node, nth = 0) => {
|
|
72
74
|
const walker = (node.ownerDocument ?? document).createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
|
73
|
-
let count = 0;
|
|
74
75
|
while (walker.nextNode())
|
|
75
|
-
if (walker.currentNode.data)
|
|
76
|
-
count++;
|
|
77
|
-
return count;
|
|
78
|
-
};
|
|
79
|
-
const firstLeaf = (node) => {
|
|
80
|
-
const walker = (node.ownerDocument ?? document).createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
|
81
|
-
while (walker.nextNode())
|
|
82
|
-
if (walker.currentNode.data)
|
|
76
|
+
if (walker.currentNode.data && nth-- === 0)
|
|
83
77
|
return walker.currentNode;
|
|
84
78
|
return null;
|
|
85
79
|
};
|
|
80
|
+
const ZERO_WIDTH_SPACE = '\u200B';
|
|
81
|
+
/**
|
|
82
|
+
* What the browser shows for `text`: its element's text nodes in order (WebKit's
|
|
83
|
+
* converted-space wrappers read as plain spaces), the empty filler and the
|
|
84
|
+
* trailing-newline marker stripped.
|
|
85
|
+
*/
|
|
86
|
+
export const readDomText = (text) => {
|
|
87
|
+
let value = '';
|
|
88
|
+
const node = text.node;
|
|
89
|
+
if (node) {
|
|
90
|
+
const walker = (node.ownerDocument ?? document).createTreeWalker(node, NodeFilter.SHOW_TEXT);
|
|
91
|
+
while (walker.nextNode()) {
|
|
92
|
+
const leaf = walker.currentNode;
|
|
93
|
+
value += leaf.parentElement?.closest('span.Apple-converted-space')
|
|
94
|
+
? leaf.data.replaceAll('\u00A0', ' ')
|
|
95
|
+
: leaf.data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// An empty text's filler shares its node with what the browser typed, on either side.
|
|
99
|
+
if (text.isEmpty)
|
|
100
|
+
return value.replaceAll(ZERO_WIDTH_SPACE, '');
|
|
101
|
+
if (text.endsWithNewline && value.endsWith(ZERO_WIDTH_SPACE)) {
|
|
102
|
+
value = value.slice(0, -ZERO_WIDTH_SPACE.length);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
};
|
|
106
|
+
export const getCollapsedDomTextSelection = (edytor) => {
|
|
107
|
+
const selection = getDomSelectionSnapshot(edytor.node);
|
|
108
|
+
if (!selection?.anchorNode || !selection.isCollapsed) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!edytor.node?.contains(selection.anchorNode)) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const text = edytor.selection.getTextOfNode(selection.anchorNode);
|
|
115
|
+
if (!text?.node) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
// No model rule here: the DOM caret is read BEFORE the reconcile writes
|
|
119
|
+
// the model, so a native insertion legitimately sits past `text.length`
|
|
120
|
+
// (`selectText` clamps the final write).
|
|
121
|
+
return { text, offset: domOffsetIn(text, selection.anchorNode, selection.anchorOffset) };
|
|
122
|
+
};
|
|
123
|
+
/** A native line break (a diff inserting only newlines at `at`) runs as the key's occurrence. */
|
|
124
|
+
export const lineBreak = async (edytor, text, value, at) => {
|
|
125
|
+
if (!text.isInDocument)
|
|
126
|
+
return;
|
|
127
|
+
const inputType = edytor.attempts.confirm()?.inputType === 'insertLineBreak' || value.includes('\n')
|
|
128
|
+
? 'insertLineBreak'
|
|
129
|
+
: 'insertParagraph';
|
|
130
|
+
text.refreshFromModel();
|
|
131
|
+
edytor.selection.selectText(text, at);
|
|
132
|
+
await runOccurrence(edytor, { inputType, cancelable: false });
|
|
133
|
+
};
|
|
86
134
|
const settle = (promises) => Promise.all(promises).then(() => undefined);
|
|
87
135
|
/** The collapsed DOM caret's text offset inside `nodes` (a replacement's), if it is there. */
|
|
88
136
|
const caretIn = (nodes) => {
|
|
@@ -139,7 +187,7 @@ export class SurfaceObserver {
|
|
|
139
187
|
#heals = 0;
|
|
140
188
|
/** The pass wrote the host itself: a render (the projector displays after it). */
|
|
141
189
|
#wrote = false;
|
|
142
|
-
/** Records arrived since the last pass (
|
|
190
|
+
/** Records arrived since the last pass (the projector reads what they did: `rendered`). */
|
|
143
191
|
#fresh = false;
|
|
144
192
|
#window = null;
|
|
145
193
|
constructor(edytor) {
|
|
@@ -167,9 +215,16 @@ export class SurfaceObserver {
|
|
|
167
215
|
clearTimeout(this.#window ?? undefined);
|
|
168
216
|
};
|
|
169
217
|
};
|
|
170
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* Register an element the core rendered (an attachment); returns its
|
|
220
|
+
* release. A text element takes the table's attributes now (its id, its
|
|
221
|
+
* editing host inside a void).
|
|
222
|
+
*/
|
|
171
223
|
register = (node, kind, block, name, owned) => {
|
|
172
|
-
|
|
224
|
+
const entry = { kind, block, name, owned };
|
|
225
|
+
this.#registry.set(node, entry);
|
|
226
|
+
if (kind === 'text')
|
|
227
|
+
heal(node, ownedOf(this.edytor, node, entry));
|
|
173
228
|
if (kind === 'block')
|
|
174
229
|
this.#blocks.set(block, node);
|
|
175
230
|
else if (kind !== 'anchor' && kind !== 'preview') {
|
|
@@ -479,7 +534,7 @@ export class SurfaceObserver {
|
|
|
479
534
|
}
|
|
480
535
|
if (this.#fresh) {
|
|
481
536
|
this.#fresh = false;
|
|
482
|
-
void settle(work).then(edytor.projector.
|
|
537
|
+
void settle(work).then(edytor.projector.rendered);
|
|
483
538
|
}
|
|
484
539
|
return work;
|
|
485
540
|
};
|
|
@@ -579,7 +634,7 @@ export class SurfaceObserver {
|
|
|
579
634
|
const shown = deltas.length ? deltas : [{ text: '\u200B' }];
|
|
580
635
|
for (const [i, delta] of shown.entries()) {
|
|
581
636
|
const node = nodes[i];
|
|
582
|
-
const leaf = node?.nodeType === Node.TEXT_NODE ? node : node &&
|
|
637
|
+
const leaf = node?.nodeType === Node.TEXT_NODE ? node : node && textLeaf(node);
|
|
583
638
|
if (!leaf)
|
|
584
639
|
return false;
|
|
585
640
|
if (leaf.data !== delta.text)
|
|
@@ -614,7 +669,7 @@ export class SurfaceObserver {
|
|
|
614
669
|
const at = replaced ? replaced.caret : caret?.text === text ? caret.offset : undefined;
|
|
615
670
|
const line = diffText(text.stringContent, dom);
|
|
616
671
|
if (line && !line.remove && /^[\r\n]+$/.test(line.insert)) {
|
|
617
|
-
work.push(
|
|
672
|
+
work.push(lineBreak(edytor, text, dom, line.at));
|
|
618
673
|
continue;
|
|
619
674
|
}
|
|
620
675
|
work.push(adopt(edytor, text, dom, at));
|
|
@@ -698,7 +753,7 @@ export class SurfaceObserver {
|
|
|
698
753
|
if (marks.join() !== chain.join())
|
|
699
754
|
return 'mark-chain';
|
|
700
755
|
// The snippet's markup is its own, but the delta renders one text node.
|
|
701
|
-
if (
|
|
756
|
+
if (!textLeaf(node) || textLeaf(node, 1))
|
|
702
757
|
return 'mark-text';
|
|
703
758
|
}
|
|
704
759
|
}
|
|
@@ -50,20 +50,6 @@ export declare const pin: (host: HTMLElement) => CSSStyleDeclaration & {
|
|
|
50
50
|
left: string;
|
|
51
51
|
top: string;
|
|
52
52
|
};
|
|
53
|
-
/**
|
|
54
|
-
* A popup's place near `rect` (client px), 8px inside the viewport: `gap`
|
|
55
|
-
* below it, above it when there is no room below (`above`: above first,
|
|
56
|
-
* below when there is no room above), its left edge at `rect`'s or
|
|
57
|
-
* centered on it (`center`). Measured now from `box` (the host's first
|
|
58
|
-
* element by default; `fallback` before it has a size), written by the
|
|
59
|
-
* answer (the overlay's read-then-write pass).
|
|
60
|
-
*/
|
|
61
|
-
export declare const placeNear: (host: HTMLElement, rect: DOMRect, fallback: readonly [width: number, height: number], { gap, above, center, box }?: {
|
|
62
|
-
gap?: number;
|
|
63
|
-
above?: boolean;
|
|
64
|
-
center?: boolean;
|
|
65
|
-
box?: Element | null;
|
|
66
|
-
}) => (() => void) | void;
|
|
67
53
|
export declare class Overlay {
|
|
68
54
|
#private;
|
|
69
55
|
/** The layer: absolutely positioned at its static place right after the host. */
|
package/dist/surface/overlay.js
CHANGED
|
@@ -68,31 +68,6 @@ export const sameValues = (a, b) => a === b ||
|
|
|
68
68
|
Object.keys(a).every((key) => a[key] === b[key]));
|
|
69
69
|
/** A mounted host whose chrome places itself layer-relative: pinned at the layer's origin. */
|
|
70
70
|
export const pin = (host) => Object.assign(host.style, { position: 'absolute', left: '0px', top: '0px' });
|
|
71
|
-
/**
|
|
72
|
-
* A popup's place near `rect` (client px), 8px inside the viewport: `gap`
|
|
73
|
-
* below it, above it when there is no room below (`above`: above first,
|
|
74
|
-
* below when there is no room above), its left edge at `rect`'s or
|
|
75
|
-
* centered on it (`center`). Measured now from `box` (the host's first
|
|
76
|
-
* element by default; `fallback` before it has a size), written by the
|
|
77
|
-
* answer (the overlay's read-then-write pass).
|
|
78
|
-
*/
|
|
79
|
-
export const placeNear = (host, rect, fallback, { gap = 8, above = false, center = false, box = host.firstElementChild } = {}) => {
|
|
80
|
-
const view = host.ownerDocument.defaultView;
|
|
81
|
-
if (!view)
|
|
82
|
-
return;
|
|
83
|
-
const size = box?.getBoundingClientRect();
|
|
84
|
-
const width = size?.width || fallback[0];
|
|
85
|
-
const height = size?.height || fallback[1];
|
|
86
|
-
const x = center ? rect.left + rect.width / 2 - width / 2 : rect.left;
|
|
87
|
-
const [below, up] = [rect.bottom + gap, rect.top - gap - height];
|
|
88
|
-
const y = above
|
|
89
|
-
? up >= 8
|
|
90
|
-
? up
|
|
91
|
-
: below
|
|
92
|
-
: Math.max(8, below + height < view.innerHeight ? below : up);
|
|
93
|
-
const left = `${Math.max(8, Math.min(x, view.innerWidth - width - 8))}px`;
|
|
94
|
-
return () => Object.assign(host.style, { left, top: `${y}px` });
|
|
95
|
-
};
|
|
96
71
|
/** The band around the viewport (`near`) whose blocks' resizes are watched, in screens above and below. */
|
|
97
72
|
const NEAR_SCREENS = 1;
|
|
98
73
|
export class Overlay {
|
|
@@ -28,11 +28,23 @@ export declare class Projector {
|
|
|
28
28
|
* removal re-rendering the text under a range) runs now. The flag is no
|
|
29
29
|
* reactive source, so no later flush is owed: without this, the range the
|
|
30
30
|
* render moved (Firefox keeps it where the removed element stood) would be
|
|
31
|
-
* adopted as the gesture's when its `selectionchange` lands.
|
|
32
|
-
* calls it too once it processed DOM records: a display that waits gets a
|
|
33
|
-
* pass.
|
|
31
|
+
* adopted as the gesture's when its `selectionchange` lands.
|
|
34
32
|
*/
|
|
35
33
|
inputHandled: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* The observer compared the records of a DOM change (a render, a browser
|
|
36
|
+
* edit): a display that waits on them gets a pass (`inputHandled`), and a
|
|
37
|
+
* DOM selection they moved off the last observation with no gesture since
|
|
38
|
+
* is render drift no event reports (`sel.drift.unreported`). A render no
|
|
39
|
+
* commit asked for (a kind's decoration reading state of its own: a code
|
|
40
|
+
* grammar that lands re-tokenizes its lines) replaced the text node the
|
|
41
|
+
* caret was in; the browser left the caret where that node stood (the
|
|
42
|
+
* line's start) and Chromium and WebKit send no `selectionchange` for it,
|
|
43
|
+
* while one that comes would read as foreign (no flush ran the projector
|
|
44
|
+
* since). The value is displayed again now, in the render's own task:
|
|
45
|
+
* before a key, or its `beforeinput` declaring that caret, can read it.
|
|
46
|
+
*/
|
|
47
|
+
rendered: () => void;
|
|
36
48
|
/**
|
|
37
49
|
* Classify a `selectionchange` by comparing the DOM selection with the
|
|
38
50
|
* last display, the render epoch and the gesture serial:
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
* the render epoch and the gesture serial: echo, render drift (displayed
|
|
35
35
|
* again), composition, foreign write or intent (adopted). Two named, counted,
|
|
36
36
|
* time-bounded browser rules are the only signatures:
|
|
37
|
-
* the Android post-delete snap-back and the IME post-commit jump.
|
|
37
|
+
* the Android post-delete snap-back and the IME post-commit jump. A render
|
|
38
|
+
* that moved the DOM selection with no event to say so is drift too, read
|
|
39
|
+
* from the observer's records (`rendered`, `sel.drift.unreported`).
|
|
38
40
|
*/
|
|
39
41
|
import { untrack } from 'svelte';
|
|
40
42
|
import { clearDomSelection, createDomRange, getActiveElement, getDomSelection, getDomSelectionSnapshot, scrollCaretIntoView } from '../selection/domSelection.js';
|
|
@@ -88,6 +90,8 @@ export class Projector {
|
|
|
88
90
|
#opening = false;
|
|
89
91
|
/** The gesture serial at the last observation (a display or an adopted `selectionchange`). */
|
|
90
92
|
#serial = -1;
|
|
93
|
+
/** Where the DOM selection was at that observation (`#note`; null: no range). */
|
|
94
|
+
#at = null;
|
|
91
95
|
/** The named rules' evidence: the last model-owned delete and the last IME commit. */
|
|
92
96
|
#deleted = null;
|
|
93
97
|
#committed = null;
|
|
@@ -183,14 +187,51 @@ export class Projector {
|
|
|
183
187
|
* removal re-rendering the text under a range) runs now. The flag is no
|
|
184
188
|
* reactive source, so no later flush is owed: without this, the range the
|
|
185
189
|
* render moved (Firefox keeps it where the removed element stood) would be
|
|
186
|
-
* adopted as the gesture's when its `selectionchange` lands.
|
|
187
|
-
* calls it too once it processed DOM records: a display that waits gets a
|
|
188
|
-
* pass.
|
|
190
|
+
* adopted as the gesture's when its `selectionchange` lands.
|
|
189
191
|
*/
|
|
190
192
|
inputHandled = () => {
|
|
191
193
|
if (this.#pending)
|
|
192
194
|
this.edytor.surface.update();
|
|
193
195
|
};
|
|
196
|
+
/**
|
|
197
|
+
* The observer compared the records of a DOM change (a render, a browser
|
|
198
|
+
* edit): a display that waits on them gets a pass (`inputHandled`), and a
|
|
199
|
+
* DOM selection they moved off the last observation with no gesture since
|
|
200
|
+
* is render drift no event reports (`sel.drift.unreported`). A render no
|
|
201
|
+
* commit asked for (a kind's decoration reading state of its own: a code
|
|
202
|
+
* grammar that lands re-tokenizes its lines) replaced the text node the
|
|
203
|
+
* caret was in; the browser left the caret where that node stood (the
|
|
204
|
+
* line's start) and Chromium and WebKit send no `selectionchange` for it,
|
|
205
|
+
* while one that comes would read as foreign (no flush ran the projector
|
|
206
|
+
* since). The value is displayed again now, in the render's own task:
|
|
207
|
+
* before a key, or its `beforeinput` declaring that caret, can read it.
|
|
208
|
+
*/
|
|
209
|
+
rendered = () => {
|
|
210
|
+
if (this.#unreported())
|
|
211
|
+
this.edytor.selection.display();
|
|
212
|
+
else
|
|
213
|
+
this.inputHandled();
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* `rendered`'s drift: a text value, no display waiting, no gesture, drag
|
|
217
|
+
* or composition since the last observation, and the DOM selection in the
|
|
218
|
+
* host no longer where that observation found it.
|
|
219
|
+
*/
|
|
220
|
+
#unreported = () => {
|
|
221
|
+
const { edytor } = this;
|
|
222
|
+
const { selection } = edytor;
|
|
223
|
+
const at = this.#at;
|
|
224
|
+
const node = edytor.node;
|
|
225
|
+
if (!at || !node || selection.value.kind !== 'text' || edytor.intentSerial !== this.#serial)
|
|
226
|
+
return false;
|
|
227
|
+
if (selection.request !== this.#request || selection.dragging || edytor.composition.live)
|
|
228
|
+
return false;
|
|
229
|
+
const dom = getDomSelection(node);
|
|
230
|
+
if (!dom || !isOwnPoint(node, dom.anchorNode))
|
|
231
|
+
return false;
|
|
232
|
+
return !(samePoint(at.anchor, dom.anchorNode, dom.anchorOffset) &&
|
|
233
|
+
samePoint(at.focus, dom.focusNode, dom.focusOffset));
|
|
234
|
+
};
|
|
194
235
|
/**
|
|
195
236
|
* Classify a `selectionchange` by comparing the DOM selection with the
|
|
196
237
|
* last display, the render epoch and the gesture serial:
|
|
@@ -283,9 +324,19 @@ export class Projector {
|
|
|
283
324
|
/** The DOM selection is observed now (adopted, or ignored by the adopter's own rules). */
|
|
284
325
|
#observed = (observation) => {
|
|
285
326
|
this.#seen = this.#flushes;
|
|
286
|
-
this.#
|
|
327
|
+
this.#note();
|
|
287
328
|
return observation;
|
|
288
329
|
};
|
|
330
|
+
/** An observation: its gesture serial, and where the DOM selection is (`#at`). */
|
|
331
|
+
#note = () => {
|
|
332
|
+
const { edytor } = this;
|
|
333
|
+
this.#serial = edytor.intentSerial;
|
|
334
|
+
const dom = edytor.node && getDomSelection(edytor.node);
|
|
335
|
+
this.#at =
|
|
336
|
+
dom?.anchorNode && dom.focusNode
|
|
337
|
+
? { anchor: [dom.anchorNode, dom.anchorOffset], focus: [dom.focusNode, dom.focusOffset] }
|
|
338
|
+
: null;
|
|
339
|
+
};
|
|
289
340
|
/** The DOM selection is our last display unchanged, or a text-bound range that shows the value. */
|
|
290
341
|
#echoes = (dom) => {
|
|
291
342
|
const displayed = this.#displayed;
|
|
@@ -344,7 +395,7 @@ export class Projector {
|
|
|
344
395
|
observe = () => {
|
|
345
396
|
this.#parking = false;
|
|
346
397
|
this.#seen = this.#flushes;
|
|
347
|
-
this.#
|
|
398
|
+
this.#note();
|
|
348
399
|
this.#displayed = null;
|
|
349
400
|
};
|
|
350
401
|
/** Answers whether the pass completed (false: wait for a later flush). */
|
|
@@ -388,7 +439,7 @@ export class Projector {
|
|
|
388
439
|
if (inside)
|
|
389
440
|
clearDomSelection(node);
|
|
390
441
|
this.#displayed = null;
|
|
391
|
-
this.#
|
|
442
|
+
this.#note();
|
|
392
443
|
}
|
|
393
444
|
return true;
|
|
394
445
|
}
|
|
@@ -425,7 +476,7 @@ export class Projector {
|
|
|
425
476
|
selection.scrollOnDisplay = false;
|
|
426
477
|
this.#displayed = { ...points, epoch: selection.epoch };
|
|
427
478
|
this.#seen = this.#flushes;
|
|
428
|
-
this.#
|
|
479
|
+
this.#note();
|
|
429
480
|
selection.observed({
|
|
430
481
|
edge: isCollapsed ? getMarkEdgeSide(startText, start[0], yStart) : undefined
|
|
431
482
|
});
|
|
@@ -467,7 +518,7 @@ export class Projector {
|
|
|
467
518
|
};
|
|
468
519
|
#write = (dom, { anchor, focus }, collapsed) => {
|
|
469
520
|
this.#parking = false;
|
|
470
|
-
if (!collapsed
|
|
521
|
+
if (!collapsed) {
|
|
471
522
|
try {
|
|
472
523
|
dom.setBaseAndExtent(anchor[0], anchor[1], focus[0], focus[1]);
|
|
473
524
|
return;
|
package/dist/text/text.svelte.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { cleanups } from '../utils.js';
|
|
1
2
|
import {} from '../utils/json.js';
|
|
2
3
|
import { segmentJSON } from '../session/handles.js';
|
|
3
4
|
/**
|
|
@@ -101,20 +102,9 @@ export class Text {
|
|
|
101
102
|
attach = (node) => {
|
|
102
103
|
this.node = node;
|
|
103
104
|
this.edytor.nodeToText.set(node, this);
|
|
105
|
+
// Registered, it takes the attribute table's attributes (`surface/attributes.ts`).
|
|
104
106
|
const release = this.edytor.surface.register(node, 'text', this.parent.id);
|
|
105
|
-
|
|
106
|
-
node.setAttribute('data-edytor-text', `true`);
|
|
107
|
-
const pluginDestroy = this.edytor.plugins.flatMap((plugin) => {
|
|
108
|
-
const action = plugin.onTextAttached?.({ node, text: this });
|
|
109
|
-
return typeof action === 'function' ? [action] : [];
|
|
110
|
-
});
|
|
111
|
-
let insideVoid = false;
|
|
112
|
-
for (let block = this.parent; block && !insideVoid; block = block.parent)
|
|
113
|
-
insideVoid = !!block.definition.void;
|
|
114
|
-
if (insideVoid) {
|
|
115
|
-
node.setAttribute('contenteditable', 'true');
|
|
116
|
-
node.style.outline = 'none';
|
|
117
|
-
}
|
|
107
|
+
const pluginDestroy = cleanups(this.edytor.plugins, (plugin) => plugin.onTextAttached?.({ node, text: this }));
|
|
118
108
|
// A display that waited for a mounted destination runs in the next pass.
|
|
119
109
|
this.edytor.projector?.mounted(this);
|
|
120
110
|
return {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sanitizers every reader of untrusted values shares: a link's href and
|
|
3
|
+
* a CSS colour (marks), read the same by render, the clipboard, HTML and
|
|
4
|
+
* markdown import and export. Pure: no DOM, no Svelte, Worker-safe.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* CSS-color sanitizer for `color`/`highlight` marks. The value lands in
|
|
8
|
+
* `style="color: {value}"` — a `;`/`{`/`}` payload escapes the property
|
|
9
|
+
* and injects arbitrary declarations (`url()` exfil, `position:fixed`
|
|
10
|
+
* overlays). Metacharacters, escape/obfuscation sequences, and
|
|
11
|
+
* resource-loading functions are rejected; real `<color>` values
|
|
12
|
+
* (`#hex`, `rgb()/hsl()/oklch()/color-mix()/var()`, named colors) pass.
|
|
13
|
+
*/
|
|
14
|
+
export declare const sanitizeCssColorValue: (value: unknown) => string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Scheme allowlist for link hrefs. `javascript:`/`data:`/`vbscript:`
|
|
17
|
+
* payloads can arrive through native `insertLink`, pasted HTML, or a
|
|
18
|
+
* malicious collaborator — and Svelte renders `href` verbatim. The href is
|
|
19
|
+
* read as the browser's URL parser reads it: it strips C0 controls and
|
|
20
|
+
* spaces at both ends and tab/newline/CR anywhere (`\u0001javascript:` is
|
|
21
|
+
* `javascript:` to a browser), then resolves it against a base; only an
|
|
22
|
+
* allowed scheme, or a relative href that stays relative, passes.
|
|
23
|
+
*/
|
|
24
|
+
export declare const sanitizeLinkHref: (href: unknown) => string | null;
|
|
25
|
+
/**
|
|
26
|
+
* A web page's URL a media block may store or render: the link sanitizer's
|
|
27
|
+
* reading of it ({@link sanitizeLinkHref}: ASCII control characters and spaces stripped,
|
|
28
|
+
* tab/newline anywhere), then only an absolute `http:`/`https:` URL with a
|
|
29
|
+
* host. `null` for anything else: a script or `data:` URL, a `mailto:`, a
|
|
30
|
+
* relative path, a bare domain.
|
|
31
|
+
*/
|
|
32
|
+
export declare const safeWebUrl: (value: unknown) => string | null;
|
|
33
|
+
/**
|
|
34
|
+
* A media file's source (a video, an audio track, an attachment): a
|
|
35
|
+
* {@link safeWebUrl}, or a `blob:` URL an upload answered. Never `data:`:
|
|
36
|
+
* an inline file would be stored in the document and sent in every update.
|
|
37
|
+
*/
|
|
38
|
+
export declare const safeMediaSrc: (value: unknown) => string | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sanitizers every reader of untrusted values shares: a link's href and
|
|
3
|
+
* a CSS colour (marks), read the same by render, the clipboard, HTML and
|
|
4
|
+
* markdown import and export. Pure: no DOM, no Svelte, Worker-safe.
|
|
5
|
+
*/
|
|
6
|
+
const SAFE_LINK_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'tel:']);
|
|
7
|
+
/**
|
|
8
|
+
* CSS-color sanitizer for `color`/`highlight` marks. The value lands in
|
|
9
|
+
* `style="color: {value}"` — a `;`/`{`/`}` payload escapes the property
|
|
10
|
+
* and injects arbitrary declarations (`url()` exfil, `position:fixed`
|
|
11
|
+
* overlays). Metacharacters, escape/obfuscation sequences, and
|
|
12
|
+
* resource-loading functions are rejected; real `<color>` values
|
|
13
|
+
* (`#hex`, `rgb()/hsl()/oklch()/color-mix()/var()`, named colors) pass.
|
|
14
|
+
*/
|
|
15
|
+
export const sanitizeCssColorValue = (value) => {
|
|
16
|
+
if (typeof value !== 'string')
|
|
17
|
+
return null;
|
|
18
|
+
const trimmed = value.trim();
|
|
19
|
+
if (!trimmed || trimmed.length > 128)
|
|
20
|
+
return null;
|
|
21
|
+
if (/[;{}<>\\'"`]/.test(trimmed) || /url\s*\(|expression\s*\(|\/\*|\*\//i.test(trimmed)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return trimmed;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Scheme allowlist for link hrefs. `javascript:`/`data:`/`vbscript:`
|
|
28
|
+
* payloads can arrive through native `insertLink`, pasted HTML, or a
|
|
29
|
+
* malicious collaborator — and Svelte renders `href` verbatim. The href is
|
|
30
|
+
* read as the browser's URL parser reads it: it strips C0 controls and
|
|
31
|
+
* spaces at both ends and tab/newline/CR anywhere (`\u0001javascript:` is
|
|
32
|
+
* `javascript:` to a browser), then resolves it against a base; only an
|
|
33
|
+
* allowed scheme, or a relative href that stays relative, passes.
|
|
34
|
+
*/
|
|
35
|
+
export const sanitizeLinkHref = (href) => {
|
|
36
|
+
const trimmed = hrefOf(href);
|
|
37
|
+
// A relative href resolves to the base's `https:`: it names no scheme of its own.
|
|
38
|
+
return trimmed &&
|
|
39
|
+
SAFE_LINK_PROTOCOLS.has(urlOf(trimmed, 'https://relative.invalid/')?.protocol ?? '')
|
|
40
|
+
? trimmed
|
|
41
|
+
: null;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A href as the browser's URL parser reads it: C0 controls and spaces
|
|
45
|
+
* stripped at both ends, tab/newline/CR anywhere; `''` for a non-string
|
|
46
|
+
* (marks arrive from untrusted sources too: synced peers, paste).
|
|
47
|
+
*/
|
|
48
|
+
const hrefOf = (href) => typeof href === 'string'
|
|
49
|
+
? // eslint-disable-next-line no-control-regex
|
|
50
|
+
href.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/g, '')
|
|
51
|
+
: '';
|
|
52
|
+
/** `href` parsed (against `base`), or `undefined` when it is no URL. */
|
|
53
|
+
const urlOf = (href, base) => {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(href, base);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* A web page's URL a media block may store or render: the link sanitizer's
|
|
63
|
+
* reading of it ({@link sanitizeLinkHref}: ASCII control characters and spaces stripped,
|
|
64
|
+
* tab/newline anywhere), then only an absolute `http:`/`https:` URL with a
|
|
65
|
+
* host. `null` for anything else: a script or `data:` URL, a `mailto:`, a
|
|
66
|
+
* relative path, a bare domain.
|
|
67
|
+
*/
|
|
68
|
+
export const safeWebUrl = (value) => {
|
|
69
|
+
const href = hrefOf(value);
|
|
70
|
+
const url = href ? urlOf(href) : undefined;
|
|
71
|
+
return url && /^https?:$/.test(url.protocol) && url.host ? href : null;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* A media file's source (a video, an audio track, an attachment): a
|
|
75
|
+
* {@link safeWebUrl}, or a `blob:` URL an upload answered. Never `data:`:
|
|
76
|
+
* an inline file would be stored in the document and sent in every update.
|
|
77
|
+
*/
|
|
78
|
+
export const safeMediaSrc = (value) => {
|
|
79
|
+
const blob = typeof value === 'string' ? /^blob:(.*)$/is.exec(value.trim()) : null;
|
|
80
|
+
const origin = blob && safeWebUrl(blob[1]);
|
|
81
|
+
return origin ? `blob:${origin}` : safeWebUrl(value);
|
|
82
|
+
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -15,3 +15,5 @@ export declare class PreventionError extends Error {
|
|
|
15
15
|
export declare const isPrevention: (error: unknown) => error is PreventionError;
|
|
16
16
|
/** A hook's `prevent`: veto the gesture, and optionally run `cb` in its place. */
|
|
17
17
|
export type Prevent = (cb?: () => void) => void;
|
|
18
|
+
/** The cleanups `call` answers for each of `items` (a hook answers a function or nothing). */
|
|
19
|
+
export declare const cleanups: <T>(items: readonly T[], call: (item: T) => unknown) => (() => void)[];
|
package/dist/utils.js
CHANGED
|
@@ -39,3 +39,8 @@ export const vetoable = (call) => {
|
|
|
39
39
|
throw veto;
|
|
40
40
|
return out;
|
|
41
41
|
};
|
|
42
|
+
/** The cleanups `call` answers for each of `items` (a hook answers a function or nothing). */
|
|
43
|
+
export const cleanups = (items, call) => items.flatMap((item) => {
|
|
44
|
+
const action = call(item);
|
|
45
|
+
return typeof action === 'function' ? [action] : [];
|
|
46
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edytor",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/beynar/edytor.git"
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
"bench:browser": "node bench/browser.js",
|
|
51
51
|
"bench:runs": "BENCH_RUNS=1 vitest --config ./vitest.crdt.config.ts --run src/tests/crdt/runs/runs.bench.test.ts",
|
|
52
52
|
"size": "node scripts/bundle-size.mjs --by dir",
|
|
53
|
+
"registry:build": "shadcn-svelte registry build ./registry.json --output site/public/r",
|
|
54
|
+
"check:registry": "node scripts/check-registry.mjs",
|
|
55
|
+
"test:registry-install": "sh tests/registry-install/run.sh",
|
|
53
56
|
"census": "node scripts/census.mjs"
|
|
54
57
|
},
|
|
55
58
|
"exports": {
|
|
@@ -66,6 +69,10 @@
|
|
|
66
69
|
"types": "./dist/crdt/index.d.ts",
|
|
67
70
|
"default": "./dist/crdt/index.js"
|
|
68
71
|
},
|
|
72
|
+
"./export": {
|
|
73
|
+
"types": "./dist/export/index.d.ts",
|
|
74
|
+
"default": "./dist/export/index.js"
|
|
75
|
+
},
|
|
69
76
|
"./protocol": {
|
|
70
77
|
"types": "./dist/crdt/protocol.d.ts",
|
|
71
78
|
"default": "./dist/crdt/protocol.js"
|
|
@@ -99,6 +106,9 @@
|
|
|
99
106
|
"@cloudflare/vitest-plugin": "1.3.0",
|
|
100
107
|
"@cloudflare/workers-types": "^5.20260927.1",
|
|
101
108
|
"@eslint/js": "^10.0.1",
|
|
109
|
+
"@floating-ui/dom": "1.8.0",
|
|
110
|
+
"@internationalized/date": "^3.12.4",
|
|
111
|
+
"@lucide/svelte": "^1.56.0",
|
|
102
112
|
"@playwright/test": "^1.59.1",
|
|
103
113
|
"@sveltejs/adapter-static": "^3.0.10",
|
|
104
114
|
"@sveltejs/kit": "^2.55.0",
|
|
@@ -110,6 +120,8 @@
|
|
|
110
120
|
"@typescript-eslint/parser": "^8.58.0",
|
|
111
121
|
"@vitest/ui": "^4.1.2",
|
|
112
122
|
"@y/protocols": "1.0.6-rc.1",
|
|
123
|
+
"bits-ui": "^2.19.5",
|
|
124
|
+
"cn": "^0.4.0",
|
|
113
125
|
"eslint": "^10.1.0",
|
|
114
126
|
"eslint-config-prettier": "^10.1.8",
|
|
115
127
|
"eslint-plugin-svelte": "^3.17.0",
|
|
@@ -117,12 +129,16 @@
|
|
|
117
129
|
"globals": "^17.6.0",
|
|
118
130
|
"jsdom": "^29.0.1",
|
|
119
131
|
"katex": "0.18.7",
|
|
132
|
+
"linkedom": "^0.18.12",
|
|
120
133
|
"prettier": "^3.8.1",
|
|
121
134
|
"prettier-plugin-svelte": "^3.5.1",
|
|
122
135
|
"publint": "^0.3.18",
|
|
136
|
+
"shadcn-svelte": "^1.7.0",
|
|
123
137
|
"svelte": "5.55.1",
|
|
124
138
|
"svelte-check": "^4.4.6",
|
|
139
|
+
"tailwind-variants": "^3.3.1",
|
|
125
140
|
"tailwindcss": "^4.2.2",
|
|
141
|
+
"tw-animate-css": "^1.4.0",
|
|
126
142
|
"typescript": "^5.9.3",
|
|
127
143
|
"vite": "^8.0.3",
|
|
128
144
|
"vitest": "^4.1.2",
|
|
@@ -147,7 +163,8 @@
|
|
|
147
163
|
}
|
|
148
164
|
},
|
|
149
165
|
"dependencies": {
|
|
150
|
-
"@tanstack/highlight": "^
|
|
166
|
+
"@tanstack/highlight": "^1.1.0",
|
|
167
|
+
"@tanstack/markdown": "1.0.0",
|
|
151
168
|
"esm-env": "^1.2.2",
|
|
152
169
|
"lib0-v14": "npm:lib0@1.0.0-rc.32"
|
|
153
170
|
}
|