edytor 0.0.11 → 0.1.0-next.16
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 +77 -197
- package/dist/block/block.svelte.d.ts +156 -69
- package/dist/block/block.svelte.js +324 -355
- package/dist/block/block.utils.d.ts +191 -24
- package/dist/block/block.utils.js +327 -460
- package/dist/block/contentRange.d.ts +4 -0
- package/dist/block/contentRange.js +20 -0
- package/dist/block/inlineBlock.svelte.d.ts +26 -19
- package/dist/block/inlineBlock.svelte.js +91 -39
- package/dist/clipboard/clipboard.d.ts +4 -0
- package/dist/clipboard/clipboard.js +4 -0
- package/dist/clipboard/createClipboardFragment.d.ts +3 -0
- package/dist/clipboard/createClipboardFragment.js +103 -0
- package/dist/clipboard/fragmentData.d.ts +6 -0
- package/dist/clipboard/fragmentData.js +74 -0
- package/dist/clipboard/htmlFlow.d.ts +22 -0
- package/dist/clipboard/htmlFlow.js +167 -0
- package/dist/clipboard/insertClipboardFragment.d.ts +18 -0
- package/dist/clipboard/insertClipboardFragment.js +48 -0
- package/dist/clipboard/serializeClipboardFragment.d.ts +7 -0
- package/dist/clipboard/serializeClipboardFragment.js +53 -0
- package/dist/clipboard/textBlocks.d.ts +23 -0
- package/dist/clipboard/textBlocks.js +103 -0
- package/dist/clipboard/types.d.ts +20 -0
- package/dist/clipboard/types.js +2 -0
- package/dist/cloudflare/DocumentRoom.d.ts +586 -0
- package/dist/cloudflare/DocumentRoom.js +1706 -0
- package/dist/cloudflare/index.d.ts +8 -0
- package/dist/cloudflare/index.js +8 -0
- package/dist/cloudflare/routeDocumentSocket.d.ts +33 -0
- package/dist/cloudflare/routeDocumentSocket.js +51 -0
- package/dist/collaboration/RemoteSelections.svelte +94 -0
- package/dist/collaboration/RemoteSelections.svelte.d.ts +7 -0
- package/dist/collaboration/awarenessSelection.d.ts +127 -0
- package/dist/collaboration/awarenessSelection.js +199 -0
- package/dist/collaboration/documentSync.d.ts +20 -0
- package/dist/collaboration/documentSync.js +13 -0
- package/dist/collaboration/index.d.ts +3 -0
- package/dist/collaboration/index.js +3 -0
- package/dist/collaboration/providers.d.ts +13 -0
- package/dist/collaboration/providers.js +21 -0
- package/dist/collaboration/remoteSelection.d.ts +21 -0
- package/dist/collaboration/remoteSelection.js +159 -0
- package/dist/components/Block.svelte +223 -15
- package/dist/components/Block.svelte.d.ts +4 -2
- package/dist/components/Content.svelte +128 -32
- package/dist/components/Content.svelte.d.ts +6 -2
- package/dist/components/Edytor.svelte +308 -110
- package/dist/components/Edytor.svelte.d.ts +64 -20
- package/dist/components/InlineBlock.svelte +38 -7
- package/dist/components/InlineBlock.svelte.d.ts +5 -2
- package/dist/components/Mark.svelte +29 -13
- package/dist/components/Mark.svelte.d.ts +4 -4
- package/dist/components/Suggestion.svelte +48 -0
- package/dist/components/Suggestion.svelte.d.ts +7 -0
- package/dist/components/SuggestionRanges.svelte +58 -0
- package/dist/components/SuggestionRanges.svelte.d.ts +7 -0
- package/dist/components/Text.svelte +107 -14
- package/dist/components/Text.svelte.d.ts +8 -1
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +0 -2
- package/dist/crdt/admission.d.ts +141 -0
- package/dist/crdt/admission.js +147 -0
- package/dist/crdt/anchors.d.ts +47 -0
- package/dist/crdt/anchors.js +42 -0
- package/dist/crdt/attribution/attribution.d.ts +169 -0
- package/dist/crdt/attribution/attribution.js +136 -0
- package/dist/crdt/attribution/block.d.ts +200 -0
- package/dist/crdt/attribution/block.js +327 -0
- package/dist/crdt/attribution/index.d.ts +9 -0
- package/dist/crdt/attribution/index.js +9 -0
- package/dist/crdt/data.d.ts +81 -0
- package/dist/crdt/data.js +383 -0
- package/dist/crdt/document.d.ts +590 -0
- package/dist/crdt/document.js +1160 -0
- package/dist/crdt/edytor-doc.d.ts +752 -0
- package/dist/crdt/edytor-doc.js +2382 -0
- package/dist/crdt/engine-api.d.ts +153 -0
- package/dist/crdt/engine.d.ts +37 -0
- package/dist/crdt/engine.js +50 -0
- package/dist/crdt/flow.d.ts +104 -0
- package/dist/crdt/flow.js +229 -0
- package/dist/crdt/index.d.ts +630 -0
- package/dist/crdt/index.js +168 -0
- package/dist/crdt/migration/legacy-schema.d.ts +61 -0
- package/dist/crdt/migration/legacy-schema.js +149 -0
- package/dist/crdt/migration/migrate.d.ts +67 -0
- package/dist/crdt/migration/migrate.js +219 -0
- package/dist/crdt/nodes.d.ts +120 -0
- package/dist/crdt/nodes.js +56 -0
- package/dist/crdt/placement/model.d.ts +383 -0
- package/dist/crdt/placement/model.js +811 -0
- package/dist/crdt/placement/rank.d.ts +57 -0
- package/dist/crdt/placement/rank.js +209 -0
- package/dist/crdt/protocols/auth.d.ts +28 -0
- package/dist/crdt/protocols/auth.js +43 -0
- package/dist/crdt/protocols/awareness.d.ts +87 -0
- package/dist/crdt/protocols/awareness.js +271 -0
- package/dist/crdt/protocols/envelope.d.ts +75 -0
- package/dist/crdt/protocols/envelope.js +89 -0
- package/dist/crdt/protocols/observable.d.ts +19 -0
- package/dist/crdt/protocols/observable.js +26 -0
- package/dist/crdt/protocols/sync.d.ts +82 -0
- package/dist/crdt/protocols/sync.js +288 -0
- package/dist/crdt/providers/container.d.ts +23 -0
- package/dist/crdt/providers/container.js +64 -0
- package/dist/crdt/providers/index.d.ts +282 -0
- package/dist/crdt/providers/index.js +118 -0
- package/dist/crdt/providers/indexeddb.d.ts +196 -0
- package/dist/crdt/providers/indexeddb.js +332 -0
- package/dist/crdt/providers/room.d.ts +193 -0
- package/dist/crdt/providers/room.js +444 -0
- package/dist/crdt/providers/websocket.d.ts +223 -0
- package/dist/crdt/providers/websocket.js +601 -0
- package/dist/crdt/rand.d.ts +22 -0
- package/dist/crdt/rand.js +22 -0
- package/dist/crdt/rangeDelete.d.ts +85 -0
- package/dist/crdt/rangeDelete.js +126 -0
- package/dist/crdt/schema.d.ts +96 -0
- package/dist/crdt/schema.js +79 -0
- package/dist/crdt/semantics.d.ts +81 -0
- package/dist/crdt/semantics.js +57 -0
- package/dist/crdt/structs.d.ts +106 -0
- package/dist/crdt/structs.js +119 -0
- package/dist/crdt/text/deletes.d.ts +23 -0
- package/dist/crdt/text/deletes.js +608 -0
- package/dist/crdt/text/model.d.ts +280 -0
- package/dist/crdt/text/model.js +554 -0
- package/dist/crdt/text/runs.d.ts +212 -0
- package/dist/crdt/text/runs.js +1142 -0
- package/dist/crdt/vendor/yjs/API-NOTES.md +59 -0
- package/dist/crdt/vendor/yjs/LICENSE +23 -0
- package/dist/crdt/vendor/yjs/UPSTREAM.md +653 -0
- package/dist/crdt/vendor/yjs/dts/global.d.ts +52 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +25 -0
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +38 -0
- package/dist/crdt/vendor/yjs/dts/structs/GC.d.ts +25 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +748 -0
- package/dist/crdt/vendor/yjs/dts/structs/Skip.d.ts +30 -0
- package/dist/crdt/vendor/yjs/dts/utils/BlockSet.d.ts +34 -0
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +198 -0
- package/dist/crdt/vendor/yjs/dts/utils/EventHandler.d.ts +17 -0
- package/dist/crdt/vendor/yjs/dts/utils/ID.d.ts +20 -0
- package/dist/crdt/vendor/yjs/dts/utils/RangeCursor.d.ts +198 -0
- package/dist/crdt/vendor/yjs/dts/utils/RelativePosition.d.ts +80 -0
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +61 -0
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +129 -0
- package/dist/crdt/vendor/yjs/dts/utils/UndoManager.d.ts +205 -0
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +170 -0
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +164 -0
- package/dist/crdt/vendor/yjs/dts/utils/YEvent.d.ts +93 -0
- package/dist/crdt/vendor/yjs/dts/utils/content-helper.d.ts +1 -0
- package/dist/crdt/vendor/yjs/dts/utils/encoding-helpers.d.ts +5 -0
- package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +26 -0
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +407 -0
- package/dist/crdt/vendor/yjs/dts/utils/isParentOf.d.ts +1 -0
- package/dist/crdt/vendor/yjs/dts/utils/meta.d.ts +25 -0
- package/dist/crdt/vendor/yjs/dts/utils/renderer-helpers.d.ts +98 -0
- package/dist/crdt/vendor/yjs/dts/utils/schemas.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/utils/transaction-helpers.d.ts +15 -0
- package/dist/crdt/vendor/yjs/dts/utils/updates.d.ts +70 -0
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +655 -0
- package/dist/crdt/vendor/yjs/global.d.ts +52 -0
- package/dist/crdt/vendor/yjs/src/index.js +64 -0
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +64 -0
- package/dist/crdt/vendor/yjs/src/structs/GC.js +75 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +1538 -0
- package/dist/crdt/vendor/yjs/src/structs/Skip.js +72 -0
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +291 -0
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +255 -0
- package/dist/crdt/vendor/yjs/src/utils/EventHandler.js +75 -0
- package/dist/crdt/vendor/yjs/src/utils/ID.js +59 -0
- package/dist/crdt/vendor/yjs/src/utils/RangeCursor.js +407 -0
- package/dist/crdt/vendor/yjs/src/utils/RelativePosition.js +301 -0
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +134 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +458 -0
- package/dist/crdt/vendor/yjs/src/utils/UndoManager.js +554 -0
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +284 -0
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +323 -0
- package/dist/crdt/vendor/yjs/src/utils/YEvent.js +158 -0
- package/dist/crdt/vendor/yjs/src/utils/content-helper.js +0 -0
- package/dist/crdt/vendor/yjs/src/utils/encoding-helpers.js +110 -0
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +662 -0
- package/dist/crdt/vendor/yjs/src/utils/ids.js +1504 -0
- package/dist/crdt/vendor/yjs/src/utils/isParentOf.js +19 -0
- package/dist/crdt/vendor/yjs/src/utils/meta.js +74 -0
- package/dist/crdt/vendor/yjs/src/utils/renderer-helpers.js +186 -0
- package/dist/crdt/vendor/yjs/src/utils/schemas.js +3 -0
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +391 -0
- package/dist/crdt/vendor/yjs/src/utils/updates.js +264 -0
- package/dist/crdt/vendor/yjs/src/ynode.js +2897 -0
- package/dist/dnd/pragmatic.d.ts +22 -0
- package/dist/dnd/pragmatic.js +26 -0
- package/dist/edytor.svelte.d.ts +323 -54
- package/dist/edytor.svelte.js +771 -197
- package/dist/edytor.utils.d.ts +49 -2
- package/dist/edytor.utils.js +65 -76
- package/dist/events/beforeInputCommands.d.ts +21 -0
- package/dist/events/beforeInputCommands.js +328 -0
- package/dist/events/beforeInputDeleteCommands.d.ts +3 -0
- package/dist/events/beforeInputDeleteCommands.js +245 -0
- package/dist/events/dataTransferPayload.d.ts +6 -0
- package/dist/events/dataTransferPayload.js +9 -0
- package/dist/events/domTextOffset.d.ts +12 -0
- package/dist/events/domTextOffset.js +52 -0
- package/dist/events/events.utils.d.ts +3 -0
- package/dist/events/events.utils.js +13 -0
- package/dist/events/nativeInteractiveControl.d.ts +31 -0
- package/dist/events/nativeInteractiveControl.js +98 -0
- package/dist/events/onBeforeInput.d.ts +10 -1
- package/dist/events/onBeforeInput.js +449 -330
- package/dist/events/onCopy.d.ts +7 -0
- package/dist/events/onCopy.js +30 -0
- package/dist/events/onCut.d.ts +8 -0
- package/dist/events/onCut.js +13 -0
- package/dist/events/onDrop.d.ts +16 -0
- package/dist/events/onDrop.js +158 -0
- package/dist/events/onFocus.d.ts +4 -0
- package/dist/events/onFocus.js +166 -0
- package/dist/events/onInput.d.ts +20 -0
- package/dist/events/onInput.js +249 -0
- package/dist/events/onKeyDown.js +191 -11
- package/dist/events/onPaste.d.ts +3 -0
- package/dist/events/onPaste.js +108 -0
- package/dist/events/wordBoundary.d.ts +2 -0
- package/dist/events/wordBoundary.js +58 -0
- package/dist/index.d.ts +38 -1
- package/dist/index.js +36 -1
- package/dist/kinds.d.ts +100 -0
- package/dist/kinds.js +198 -0
- package/dist/plugins/arrowMove/arrowMove.d.ts +1 -1
- package/dist/plugins/arrowMove/arrowMove.js +30 -65
- package/dist/plugins/blockHandles/BlockHandle.svelte +152 -0
- package/dist/plugins/blockHandles/BlockHandle.svelte.d.ts +12 -0
- package/dist/plugins/blockHandles/BlockHandleController.svelte.d.ts +208 -0
- package/dist/plugins/blockHandles/BlockHandleController.svelte.js +823 -0
- package/dist/plugins/blockHandles/BlockHandles.svelte +151 -0
- package/dist/plugins/blockHandles/BlockHandles.svelte.d.ts +20 -0
- package/dist/plugins/blockHandles/autoScroll.d.ts +8 -0
- package/dist/plugins/blockHandles/autoScroll.js +25 -0
- package/dist/plugins/blockHandles/blockHandlesPlugin.d.ts +37 -0
- package/dist/plugins/blockHandles/blockHandlesPlugin.js +100 -0
- package/dist/plugins/blockHandles/dragPreview.d.ts +33 -0
- package/dist/plugins/blockHandles/dragPreview.js +228 -0
- package/dist/plugins/blockMenu/BlockMenu.svelte +301 -0
- package/dist/plugins/blockMenu/BlockMenu.svelte.d.ts +9 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.d.ts +102 -0
- package/dist/plugins/blockMenu/BlockMenuController.svelte.js +245 -0
- package/dist/plugins/blockMenu/blockMenuPlugin.d.ts +11 -0
- package/dist/plugins/blockMenu/blockMenuPlugin.js +89 -0
- package/dist/plugins/code/CodePlugin.svelte +210 -163
- package/dist/plugins/code/CodePlugin.svelte.d.ts +1 -3
- package/dist/plugins/code/code.css +92 -0
- package/dist/plugins/icons.d.ts +2 -0
- package/dist/plugins/icons.js +31 -0
- package/dist/plugins/image/ImageEmpty.svelte +69 -0
- package/dist/plugins/image/ImageEmpty.svelte.d.ts +8 -0
- package/dist/plugins/image/ImagePlugin.svelte +74 -12
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +15 -0
- package/dist/plugins/image/image.d.ts +2 -0
- package/dist/plugins/image/image.js +7 -0
- package/dist/plugins/index.d.ts +14 -1
- package/dist/plugins/index.js +10 -1
- package/dist/plugins/keepInView.d.ts +4 -0
- package/dist/plugins/keepInView.js +6 -0
- package/dist/plugins/markdownShortcuts.d.ts +3 -0
- package/dist/plugins/markdownShortcuts.js +104 -0
- package/dist/plugins/mention/MentionPlugin.svelte +28 -31
- package/dist/plugins/richtext/RichTextPlugin.svelte +411 -200
- package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +8 -7
- package/dist/plugins/richtext/richTextOperations.d.ts +53 -0
- package/dist/plugins/richtext/richTextOperations.js +200 -0
- package/dist/plugins/slashMenu/SlashMenu.svelte +297 -0
- package/dist/plugins/slashMenu/SlashMenu.svelte.d.ts +11 -0
- package/dist/plugins/slashMenu/SlashMenuController.svelte.d.ts +56 -0
- package/dist/plugins/slashMenu/SlashMenuController.svelte.js +275 -0
- package/dist/plugins/slashMenu/slashMenuPlugin.d.ts +25 -0
- package/dist/plugins/slashMenu/slashMenuPlugin.js +105 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte +142 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +10 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +28 -0
- package/dist/plugins/suggestions/suggestionsPlugin.js +53 -0
- package/dist/plugins/toolbar/Toolbar.svelte +341 -0
- package/dist/plugins/toolbar/Toolbar.svelte.d.ts +9 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.d.ts +85 -0
- package/dist/plugins/toolbar/ToolbarController.svelte.js +150 -0
- package/dist/plugins/toolbar/toolbarPlugin.d.ts +16 -0
- package/dist/plugins/toolbar/toolbarPlugin.js +40 -0
- package/dist/plugins.d.ts +249 -40
- package/dist/plugins.js +11 -5
- package/dist/selection/domSelection.d.ts +29 -0
- package/dist/selection/domSelection.js +230 -0
- package/dist/selection/replaceSelection.d.ts +143 -0
- package/dist/selection/replaceSelection.js +316 -0
- package/dist/selection/selection.svelte.d.ts +275 -37
- package/dist/selection/selection.svelte.js +1162 -331
- package/dist/selection/selection.utils.d.ts +35 -4
- package/dist/selection/selection.utils.js +309 -100
- package/dist/selection/visibility.d.ts +88 -0
- package/dist/selection/visibility.js +140 -0
- package/dist/session/attempt.d.ts +190 -0
- package/dist/session/attempt.js +188 -0
- package/dist/session/bindings.d.ts +16 -0
- package/dist/session/bindings.js +262 -0
- package/dist/session/commands.d.ts +158 -0
- package/dist/session/commands.js +601 -0
- package/dist/session/composition.svelte.d.ts +114 -0
- package/dist/session/composition.svelte.js +489 -0
- package/dist/session/editing/text.d.ts +33 -0
- package/dist/session/editing/text.js +36 -0
- package/dist/session/handles.d.ts +63 -0
- package/dist/session/handles.js +123 -0
- package/dist/session/history.d.ts +49 -0
- package/dist/session/history.js +131 -0
- package/dist/session/keymap.d.ts +42 -0
- package/dist/session/keymap.js +144 -0
- package/dist/session/moves.d.ts +32 -0
- package/dist/session/moves.js +116 -0
- package/dist/session/navigation.d.ts +51 -0
- package/dist/session/navigation.js +312 -0
- package/dist/session/props.d.ts +28 -0
- package/dist/session/props.js +162 -0
- package/dist/session/selection.d.ts +126 -0
- package/dist/session/selection.js +227 -0
- package/dist/session/suggestions.svelte.d.ts +125 -0
- package/dist/session/suggestions.svelte.js +329 -0
- package/dist/session/virtual.d.ts +33 -0
- package/dist/session/virtual.js +182 -0
- package/dist/surface/attributes.d.ts +36 -0
- package/dist/surface/attributes.js +143 -0
- package/dist/surface/cells.d.ts +103 -0
- package/dist/surface/cells.js +215 -0
- package/dist/surface/observer.svelte.d.ts +70 -0
- package/dist/surface/observer.svelte.js +969 -0
- package/dist/surface/overlay.d.ts +33 -0
- package/dist/surface/overlay.js +91 -0
- package/dist/surface/pin.svelte.d.ts +56 -0
- package/dist/surface/pin.svelte.js +108 -0
- package/dist/surface/projector.svelte.d.ts +69 -0
- package/dist/surface/projector.svelte.js +486 -0
- package/dist/text/deltas.d.ts +14 -3
- package/dist/text/deltas.js +27 -90
- package/dist/text/text.svelte.d.ts +43 -31
- package/dist/text/text.svelte.js +88 -69
- package/dist/text/text.utils.d.ts +7 -2
- package/dist/text/text.utils.js +121 -97
- package/dist/themes/notion.css +473 -0
- package/dist/utils/diffText.d.ts +15 -0
- package/dist/utils/diffText.js +26 -0
- package/dist/utils/json.d.ts +90 -3
- package/dist/utils/json.js +369 -2
- package/dist/utils.d.ts +3 -1
- package/dist/utils.js +2 -0
- package/package.json +112 -50
- package/dist/block/content.svelte.d.ts +0 -14
- package/dist/block/content.svelte.js +0 -40
- package/dist/components/ReadonlyEditor.svelte +0 -46
- package/dist/components/ReadonlyEditor.svelte.d.ts +0 -4
- package/dist/components/readonlyElements.svelte.d.ts +0 -82
- package/dist/components/readonlyElements.svelte.js +0 -144
- package/dist/dnd.svelte.d.ts +0 -17
- package/dist/dnd.svelte.js +0 -125
- package/dist/hotkeys.d.ts +0 -27
- package/dist/hotkeys.js +0 -282
- package/dist/localProvider.d.ts +0 -82
- package/dist/localProvider.js +0 -267
- package/dist/plugins/code/prism.css +0 -141
- package/dist/utils/jsx.d.ts +0 -25
- package/dist/utils/jsx.js +0 -66
- package/dist/utils/serialize.js +0 -1
- /package/dist/{utils/serialize.d.ts → crdt/engine-api.js} +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Range deletion between two positions — one prepared document operation
|
|
3
|
+
* (R6) whose merge is the document's `canMerge` (R5). The rules are the
|
|
4
|
+
* `del.range.*` rows of `docs/editor-delete-contract.md`:
|
|
5
|
+
*
|
|
6
|
+
* - the head keeps `[0, start)`, the tail keeps `[end, len)`, and every
|
|
7
|
+
* block strictly between them in document order dies;
|
|
8
|
+
* - the head dies iff its prefix is empty (never for a replacement,
|
|
9
|
+
* `del.range.replace`, nor when nothing else would be left to hold the
|
|
10
|
+
* caret, `del.range.whole-doc`: the head is kept, emptied, with its id,
|
|
11
|
+
* type and data, so peers deleting the same range keep the same block)
|
|
12
|
+
* and the tail then keeps its id; the tail dies iff
|
|
13
|
+
* its suffix is empty; otherwise the tail's suffix merges into the head
|
|
14
|
+
* when `canMerge(tail, head)` allows it (`del.range.island-seal`) — an end
|
|
15
|
+
* at the tail's start included: the seam between them is deleted;
|
|
16
|
+
* - what follows the range end survives: the dying tail's children and the
|
|
17
|
+
* later siblings inside every container the range dies through take the
|
|
18
|
+
* topmost such container's slot, unless that would cross an island seal —
|
|
19
|
+
* then those containers stay (`del.range.outside-survives`);
|
|
20
|
+
* - a container (a list: the document's `isContainer`) never dies because
|
|
21
|
+
* the range starts before it: it keeps what follows the range end, like
|
|
22
|
+
* the keys (`del.merge.container`); it dies only when it loses every
|
|
23
|
+
* child and holds no text of its own — the document's `emptiable`, the
|
|
24
|
+
* keys' rule (`del.range.empty-container`, ZW-05). An island that renders
|
|
25
|
+
* no content dies when it loses every child too, except a `lines` island
|
|
26
|
+
* (a code block) the range lies in: its head is then kept, emptied
|
|
27
|
+
* (`del.range.island-kept`);
|
|
28
|
+
* - what a range rescues takes the kind it shows in its new slot (the
|
|
29
|
+
* document's `settle`: a paragraph rescued into a list is its item);
|
|
30
|
+
* - blocks the view hides (a closed toggle's body) are not in the range:
|
|
31
|
+
* they go only with a block that goes, so a surviving head keeps its
|
|
32
|
+
* hidden children, unless it is kept only to hold the caret; a dying
|
|
33
|
+
* closed tail's body takes its place, shown (`del.range.hidden-body`);
|
|
34
|
+
* - the plan's `at` is where the caret lands (`del.range.caret`).
|
|
35
|
+
*/
|
|
36
|
+
import type { BlockId, Destination } from './placement/model.js';
|
|
37
|
+
import type { Plan, PlanStep, Prepared } from './edytor-doc.js';
|
|
38
|
+
/** A position in one document version: a block and a display offset (R4). */
|
|
39
|
+
export type DocPosition = {
|
|
40
|
+
block: BlockId;
|
|
41
|
+
offset: number;
|
|
42
|
+
};
|
|
43
|
+
/** What range deletion reads from the document and the step writers it composes. */
|
|
44
|
+
export type RangeDeleteContext = {
|
|
45
|
+
ref: (id: BlockId) => BlockId;
|
|
46
|
+
refused: Prepared;
|
|
47
|
+
plan: (ids: readonly BlockId[], writes: PlanStep[]) => Plan;
|
|
48
|
+
order: () => {
|
|
49
|
+
ids: readonly BlockId[];
|
|
50
|
+
at: ReadonlyMap<BlockId, number>;
|
|
51
|
+
};
|
|
52
|
+
positionOf: (id: BlockId) => Destination | null;
|
|
53
|
+
/** Display ancestors, nearest first (the root excluded). */
|
|
54
|
+
ancestorsOf: (id: BlockId) => BlockId[];
|
|
55
|
+
childrenIds: (parent: BlockId | null) => BlockId[];
|
|
56
|
+
displayLength: (id: BlockId) => number;
|
|
57
|
+
/** Live, with its own content node. */
|
|
58
|
+
contentTarget: (id: BlockId) => boolean;
|
|
59
|
+
rendersContent: (id: BlockId) => boolean;
|
|
60
|
+
canMerge: (from: BlockId, into: BlockId) => boolean;
|
|
61
|
+
isIsland: (id: BlockId) => boolean;
|
|
62
|
+
/** An island declared `lines` (a code block): it holds only lines. */
|
|
63
|
+
isLines: (id: BlockId) => boolean;
|
|
64
|
+
move: (ids: BlockId[], parent: BlockId | null, index: number) => PlanStep[];
|
|
65
|
+
/** A block that shows only its children: a list, a table row, a column. */
|
|
66
|
+
isContainer: (id: BlockId) => boolean;
|
|
67
|
+
/** A container that goes once it loses every child (it holds no text of its own). */
|
|
68
|
+
emptiable: (id: BlockId) => boolean;
|
|
69
|
+
/** The kind steps for `from`'s children `kids` landing under `parent` (island and container rules). */
|
|
70
|
+
settle: (from: BlockId | null, kids: readonly BlockId[], parent: BlockId | null) => PlanStep[];
|
|
71
|
+
remove: (id: BlockId, kept: readonly BlockId[]) => PlanStep;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* What the view knows of a range (view state the document does not hold):
|
|
75
|
+
* whether it hides a block, and, given `removed`, whether the block stays
|
|
76
|
+
* hidden once those go (a removed closed toggle's children take its place, shown).
|
|
77
|
+
*/
|
|
78
|
+
export type RangeView = {
|
|
79
|
+
hidden?: (id: BlockId, removed?: ReadonlySet<BlockId>) => boolean;
|
|
80
|
+
};
|
|
81
|
+
/** `deleteRange` and `replaceRange` (the head kept), prepared. */
|
|
82
|
+
export declare const rangeDeleteOps: (c: RangeDeleteContext) => {
|
|
83
|
+
deleteRange: (from: DocPosition, to: DocPosition, view?: RangeView) => Prepared;
|
|
84
|
+
replaceRange: (from: DocPosition, to: DocPosition, view?: RangeView) => Prepared;
|
|
85
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** `deleteRange` and `replaceRange` (the head kept), prepared. */
|
|
2
|
+
export const rangeDeleteOps = (c) => {
|
|
3
|
+
// `headSpent`: a whole-doc retry keeps the head, but what it hid goes as if it went.
|
|
4
|
+
const prepare = (keepHead, headSpent = false) => (from, to, view = {}) => {
|
|
5
|
+
// A position names shown text: a live block that renders its own content.
|
|
6
|
+
const holds = (id) => c.contentTarget(id) && c.rendersContent(id);
|
|
7
|
+
const pos = (p) => {
|
|
8
|
+
const block = c.ref(p.block);
|
|
9
|
+
const len = holds(block) ? c.displayLength(block) : -1;
|
|
10
|
+
return { block, offset: Math.max(0, Math.min(p.offset, len)) };
|
|
11
|
+
};
|
|
12
|
+
let [s, e] = [pos(from), pos(to)];
|
|
13
|
+
if (!holds(s.block) || !holds(e.block))
|
|
14
|
+
return c.refused;
|
|
15
|
+
const { ids, at } = c.order();
|
|
16
|
+
if (at.get(s.block) > at.get(e.block) || (s.block === e.block && s.offset > e.offset))
|
|
17
|
+
[s, e] = [e, s];
|
|
18
|
+
const S = s.block;
|
|
19
|
+
const E = e.block;
|
|
20
|
+
if (S === E) {
|
|
21
|
+
const length = e.offset - s.offset;
|
|
22
|
+
const writes = length > 0 ? [{ op: 'deleteText', id: S, offset: s.offset, length }] : [];
|
|
23
|
+
return { ...c.plan([S], writes), at: s };
|
|
24
|
+
}
|
|
25
|
+
const parent = (id) => c.ancestorsOf(id)[0] ?? null;
|
|
26
|
+
const chain = c.ancestorsOf(E);
|
|
27
|
+
// What the view hides once `removed` go (nothing: as it is now).
|
|
28
|
+
const hiddenOnce = (removed) => {
|
|
29
|
+
const set = new Set(removed);
|
|
30
|
+
return (id) => !!view.hidden?.(id, set);
|
|
31
|
+
};
|
|
32
|
+
const headDies = !keepHead && s.offset === 0;
|
|
33
|
+
// What the view hides is not in the range: it goes only with a block that goes,
|
|
34
|
+
// a dying head's body included.
|
|
35
|
+
const hidden = hiddenOnce(headDies || headSpent ? [S] : []);
|
|
36
|
+
const between = ids.slice(at.get(S) + 1, at.get(E)).filter((id) => !hidden(id));
|
|
37
|
+
const lenE = c.displayLength(E);
|
|
38
|
+
const tailDies = e.offset === lenE;
|
|
39
|
+
const merges = !headDies && !tailDies && c.canMerge(E, S);
|
|
40
|
+
const tailGone = tailDies || merges;
|
|
41
|
+
// E's ancestors the range starts before die unless the rescue would cross an island.
|
|
42
|
+
// A container (a list) is not one: it dies only when the range empties it, and
|
|
43
|
+
// keeps its later items like the keys do (`del.merge.container`, DR-crdt-4).
|
|
44
|
+
const partial = chain.filter((a) => (a === (headDies ? S : null) || between.includes(a)) && !c.isContainer(a));
|
|
45
|
+
const top = partial.at(-1);
|
|
46
|
+
const sealed = top !== undefined && chain.slice(0, chain.indexOf(top) + 1).some(c.isIsland);
|
|
47
|
+
const spared = new Set(sealed ? chain : []);
|
|
48
|
+
const doomed = new Set(between.filter((id) => !spared.has(id) && !chain.includes(id)));
|
|
49
|
+
if (!sealed)
|
|
50
|
+
partial.forEach((a) => doomed.add(a));
|
|
51
|
+
if (headDies && !spared.has(S))
|
|
52
|
+
doomed.add(S);
|
|
53
|
+
if (tailDies)
|
|
54
|
+
doomed.add(E);
|
|
55
|
+
// Rescue what follows the range end right after the topmost dying container's slot
|
|
56
|
+
// (after, not at it: a concurrent delete of S revives a merged E above them, UW-20).
|
|
57
|
+
const home = sealed || top === undefined ? (tailGone ? E : undefined) : top;
|
|
58
|
+
const rescued = home === undefined ? [] : tailGone ? c.childrenIds(E) : [E];
|
|
59
|
+
for (let cur = E; home !== undefined && cur !== home; cur = parent(cur)) {
|
|
60
|
+
const sibs = c.childrenIds(parent(cur));
|
|
61
|
+
rescued.push(...sibs.slice(sibs.indexOf(cur) + 1));
|
|
62
|
+
}
|
|
63
|
+
const dest = home === undefined || rescued.length === 0 ? null : c.positionOf(home);
|
|
64
|
+
// A container (or an island that renders nothing) losing every child dies too.
|
|
65
|
+
const gone = (id) => doomed.has(id) || (merges && id === E);
|
|
66
|
+
const dies = (id) => c.emptiable(id) || (c.isIsland(id) && !c.rendersContent(id));
|
|
67
|
+
const emptied = (id) => {
|
|
68
|
+
if (id === null || gone(id) || !dies(id) || id === dest?.parent)
|
|
69
|
+
return;
|
|
70
|
+
if (!c.childrenIds(id).every(gone))
|
|
71
|
+
return;
|
|
72
|
+
doomed.add(id);
|
|
73
|
+
emptied(parent(id));
|
|
74
|
+
};
|
|
75
|
+
[...doomed, ...(merges ? [E] : [])].forEach((id) => emptied(parent(id)));
|
|
76
|
+
// A range inside one `lines` island never removes it: the head stays, emptied
|
|
77
|
+
// (a code block's Mod+A, `del.range.island-kept`).
|
|
78
|
+
const island = c.ancestorsOf(S).find(c.isIsland);
|
|
79
|
+
const linesIsland = island !== undefined && c.isLines(island);
|
|
80
|
+
if (!keepHead && linesIsland && doomed.has(island) && chain.includes(island))
|
|
81
|
+
return prepare(true, headDies)(from, to, view);
|
|
82
|
+
const lenS = c.displayLength(S);
|
|
83
|
+
const writes = [];
|
|
84
|
+
if (!doomed.has(S) && lenS > s.offset)
|
|
85
|
+
writes.push({ op: 'deleteText', id: S, offset: s.offset, length: lenS - s.offset });
|
|
86
|
+
if (!gone(E) && e.offset > 0)
|
|
87
|
+
writes.push({ op: 'deleteText', id: E, offset: 0, length: e.offset });
|
|
88
|
+
if (dest !== null) {
|
|
89
|
+
writes.push(...c.move(rescued, dest.parent, dest.index + 1));
|
|
90
|
+
// Each shows the kind of its new slot (an island's line, a list's item leave theirs).
|
|
91
|
+
for (const id of rescued)
|
|
92
|
+
writes.push(...c.settle(parent(id), [id], dest.parent));
|
|
93
|
+
}
|
|
94
|
+
if (merges) {
|
|
95
|
+
// The whole tail merges, then its cut prefix goes: E's own text is never written.
|
|
96
|
+
writes.push({ op: 'mergeBlocks', from: E, into: S, at: s.offset, length: lenE });
|
|
97
|
+
if (e.offset > 0)
|
|
98
|
+
writes.push({ op: 'deleteText', id: S, offset: s.offset, length: e.offset });
|
|
99
|
+
}
|
|
100
|
+
const kept = [...rescued, ...(merges ? [E] : [])];
|
|
101
|
+
for (const id of doomed)
|
|
102
|
+
if (!doomed.has(parent(id)))
|
|
103
|
+
writes.push(c.remove(id, kept));
|
|
104
|
+
const caret = (() => {
|
|
105
|
+
if (!doomed.has(S))
|
|
106
|
+
return s;
|
|
107
|
+
if (!gone(E))
|
|
108
|
+
return { block: E, offset: 0 };
|
|
109
|
+
const survives = (id) => id === null || rescued.includes(id) || (!gone(id) && survives(parent(id)));
|
|
110
|
+
// Shown once the plan runs: a dying closed tail's rescued body is.
|
|
111
|
+
const hiddenAfter = hiddenOnce([...doomed, ...(merges ? [E] : [])]);
|
|
112
|
+
const shown = (id) => survives(id) && holds(id) && !hiddenAfter(id);
|
|
113
|
+
// Before the tail: blocks a sealed rescue spared between S and E count too.
|
|
114
|
+
const prev = ids.slice(0, at.get(E)).findLast(shown);
|
|
115
|
+
if (prev !== undefined)
|
|
116
|
+
return { block: prev, offset: c.displayLength(prev) };
|
|
117
|
+
const next = ids.slice(at.get(E) + 1).find(shown);
|
|
118
|
+
return next === undefined ? null : { block: next, offset: 0 };
|
|
119
|
+
})();
|
|
120
|
+
// Nothing else left to hold the caret: the head stays, emptied (whole-doc).
|
|
121
|
+
if (caret === null)
|
|
122
|
+
return prepare(true, headDies)(from, to, view);
|
|
123
|
+
return { ...c.plan([caret.block], writes), at: caret };
|
|
124
|
+
};
|
|
125
|
+
return { deleteRange: prepare(false), replaceRange: prepare(true) };
|
|
126
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema names — the one table (O20): every root key, node role and attr name
|
|
3
|
+
* a document carries, plus the generation stamp written on `meta`. A
|
|
4
|
+
* dependency-free leaf, so every model layer reads it without importing the
|
|
5
|
+
* facade module.
|
|
6
|
+
*/
|
|
7
|
+
import type { EngineNode } from './engine-api.js';
|
|
8
|
+
/** A replicated value that is a node (a block, its content, claims or `at` map, an atom). */
|
|
9
|
+
export declare const isNodeLike: (v: unknown) => v is EngineNode;
|
|
10
|
+
/** Flat block registry root: blockId → node('block') (`SCHEMA.roots.registry`). */
|
|
11
|
+
export declare const REGISTRY_KEY = "blocks";
|
|
12
|
+
/** Version/manifest record root (`SCHEMA.roots.meta`). */
|
|
13
|
+
export declare const META_ROOT_KEY = "meta";
|
|
14
|
+
/** Attribution metadata root (`SCHEMA.roots.attribution`). */
|
|
15
|
+
export declare const ATTRIBUTION_ROOT = "attribution";
|
|
16
|
+
/** U1 per-block attribution root — `b/<blockId>` records (`SCHEMA.roots.blockAttribution`). */
|
|
17
|
+
export declare const BLOCK_ATTR_ROOT = "blockattr";
|
|
18
|
+
/**
|
|
19
|
+
* Per-writer text delete marks (`text/deletes.ts`): one record per text
|
|
20
|
+
* delete, naming the characters it deleted; its writer is the record's
|
|
21
|
+
* engine client. In the history's scope, so an undo removes the undoer's
|
|
22
|
+
* own mark and a redo writes it again.
|
|
23
|
+
*/
|
|
24
|
+
export declare const TEXT_DELETES_ROOT = "textdel";
|
|
25
|
+
/**
|
|
26
|
+
* Restoration records (`text/deletes.ts`): which characters an undo's copies
|
|
27
|
+
* restore, one attr per record (keyed by its writer and clock). Outside the
|
|
28
|
+
* history's scope: a record is never removed.
|
|
29
|
+
*/
|
|
30
|
+
export declare const RESTORED_ROOT = "restored";
|
|
31
|
+
/**
|
|
32
|
+
* The document's own data (`crdt/data.ts`): its leaves, as a block's. In the
|
|
33
|
+
* history's scope.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DOC_DATA_ROOT = "docdata";
|
|
36
|
+
/** Named node roles (`SCHEMA.nodes`). */
|
|
37
|
+
export declare const BLOCK_NODE = "block";
|
|
38
|
+
export declare const CONTENT_NODE = "content";
|
|
39
|
+
export declare const CLAIMS_NODE = "claims";
|
|
40
|
+
export declare const AT_NODE = "at";
|
|
41
|
+
export declare const INLINE_NODE = "inline";
|
|
42
|
+
/** Attr keys on a block node (`SCHEMA.blockAttrs`). */
|
|
43
|
+
export declare const ID = "id";
|
|
44
|
+
export declare const TYPE = "type";
|
|
45
|
+
/** A block's or atom's whole data: the legacy layer, read under the leaves and exploded by the first patch. */
|
|
46
|
+
export declare const DATA = "data";
|
|
47
|
+
/** A data leaf: `d/` and its path, RFC 6901 escaped (`crdt/data.ts`). */
|
|
48
|
+
export declare const DATA_LEAF_PREFIX = "d/";
|
|
49
|
+
/**
|
|
50
|
+
* Incarnation nonce (O23, §2.1): which creation of a (recyclable) block id a
|
|
51
|
+
* node is — and which stream boundary `{s, n}` starts the block's stream.
|
|
52
|
+
* Written at creation (random from the `rand` seam, derived from the seed
|
|
53
|
+
* hash for seeded blocks) and re-minted, derived from the dead incarnation,
|
|
54
|
+
* when a streamless block gets its own text; replicated, so an undo/redo copy
|
|
55
|
+
* of the node carries it and every replica agrees on the incarnation.
|
|
56
|
+
*/
|
|
57
|
+
export declare const NONCE = "n";
|
|
58
|
+
/**
|
|
59
|
+
* Per-writer delete marks (R3): `del.<writer>: true`. A block is deleted iff
|
|
60
|
+
* any mark is live, so an undo removes only the undoer's own mark.
|
|
61
|
+
*/
|
|
62
|
+
export declare const DEL_PREFIX = "del.";
|
|
63
|
+
export declare const hasDeleteMark: (node: {
|
|
64
|
+
attrKeys(): IterableIterator<string>;
|
|
65
|
+
}) => boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Per-writer withdraw marks (`hist.undo.withdraw`): `wd.<writer>: true`, what
|
|
68
|
+
* an undo of the block's creation writes instead of deleting the node. A
|
|
69
|
+
* withdrawn block without a delete mark is visible while it holds content
|
|
70
|
+
* (a live unit in its stream, or a visible child), hidden otherwise.
|
|
71
|
+
*/
|
|
72
|
+
export declare const WITHDRAW_PREFIX = "wd.";
|
|
73
|
+
export declare const hasWithdrawMark: (node: {
|
|
74
|
+
attrKeys(): IterableIterator<string>;
|
|
75
|
+
}) => boolean;
|
|
76
|
+
/** U1 `lastChangedBy` LWW attr (`SCHEMA.blockAttrs.lastChanged`). */
|
|
77
|
+
export declare const LAST_CHANGED_ATTR = "l";
|
|
78
|
+
export declare const CONTENT = "content";
|
|
79
|
+
/** Ordered merge claims `{m: blockId}` (R2). */
|
|
80
|
+
export declare const CLAIMS = "claims";
|
|
81
|
+
export declare const AT = "at";
|
|
82
|
+
/** `b/<blockId>` record-key prefix on the `blockattr` root. */
|
|
83
|
+
export declare const REC_PREFIX = "b/";
|
|
84
|
+
/** The schema generation stamped on the `meta` root (`v`) and its manifest name (`schema`). */
|
|
85
|
+
export declare const SCHEMA: {
|
|
86
|
+
readonly version: 4;
|
|
87
|
+
readonly name: "edytor-doc";
|
|
88
|
+
readonly roots: {
|
|
89
|
+
readonly registry: "blocks";
|
|
90
|
+
readonly meta: "meta";
|
|
91
|
+
};
|
|
92
|
+
readonly metaAttrs: {
|
|
93
|
+
readonly version: "v";
|
|
94
|
+
readonly schema: "schema";
|
|
95
|
+
};
|
|
96
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/** A replicated value that is a node (a block, its content, claims or `at` map, an atom). */
|
|
2
|
+
export const isNodeLike = (v) => v != null && typeof v.getAttr === 'function';
|
|
3
|
+
/** Flat block registry root: blockId → node('block') (`SCHEMA.roots.registry`). */
|
|
4
|
+
export const REGISTRY_KEY = 'blocks';
|
|
5
|
+
/** Version/manifest record root (`SCHEMA.roots.meta`). */
|
|
6
|
+
export const META_ROOT_KEY = 'meta';
|
|
7
|
+
/** Attribution metadata root (`SCHEMA.roots.attribution`). */
|
|
8
|
+
export const ATTRIBUTION_ROOT = 'attribution';
|
|
9
|
+
/** U1 per-block attribution root — `b/<blockId>` records (`SCHEMA.roots.blockAttribution`). */
|
|
10
|
+
export const BLOCK_ATTR_ROOT = 'blockattr';
|
|
11
|
+
/**
|
|
12
|
+
* Per-writer text delete marks (`text/deletes.ts`): one record per text
|
|
13
|
+
* delete, naming the characters it deleted; its writer is the record's
|
|
14
|
+
* engine client. In the history's scope, so an undo removes the undoer's
|
|
15
|
+
* own mark and a redo writes it again.
|
|
16
|
+
*/
|
|
17
|
+
export const TEXT_DELETES_ROOT = 'textdel';
|
|
18
|
+
/**
|
|
19
|
+
* Restoration records (`text/deletes.ts`): which characters an undo's copies
|
|
20
|
+
* restore, one attr per record (keyed by its writer and clock). Outside the
|
|
21
|
+
* history's scope: a record is never removed.
|
|
22
|
+
*/
|
|
23
|
+
export const RESTORED_ROOT = 'restored';
|
|
24
|
+
/**
|
|
25
|
+
* The document's own data (`crdt/data.ts`): its leaves, as a block's. In the
|
|
26
|
+
* history's scope.
|
|
27
|
+
*/
|
|
28
|
+
export const DOC_DATA_ROOT = 'docdata';
|
|
29
|
+
/** Named node roles (`SCHEMA.nodes`). */
|
|
30
|
+
export const BLOCK_NODE = 'block';
|
|
31
|
+
export const CONTENT_NODE = 'content';
|
|
32
|
+
export const CLAIMS_NODE = 'claims';
|
|
33
|
+
export const AT_NODE = 'at';
|
|
34
|
+
export const INLINE_NODE = 'inline';
|
|
35
|
+
/** Attr keys on a block node (`SCHEMA.blockAttrs`). */
|
|
36
|
+
export const ID = 'id';
|
|
37
|
+
export const TYPE = 'type';
|
|
38
|
+
/** A block's or atom's whole data: the legacy layer, read under the leaves and exploded by the first patch. */
|
|
39
|
+
export const DATA = 'data';
|
|
40
|
+
/** A data leaf: `d/` and its path, RFC 6901 escaped (`crdt/data.ts`). */
|
|
41
|
+
export const DATA_LEAF_PREFIX = 'd/';
|
|
42
|
+
/**
|
|
43
|
+
* Incarnation nonce (O23, §2.1): which creation of a (recyclable) block id a
|
|
44
|
+
* node is — and which stream boundary `{s, n}` starts the block's stream.
|
|
45
|
+
* Written at creation (random from the `rand` seam, derived from the seed
|
|
46
|
+
* hash for seeded blocks) and re-minted, derived from the dead incarnation,
|
|
47
|
+
* when a streamless block gets its own text; replicated, so an undo/redo copy
|
|
48
|
+
* of the node carries it and every replica agrees on the incarnation.
|
|
49
|
+
*/
|
|
50
|
+
export const NONCE = 'n';
|
|
51
|
+
/**
|
|
52
|
+
* Per-writer delete marks (R3): `del.<writer>: true`. A block is deleted iff
|
|
53
|
+
* any mark is live, so an undo removes only the undoer's own mark.
|
|
54
|
+
*/
|
|
55
|
+
export const DEL_PREFIX = 'del.';
|
|
56
|
+
export const hasDeleteMark = (node) => [...node.attrKeys()].some((key) => key.startsWith(DEL_PREFIX));
|
|
57
|
+
/**
|
|
58
|
+
* Per-writer withdraw marks (`hist.undo.withdraw`): `wd.<writer>: true`, what
|
|
59
|
+
* an undo of the block's creation writes instead of deleting the node. A
|
|
60
|
+
* withdrawn block without a delete mark is visible while it holds content
|
|
61
|
+
* (a live unit in its stream, or a visible child), hidden otherwise.
|
|
62
|
+
*/
|
|
63
|
+
export const WITHDRAW_PREFIX = 'wd.';
|
|
64
|
+
export const hasWithdrawMark = (node) => [...node.attrKeys()].some((key) => key.startsWith(WITHDRAW_PREFIX));
|
|
65
|
+
/** U1 `lastChangedBy` LWW attr (`SCHEMA.blockAttrs.lastChanged`). */
|
|
66
|
+
export const LAST_CHANGED_ATTR = 'l';
|
|
67
|
+
export const CONTENT = 'content';
|
|
68
|
+
/** Ordered merge claims `{m: blockId}` (R2). */
|
|
69
|
+
export const CLAIMS = 'claims';
|
|
70
|
+
export const AT = 'at';
|
|
71
|
+
/** `b/<blockId>` record-key prefix on the `blockattr` root. */
|
|
72
|
+
export const REC_PREFIX = 'b/';
|
|
73
|
+
/** The schema generation stamped on the `meta` root (`v`) and its manifest name (`schema`). */
|
|
74
|
+
export const SCHEMA = {
|
|
75
|
+
version: 4,
|
|
76
|
+
name: 'edytor-doc',
|
|
77
|
+
roots: { registry: REGISTRY_KEY, meta: META_ROOT_KEY },
|
|
78
|
+
metaAttrs: { version: 'v', schema: 'schema' }
|
|
79
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { BlockRole } from './edytor-doc.js';
|
|
2
|
+
/** One kind's structural row — the fields of its `BlockDefinition` a document adopts. */
|
|
3
|
+
export type KindSemantics = BlockRole & {
|
|
4
|
+
rendersContent?: boolean;
|
|
5
|
+
defaultChild?: string;
|
|
6
|
+
};
|
|
7
|
+
/** `richTextPlugin`'s structural rows (its other kinds are plain). */
|
|
8
|
+
export declare const richTextKinds: {
|
|
9
|
+
divider: {
|
|
10
|
+
void: true;
|
|
11
|
+
rendersContent: false;
|
|
12
|
+
};
|
|
13
|
+
horizontalRule: {
|
|
14
|
+
void: true;
|
|
15
|
+
rendersContent: false;
|
|
16
|
+
};
|
|
17
|
+
'ordered-list': {
|
|
18
|
+
rendersContent: false;
|
|
19
|
+
defaultChild: string;
|
|
20
|
+
};
|
|
21
|
+
'unordered-list': {
|
|
22
|
+
rendersContent: false;
|
|
23
|
+
defaultChild: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/** `codePlugin`'s structural rows: the code block is an island of `codeLine`s (`lines`). */
|
|
27
|
+
export declare const codeKinds: {
|
|
28
|
+
code: {
|
|
29
|
+
island: true;
|
|
30
|
+
lines: true;
|
|
31
|
+
rendersContent: false;
|
|
32
|
+
defaultChild: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** `imagePlugin`'s structural rows: void, its only text is the caption. */
|
|
36
|
+
export declare const imageKinds: {
|
|
37
|
+
image: {
|
|
38
|
+
void: true;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Kind tables (`type → row`) as one {@link DocumentSemanticsConfig}, deeply
|
|
43
|
+
* frozen: `semanticsOf({ embed: { void: true, rendersContent: false } })`.
|
|
44
|
+
* To add kinds to a bundled config, merge `roles`, `rendersContent` and
|
|
45
|
+
* `defaultChild` field by field
|
|
46
|
+
* (`roles: { ...defaultSemantics.roles, ...mine.roles }`, and so on): a
|
|
47
|
+
* top-level spread of two configs keeps only the last one's fields and
|
|
48
|
+
* drops the bundled void, island and default-child rows.
|
|
49
|
+
*/
|
|
50
|
+
export declare const semanticsOf: (...tables: Record<string, KindSemantics>[]) => {
|
|
51
|
+
roles: Record<string, BlockRole>;
|
|
52
|
+
rendersContent: Record<string, boolean>;
|
|
53
|
+
defaultChild: Record<string, string>;
|
|
54
|
+
};
|
|
55
|
+
/** `richTextPlugin`'s block roles, for `createDocument({ semantics })` and the room. */
|
|
56
|
+
export declare const richTextSemantics: {
|
|
57
|
+
roles: Record<string, BlockRole>;
|
|
58
|
+
rendersContent: Record<string, boolean>;
|
|
59
|
+
defaultChild: Record<string, string>;
|
|
60
|
+
};
|
|
61
|
+
/** `codePlugin`'s block roles. */
|
|
62
|
+
export declare const codeSemantics: {
|
|
63
|
+
roles: Record<string, BlockRole>;
|
|
64
|
+
rendersContent: Record<string, boolean>;
|
|
65
|
+
defaultChild: Record<string, string>;
|
|
66
|
+
};
|
|
67
|
+
/** `imagePlugin`'s block roles. */
|
|
68
|
+
export declare const imageSemantics: {
|
|
69
|
+
roles: Record<string, BlockRole>;
|
|
70
|
+
rendersContent: Record<string, boolean>;
|
|
71
|
+
defaultChild: Record<string, string>;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The rich-text, code and image plugins' block roles together — what the
|
|
75
|
+
* room adopts by default and what a headless document passes explicitly.
|
|
76
|
+
*/
|
|
77
|
+
export declare const defaultSemantics: {
|
|
78
|
+
roles: Record<string, BlockRole>;
|
|
79
|
+
rendersContent: Record<string, boolean>;
|
|
80
|
+
defaultChild: Record<string, string>;
|
|
81
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Freeze `value` and everything in it: the tables are shared by every document. */
|
|
2
|
+
const frozen = (value) => {
|
|
3
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
4
|
+
Object.freeze(value);
|
|
5
|
+
for (const inner of Object.values(value))
|
|
6
|
+
frozen(inner);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
/** `richTextPlugin`'s structural rows (its other kinds are plain). */
|
|
11
|
+
export const richTextKinds = frozen({
|
|
12
|
+
divider: { void: true, rendersContent: false },
|
|
13
|
+
horizontalRule: { void: true, rendersContent: false },
|
|
14
|
+
'ordered-list': { rendersContent: false, defaultChild: 'list-item' },
|
|
15
|
+
'unordered-list': { rendersContent: false, defaultChild: 'list-item' }
|
|
16
|
+
});
|
|
17
|
+
/** `codePlugin`'s structural rows: the code block is an island of `codeLine`s (`lines`). */
|
|
18
|
+
export const codeKinds = frozen({
|
|
19
|
+
code: { island: true, lines: true, rendersContent: false, defaultChild: 'codeLine' }
|
|
20
|
+
});
|
|
21
|
+
/** `imagePlugin`'s structural rows: void, its only text is the caption. */
|
|
22
|
+
export const imageKinds = frozen({ image: { void: true } });
|
|
23
|
+
/**
|
|
24
|
+
* Kind tables (`type → row`) as one {@link DocumentSemanticsConfig}, deeply
|
|
25
|
+
* frozen: `semanticsOf({ embed: { void: true, rendersContent: false } })`.
|
|
26
|
+
* To add kinds to a bundled config, merge `roles`, `rendersContent` and
|
|
27
|
+
* `defaultChild` field by field
|
|
28
|
+
* (`roles: { ...defaultSemantics.roles, ...mine.roles }`, and so on): a
|
|
29
|
+
* top-level spread of two configs keeps only the last one's fields and
|
|
30
|
+
* drops the bundled void, island and default-child rows.
|
|
31
|
+
*/
|
|
32
|
+
export const semanticsOf = (...tables) => {
|
|
33
|
+
const semantics = {
|
|
34
|
+
roles: {},
|
|
35
|
+
rendersContent: {},
|
|
36
|
+
defaultChild: {}
|
|
37
|
+
};
|
|
38
|
+
for (const [type, { rendersContent, defaultChild, ...role }] of tables.flatMap(Object.entries)) {
|
|
39
|
+
semantics.roles[type] = role;
|
|
40
|
+
if (rendersContent !== undefined)
|
|
41
|
+
semantics.rendersContent[type] = rendersContent;
|
|
42
|
+
if (defaultChild !== undefined)
|
|
43
|
+
semantics.defaultChild[type] = defaultChild;
|
|
44
|
+
}
|
|
45
|
+
return frozen(semantics);
|
|
46
|
+
};
|
|
47
|
+
/** `richTextPlugin`'s block roles, for `createDocument({ semantics })` and the room. */
|
|
48
|
+
export const richTextSemantics = semanticsOf(richTextKinds);
|
|
49
|
+
/** `codePlugin`'s block roles. */
|
|
50
|
+
export const codeSemantics = semanticsOf(codeKinds);
|
|
51
|
+
/** `imagePlugin`'s block roles. */
|
|
52
|
+
export const imageSemantics = semanticsOf(imageKinds);
|
|
53
|
+
/**
|
|
54
|
+
* The rich-text, code and image plugins' block roles together — what the
|
|
55
|
+
* room adopts by default and what a headless document passes explicitly.
|
|
56
|
+
*/
|
|
57
|
+
export const defaultSemantics = semanticsOf(richTextKinds, codeKinds, imageKinds);
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendor-internal struct access — the ONE place Edytor code reaches into
|
|
3
|
+
* the engine's struct store (`doc.store.clients`) and walks structs by id
|
|
4
|
+
* set. Centralizing these reads keeps the blast radius of a vendored
|
|
5
|
+
* engine upgrade in one module: when an internal is renamed or reshaped,
|
|
6
|
+
* the fail-fast checks below break loudly at upgrade time instead of
|
|
7
|
+
* silently disabling run invalidation or history lineage (the old
|
|
8
|
+
* optional-chained reads turned a rename into a silent no-op).
|
|
9
|
+
*
|
|
10
|
+
* The engine surface is injected (`Y: EngineApi`) like every other bound
|
|
11
|
+
* module — this file never imports vendor `.js`.
|
|
12
|
+
*/
|
|
13
|
+
import type { EngineApi, EngineDoc } from './engine-api.js';
|
|
14
|
+
/**
|
|
15
|
+
* Structural shape of one store struct (Item | GC | Skip) — the subset
|
|
16
|
+
* Edytor reads. `redone`/`keep` are local-only bits (never serialized).
|
|
17
|
+
*/
|
|
18
|
+
export type StoreStruct = {
|
|
19
|
+
id: {
|
|
20
|
+
client: number;
|
|
21
|
+
clock: number;
|
|
22
|
+
};
|
|
23
|
+
length: number;
|
|
24
|
+
deleted: boolean;
|
|
25
|
+
/** `null` for sequence items; an attr name for map-entry items. */
|
|
26
|
+
parentSub: string | null;
|
|
27
|
+
parent: unknown;
|
|
28
|
+
right?: StoreStruct | null;
|
|
29
|
+
countable?: boolean;
|
|
30
|
+
keep?: boolean;
|
|
31
|
+
redone?: {
|
|
32
|
+
client: number;
|
|
33
|
+
clock: number;
|
|
34
|
+
} | null;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The engine's IdSet shape (insertSet/deleteSet): per-client range lists —
|
|
38
|
+
* each `{clock, len}` covers a contiguous struct run in that client's
|
|
39
|
+
* store list.
|
|
40
|
+
*/
|
|
41
|
+
export type IdSetLike = {
|
|
42
|
+
clients: Map<number, {
|
|
43
|
+
getIds(): {
|
|
44
|
+
clock: number;
|
|
45
|
+
len: number;
|
|
46
|
+
}[];
|
|
47
|
+
}>;
|
|
48
|
+
has(client: number, clock: number): boolean;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The doc's struct store — vendor-internal (`doc.store.clients`).
|
|
52
|
+
* FAIL-FAST: a missing store means the doc is not a vendored-engine doc
|
|
53
|
+
* (or the vendored layout changed) — either way the only honest response
|
|
54
|
+
* is a descriptive throw, never a silent fallback.
|
|
55
|
+
*/
|
|
56
|
+
export declare const clientsOf: (doc: EngineDoc) => Map<number, StoreStruct[]>;
|
|
57
|
+
/**
|
|
58
|
+
* The struct covering `clock` in a client's clock-sorted struct list, or
|
|
59
|
+
* `null` when no struct contains it. Wraps the vendor's `findIndexSS`
|
|
60
|
+
* binary search — which THROWS on a miss — into the null-on-miss
|
|
61
|
+
* convention callers use.
|
|
62
|
+
*/
|
|
63
|
+
export declare const structAt: (Y: EngineApi, structs: readonly StoreStruct[], clock: number) => StoreStruct | null;
|
|
64
|
+
/**
|
|
65
|
+
* Walk every struct an IdSet covers, invoking `fn` per struct in
|
|
66
|
+
* (client, clock) order. IdSet ranges are item-aligned (they record whole
|
|
67
|
+
* structs), so an overlapping struct is a covered struct — the walk never
|
|
68
|
+
* splits.
|
|
69
|
+
*
|
|
70
|
+
* `fn` returning `false` halts the walk early (caller-side short-circuit).
|
|
71
|
+
*
|
|
72
|
+
* Returns `false` when coverage was INCOMPLETE — a client's struct list
|
|
73
|
+
* was missing or a range start could not be located — so the caller can
|
|
74
|
+
* fall back to an all-consumers/opaque answer instead of trusting a
|
|
75
|
+
* partial walk. `clientsOf` throws first when the store itself is absent.
|
|
76
|
+
*/
|
|
77
|
+
export declare const walkIdSetStructs: (Y: EngineApi, doc: EngineDoc, idSet: IdSetLike, fn: (struct: StoreStruct) => boolean | void) => boolean;
|
|
78
|
+
/**
|
|
79
|
+
* The id the local `redone` chain leads `id` to: the copy an undo or redo
|
|
80
|
+
* on THIS replica re-created of a deleted item (redo copies chain), or `id`
|
|
81
|
+
* itself. Replica-dependent by design: only the replica that ran the
|
|
82
|
+
* history command has the chain.
|
|
83
|
+
*/
|
|
84
|
+
export declare const followRedone: (Y: EngineApi, doc: EngineDoc, id: {
|
|
85
|
+
client: number;
|
|
86
|
+
clock: number;
|
|
87
|
+
}) => {
|
|
88
|
+
client: number;
|
|
89
|
+
clock: number;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Forget `node`'s search markers (vendor-internal `_searchMarker`, P4): a
|
|
93
|
+
* write made through items directly (a delete or `redoItem`, not the node's
|
|
94
|
+
* own insert/delete methods) leaves their indices stale — the engine's undo
|
|
95
|
+
* drops them the same way after a pop.
|
|
96
|
+
*/
|
|
97
|
+
export declare const dropSearchMarkers: (node: unknown) => void;
|
|
98
|
+
/** The clock `client`'s next struct gets in `doc`. */
|
|
99
|
+
export declare const nextClock: (doc: EngineDoc, client: number) => number;
|
|
100
|
+
/**
|
|
101
|
+
* Whether `doc` holds updates it cannot integrate yet (vendor-internal
|
|
102
|
+
* `store.pendingStructs`/`pendingDs`): an out-of-order delivery can apply
|
|
103
|
+
* a delete whose replacement is still pending, so an attr may read
|
|
104
|
+
* undefined until the missing update arrives.
|
|
105
|
+
*/
|
|
106
|
+
export declare const holdsPending: (doc: EngineDoc) => boolean;
|