edytor 3.0.0 → 5.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 +2 -2
- package/dist/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +23 -12
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +139 -129
- package/dist/cloudflare/room/context.js +11 -4
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +45 -19
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +34 -12
- package/dist/crdt/attribution/attribution.js +59 -21
- 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/capability.js +9 -13
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +34 -26
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +31 -9
- package/dist/crdt/doc/history.js +204 -73
- package/dist/crdt/doc/layout.js +16 -27
- package/dist/crdt/doc/meta.js +17 -23
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +20 -30
- 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 +55 -125
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +26 -6
- package/dist/crdt/document.js +40 -15
- package/dist/crdt/edytor-doc.d.ts +410 -390
- package/dist/crdt/edytor-doc.js +143 -95
- package/dist/crdt/engine-api.d.ts +39 -1
- package/dist/crdt/engine.d.ts +2 -2
- package/dist/crdt/engine.js +4 -5
- package/dist/crdt/flow.d.ts +2 -2
- package/dist/crdt/flow.js +29 -37
- package/dist/crdt/index.d.ts +134 -127
- package/dist/crdt/index.js +4 -1
- package/dist/crdt/operations.d.ts +12 -3
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +6 -31
- package/dist/crdt/placement/display.js +41 -146
- package/dist/crdt/placement/keys.d.ts +63 -0
- package/dist/crdt/placement/keys.js +95 -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 +188 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +6 -4
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.d.ts +6 -4
- package/dist/crdt/providers/websocket.js +14 -7
- package/dist/crdt/purge.d.ts +24 -9
- package/dist/crdt/purge.js +266 -126
- package/dist/crdt/restore.d.ts +4 -3
- package/dist/crdt/restore.js +27 -34
- package/dist/crdt/schema.d.ts +30 -21
- package/dist/crdt/schema.js +39 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +17 -15
- package/dist/crdt/structs.js +10 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +13 -23
- 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.d.ts +2 -4
- package/dist/crdt/text/index/layout.js +14 -30
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +420 -157
- 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 +29 -17
- 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 +87 -62
- package/dist/crdt/text/model.js +91 -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 +51 -42
- 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 +651 -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 +197 -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 +54 -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 +649 -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 +199 -12
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +103 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +28 -3
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +11 -37
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +130 -83
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +67 -22
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/package.json +4 -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/crdt/doc/layout.js
CHANGED
|
@@ -8,12 +8,12 @@ import { sanitizeSpec } from '../../utils/json.js';
|
|
|
8
8
|
import { ref } from './plan.js';
|
|
9
9
|
/** The layout ops of one facade, prepared. */
|
|
10
10
|
export const layoutOps = (c) => {
|
|
11
|
-
const { roles, view, blockTypeOf, childrenIds, positionOf, ancestorsOf, isIsland, isLines, insideIsland, isLayout, isLayoutItem, holdsLayout, insideItem, live, canPlace, isContainer, fits, fitsIn, REFUSED, plan,
|
|
11
|
+
const { roles, view, blockTypeOf, childrenIds, positionOf, ancestorsOf, isIsland, isLines, insideIsland, isLayout, isLayoutItem, holdsLayout, insideItem, live, canPlace, isContainer, fits, fitsIn, REFUSED, plan, moveTo, landing, emptied } = c;
|
|
12
12
|
/** The layout kind a new layout takes: `kind` when it is one, else the only one the roles declare. */
|
|
13
13
|
const layoutKind = (kind) => {
|
|
14
14
|
if (kind !== undefined)
|
|
15
|
-
return roles.
|
|
16
|
-
const kinds =
|
|
15
|
+
return roles.of(kind).layout === undefined ? undefined : kind;
|
|
16
|
+
const kinds = roles.declared().layouts;
|
|
17
17
|
return kinds.length === 1 ? kinds[0] : undefined;
|
|
18
18
|
};
|
|
19
19
|
/** A fresh empty block of `type`. */
|
|
@@ -62,8 +62,8 @@ export const layoutOps = (c) => {
|
|
|
62
62
|
* an item, a layout or a block holding one, a block that does not
|
|
63
63
|
* fit an item, a block or a layout inside an island, and, wrapping,
|
|
64
64
|
* when there is no layout kind. The sources are cleaned in the same
|
|
65
|
-
* plan (`emptying`, `dissolving`); plain
|
|
66
|
-
*
|
|
65
|
+
* plan (`emptying`, `dissolving`); a plain move. `ids`: the moved
|
|
66
|
+
* blocks.
|
|
67
67
|
*/
|
|
68
68
|
const placeBeside = (ids, target, side, kind) => {
|
|
69
69
|
const moved = ids.map(ref);
|
|
@@ -90,7 +90,7 @@ export const layoutOps = (c) => {
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
const wrap = layout === null ? layoutKind(kind && ref(kind)) : blockTypeOf(layout);
|
|
93
|
-
const item = wrap === undefined ? undefined : roles.
|
|
93
|
+
const item = wrap === undefined ? undefined : roles.of(wrap).layout;
|
|
94
94
|
if (wrap === undefined || item === undefined)
|
|
95
95
|
return REFUSED;
|
|
96
96
|
if (moved.some((id) => !fitsIn(item, blockTypeOf(id))))
|
|
@@ -98,25 +98,20 @@ export const layoutOps = (c) => {
|
|
|
98
98
|
if (layout !== null && (isIsland(layout) || insideIsland(layout, v)))
|
|
99
99
|
return REFUSED;
|
|
100
100
|
const fresh = spec(item);
|
|
101
|
-
const into = (parent) => moveTo(moved, parent, 0
|
|
101
|
+
const into = (parent) => moveTo(moved, parent, 0);
|
|
102
102
|
let writes;
|
|
103
103
|
let kept;
|
|
104
104
|
if (layout !== null) {
|
|
105
|
-
|
|
106
|
-
writes = [
|
|
107
|
-
{ op: 'insertBlocks', parent: layout, index, specs: [fresh], ranks },
|
|
108
|
-
...into(fresh.id)
|
|
109
|
-
];
|
|
105
|
+
writes = [{ op: 'insertBlocks', parent: layout, index, specs: [fresh] }, ...into(fresh.id)];
|
|
110
106
|
kept = new Set([fresh.id, ...landing(layout)]);
|
|
111
107
|
}
|
|
112
108
|
else {
|
|
113
109
|
const pos = positionOf(at);
|
|
114
110
|
const host = spec(item);
|
|
115
111
|
const wrapper = { ...spec(wrap), children: right ? [host, fresh] : [fresh, host] };
|
|
116
|
-
const ranks = ranksFor(pos.parent, pos.index, 1);
|
|
117
112
|
writes = [
|
|
118
|
-
{ op: 'insertBlocks', parent: pos.parent, index: pos.index, specs: [wrapper]
|
|
119
|
-
...moveTo([at], host.id, 0
|
|
113
|
+
{ op: 'insertBlocks', parent: pos.parent, index: pos.index, specs: [wrapper] },
|
|
114
|
+
...moveTo([at], host.id, 0),
|
|
120
115
|
...into(fresh.id)
|
|
121
116
|
];
|
|
122
117
|
kept = new Set([wrapper.id, host.id, fresh.id, ...landing(pos.parent)]);
|
|
@@ -134,8 +129,8 @@ export const layoutOps = (c) => {
|
|
|
134
129
|
* fewer items than blocks, blocks of different parents, an item, a
|
|
135
130
|
* layout or a block holding one, where the layout does not fit
|
|
136
131
|
* (`fits`: in a list, a code block) or lands inside an item or an
|
|
137
|
-
* island, and for a block that does not fit an item.
|
|
138
|
-
*
|
|
132
|
+
* island, and for a block that does not fit an item. A plain move.
|
|
133
|
+
* `ids`: the new layout.
|
|
139
134
|
*/
|
|
140
135
|
const wrapInLayout = (ids, kind, columns = ids.length) => {
|
|
141
136
|
const blocks = ids.map(ref);
|
|
@@ -150,14 +145,14 @@ export const layoutOps = (c) => {
|
|
|
150
145
|
if (blocks.some((id) => isLayoutItem(id) || holdsLayout(id)))
|
|
151
146
|
return REFUSED;
|
|
152
147
|
const wrap = layoutKind(kind && ref(kind));
|
|
153
|
-
const item = wrap === undefined ? undefined : roles.
|
|
148
|
+
const item = wrap === undefined ? undefined : roles.of(wrap).layout;
|
|
154
149
|
if (wrap === undefined || item === undefined || !fits(parent, wrap))
|
|
155
150
|
return REFUSED;
|
|
156
151
|
if (parent !== null && (insideItem(parent, v) || isIsland(parent) || insideIsland(parent, v)))
|
|
157
152
|
return REFUSED;
|
|
158
153
|
if (blocks.some((id) => !fitsIn(item, blockTypeOf(id))))
|
|
159
154
|
return REFUSED;
|
|
160
|
-
const fill = roles.
|
|
155
|
+
const fill = roles.of(item).child;
|
|
161
156
|
if (columns > blocks.length && !fitsIn(item, fill))
|
|
162
157
|
return REFUSED;
|
|
163
158
|
const ordered = blocks.toSorted((a, b) => positionOf(a).index - positionOf(b).index);
|
|
@@ -165,14 +160,8 @@ export const layoutOps = (c) => {
|
|
|
165
160
|
const wrapper = { ...spec(wrap), children: items };
|
|
166
161
|
const at = positionOf(ordered[0]).index;
|
|
167
162
|
const writes = [
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
parent,
|
|
171
|
-
index: at,
|
|
172
|
-
specs: [wrapper],
|
|
173
|
-
ranks: ranksFor(parent, at, 1)
|
|
174
|
-
},
|
|
175
|
-
...ordered.flatMap((id, i) => moveTo([id], items[i].id, 0, ranksFor(items[i].id, 0, 1)))
|
|
163
|
+
{ op: 'insertBlocks', parent, index: at, specs: [wrapper] },
|
|
164
|
+
...ordered.flatMap((id, i) => moveTo([id], items[i].id, 0))
|
|
176
165
|
];
|
|
177
166
|
const kept = new Set([
|
|
178
167
|
wrapper.id,
|
package/dist/crdt/doc/meta.js
CHANGED
|
@@ -7,11 +7,11 @@ import { randOf } from '../rand.js';
|
|
|
7
7
|
import { TYPE } from '../schema.js';
|
|
8
8
|
import { asBlockSpec, asContentItem, sanitizeSpec, sanitizeWireJson } from '../../utils/json.js';
|
|
9
9
|
import { patchWrites, leafKey, isObject } from '../data.js';
|
|
10
|
-
import { collides
|
|
10
|
+
import { collides } from '../placement/model.js';
|
|
11
11
|
import { ref, refused, sanitizeItem } from './plan.js';
|
|
12
12
|
/** The meta ops of one facade, prepared. */
|
|
13
13
|
export const metaOps = (c) => {
|
|
14
|
-
const { doc, roles, roleOf, dataNode, view, blockTypeOf, blockDataOf, childrenIds, positionOf,
|
|
14
|
+
const { doc, roles, roleOf, dataNode, view, blockTypeOf, blockDataOf, childrenIds, positionOf, isLines, isVoid, displayLength, live, contentTarget, REFUSED, plan, attr, leavingIsland, settle, remove, atomOf, writeContent } = c;
|
|
15
15
|
/**
|
|
16
16
|
* The data step of `patches` (sanitized) on a block, one of its atoms or
|
|
17
17
|
* the document (`crdt/data.ts`): none when it changes nothing, `null`
|
|
@@ -23,12 +23,12 @@ export const metaOps = (c) => {
|
|
|
23
23
|
// A block kind's atomic paths are written as one leaf each (`data.atomic`).
|
|
24
24
|
const type = inlineId === undefined && id !== undefined ? blockTypeOf(id) : undefined;
|
|
25
25
|
const atomic = (type === undefined ? undefined : roleOf(type)?.atomic) ?? [];
|
|
26
|
-
const
|
|
26
|
+
const writes = node
|
|
27
27
|
? patchWrites(node, patches, doc.clientID, randOf(doc), atomic.map((p) => leafKey(typeof p === 'string' ? [p] : p)))
|
|
28
28
|
: [];
|
|
29
|
-
if (
|
|
29
|
+
if (writes === null)
|
|
30
30
|
return null; // a patch fits no value there
|
|
31
|
-
if (
|
|
31
|
+
if (writes.length === 0)
|
|
32
32
|
return [];
|
|
33
33
|
const offset = inlineId === undefined ? undefined : atomOf(id, inlineId)?.at;
|
|
34
34
|
return [
|
|
@@ -37,7 +37,7 @@ export const metaOps = (c) => {
|
|
|
37
37
|
...(id !== undefined && { id }),
|
|
38
38
|
...(inlineId !== undefined && { inlineId, offset }),
|
|
39
39
|
ops: patches,
|
|
40
|
-
|
|
40
|
+
writes
|
|
41
41
|
}
|
|
42
42
|
];
|
|
43
43
|
};
|
|
@@ -65,10 +65,8 @@ export const metaOps = (c) => {
|
|
|
65
65
|
/**
|
|
66
66
|
* The target role decides: nothing renders a void's children,
|
|
67
67
|
* so a block retyped to a void kind hands them to the slot right after
|
|
68
|
-
* it, as the kind they show there (`settledKind`)
|
|
69
|
-
*
|
|
70
|
-
* shedding gives it (`promotedRank`), so a child a peer adds
|
|
71
|
-
* meanwhile keeps its place in the void's order among them. An island
|
|
68
|
+
* it, as the kind they show there (`settledKind`), in their order (where
|
|
69
|
+
* the read-time shedding shows a child a peer adds meanwhile). An island
|
|
72
70
|
* declared `lines` retyped to an ordinary kind keeps its lines, each
|
|
73
71
|
* retyped to the new kind's default child (the document's where that
|
|
74
72
|
* renders no content): no line kind outside its island. Any other
|
|
@@ -82,25 +80,23 @@ export const metaOps = (c) => {
|
|
|
82
80
|
const moved = kids.get(id) ?? [];
|
|
83
81
|
// A code block retyped to an ordinary kind keeps its lines as that kind's children
|
|
84
82
|
// (`leavingIsland`: never a kind that hides their text).
|
|
85
|
-
const
|
|
86
|
-
if (
|
|
87
|
-
const to = roles.defaultChild(type);
|
|
83
|
+
const to = roles.of(type);
|
|
84
|
+
if (isLines(id) && !to.island && !to.childless) {
|
|
88
85
|
return [
|
|
89
86
|
...steps,
|
|
90
87
|
...moved.flatMap((k) => {
|
|
91
|
-
const kind = leavingIsland(id, blockTypeOf(k.id), to);
|
|
88
|
+
const kind = leavingIsland(id, blockTypeOf(k.id), to.child);
|
|
92
89
|
return kind === undefined ? [] : attr(k.id, TYPE, kind);
|
|
93
90
|
})
|
|
94
91
|
];
|
|
95
92
|
}
|
|
96
|
-
if (!
|
|
93
|
+
if (!to.childless || moved.length === 0 || pos === null)
|
|
97
94
|
return steps;
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
// The children keep their home slots: the read shows them in the void's
|
|
96
|
+
// slot (`conc.void-children`), settled for its parent.
|
|
100
97
|
return [
|
|
101
98
|
...steps,
|
|
102
|
-
...
|
|
103
|
-
...settle(id, ids, pos.parent)
|
|
99
|
+
...settle(id, moved.map((k) => k.id), pos.parent)
|
|
104
100
|
];
|
|
105
101
|
};
|
|
106
102
|
/** Set the block type (attr write — the block keeps its identity; see `retypeSteps`). */
|
|
@@ -162,7 +158,7 @@ export const metaOps = (c) => {
|
|
|
162
158
|
if (!live(id) || (content !== undefined && !contentTarget(id)))
|
|
163
159
|
return REFUSED;
|
|
164
160
|
const type = value.type === undefined ? undefined : ref(value.type);
|
|
165
|
-
const toVoid = type === undefined ? isVoid(id) : roles.
|
|
161
|
+
const toVoid = type === undefined ? isVoid(id) : roles.of(type).childless;
|
|
166
162
|
if (children?.length && toVoid)
|
|
167
163
|
return REFUSED;
|
|
168
164
|
if (children !== undefined && collides(doc, children))
|
|
@@ -187,10 +183,8 @@ export const metaOps = (c) => {
|
|
|
187
183
|
if (children !== undefined) {
|
|
188
184
|
for (const kid of childrenIds(id))
|
|
189
185
|
writes.push(remove(kid));
|
|
190
|
-
// Every current child is deleted, so the new ones rank from an empty list.
|
|
191
|
-
const ranks = ranksAt([], 0, children.length, doc.clientID, randOf(doc));
|
|
192
186
|
if (children.length > 0)
|
|
193
|
-
writes.push({ op: 'insertBlocks', parent: id, index: 0, specs: children
|
|
187
|
+
writes.push({ op: 'insertBlocks', parent: id, index: 0, specs: children });
|
|
194
188
|
}
|
|
195
189
|
return plan([id], writes);
|
|
196
190
|
};
|
package/dist/crdt/doc/moves.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type JSONBlock } from '../../utils/json.js';
|
|
2
2
|
import { type BlockId, type BlockSpec, type Destination } from '../placement/model.js';
|
|
3
3
|
import type { Prepared } from './types.js';
|
|
4
|
-
import type
|
|
4
|
+
import { type OpsContext } from './steps.js';
|
|
5
5
|
/** The moves ops of one facade, prepared. */
|
|
6
6
|
export declare const moveOps: (c: OpsContext) => {
|
|
7
7
|
insertBlocks: (dest: Destination, specs: readonly (BlockSpec | JSONBlock)[]) => Prepared;
|
package/dist/crdt/doc/moves.js
CHANGED
|
@@ -7,9 +7,10 @@ import { id as newId } from '../../utils.js';
|
|
|
7
7
|
import { asBlockSpec, sanitizeSpec } from '../../utils/json.js';
|
|
8
8
|
import { collides } from '../placement/model.js';
|
|
9
9
|
import { ref } from './plan.js';
|
|
10
|
+
import { PART } from './steps.js';
|
|
10
11
|
/** The moves ops of one facade, prepared. */
|
|
11
12
|
export const moveOps = (c) => {
|
|
12
|
-
const { doc, blockTypeOf, blockDataOf, childrenIds, positionOf, isVoid, isIsland, isLine, contentItems, live, canPlace, isContainer, fits, kindToCopy, REFUSED,
|
|
13
|
+
const { doc, blockTypeOf, blockDataOf, childrenIds, positionOf, isVoid, isIsland, isLine, contentItems, live, canPlace, isContainer, fits, kindToCopy, REFUSED, plan, moveTo, move, leaving, settledKind, settle, landing, emptying, emptied } = c;
|
|
13
14
|
/**
|
|
14
15
|
* Insert blocks (specs may carry children/content/data; the batch is
|
|
15
16
|
* all-or-nothing). A spec is a `BlockSpec` or the `JSONBlock` that
|
|
@@ -29,8 +30,7 @@ export const moveOps = (c) => {
|
|
|
29
30
|
return plan([], []);
|
|
30
31
|
if ((parent !== null && !live(parent)) || collides(doc, clean))
|
|
31
32
|
return REFUSED;
|
|
32
|
-
|
|
33
|
-
return plan(clean.map((s) => s.id), [{ op: 'insertBlocks', parent, index: dest.index, specs: clean, ranks }]);
|
|
33
|
+
return plan(clean.map((s) => s.id), [{ op: 'insertBlocks', parent, index: dest.index, specs: clean }]);
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
36
|
* Grouped move — one step, per-member conflict resolution; refused
|
|
@@ -80,8 +80,7 @@ export const moveOps = (c) => {
|
|
|
80
80
|
const retype = settle(from, moved, ppos.parent);
|
|
81
81
|
if (isContainer(from))
|
|
82
82
|
return splitOut(moved, [from], [], false, retype);
|
|
83
|
-
const
|
|
84
|
-
const writes = [...moveTo(moved, ppos.parent, ppos.index + 1, out), ...retype];
|
|
83
|
+
const writes = [...moveTo(moved, ppos.parent, ppos.index + 1, leaving(true)), ...retype];
|
|
85
84
|
const adopts = after.length > 0 &&
|
|
86
85
|
!isVoid(last) &&
|
|
87
86
|
!isIsland(last) &&
|
|
@@ -123,13 +122,11 @@ export const moveOps = (c) => {
|
|
|
123
122
|
const splitOut = (ids, levels, specs, keep, retype = []) => {
|
|
124
123
|
const moved = keep ? [] : [...ids];
|
|
125
124
|
const placed = [...moved, ...specs.map((s) => s.id)];
|
|
126
|
-
const insert = (at, index, s
|
|
125
|
+
const insert = (at, index, s) => s.length ? [{ op: 'insertBlocks', parent: at, index, specs: s }] : [];
|
|
127
126
|
const last = ids.at(-1);
|
|
128
127
|
const { parent, index } = positionOf(levels.at(-1) ?? last);
|
|
129
|
-
if (levels.length === 0)
|
|
130
|
-
|
|
131
|
-
return plan(placed, [...insert(parent, index + 1, specs, ranks), ...retype]);
|
|
132
|
-
}
|
|
128
|
+
if (levels.length === 0)
|
|
129
|
+
return plan(placed, [...insert(parent, index + 1, specs), ...retype]);
|
|
133
130
|
// Bottom up: what stays before the split at each level (the level
|
|
134
131
|
// itself, or a new head holding it), and whether anything follows it.
|
|
135
132
|
const heads = [];
|
|
@@ -141,10 +138,8 @@ export const moveOps = (c) => {
|
|
|
141
138
|
for (const level of levels) {
|
|
142
139
|
const kids = childrenIds(level);
|
|
143
140
|
const at = kids.indexOf(child);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
...(before && !before.head ? [before.id] : [])
|
|
147
|
-
];
|
|
141
|
+
const stays = (keep && child === last) || (before !== null && !before.head);
|
|
142
|
+
const lead = kids.slice(0, at + (stays ? 1 : 0)).filter((k) => !moved.includes(k));
|
|
148
143
|
if (!follows && at + 1 < kids.length)
|
|
149
144
|
[follows, next] = [true, kids[at + 1]];
|
|
150
145
|
if ((lead.length > 0 || before?.head) && follows) {
|
|
@@ -160,27 +155,52 @@ export const moveOps = (c) => {
|
|
|
160
155
|
before = lead.length > 0 ? { id: level } : null;
|
|
161
156
|
child = level;
|
|
162
157
|
}
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
158
|
+
// Written in one gap, each right after the block before it, as an
|
|
159
|
+
// after-record out of the level's slot or a before-record into the gap
|
|
160
|
+
// before it (`leaving`): the engine's sort decides their order against
|
|
161
|
+
// a peer's records there (fork patch YP22, `sourceOrder`: the block a
|
|
162
|
+
// record stands at, then its part), so a peer's heads, pieces, lifts
|
|
163
|
+
// and new blocks in the same gap keep the serial order whatever the
|
|
164
|
+
// client ids (`del.merge.container`). The head stands at the first block
|
|
165
|
+
// (with `keep`, at it: `with`), the blocks at themselves, the new
|
|
166
|
+
// blocks after the last (`after`) — or, kept before the block that
|
|
167
|
+
// follows, at that block (`kept`). The records are written last first,
|
|
168
|
+
// so each stands before the one written before it, in the order the
|
|
169
|
+
// sort gives them; the head and the new blocks are created in the
|
|
170
|
+
// innermost level right after the block they stand at (their home,
|
|
171
|
+
// hidden while their records show them) and moved out by them.
|
|
168
172
|
const outside = before !== null && !before.head;
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
const side = leaving(outside);
|
|
174
|
+
const gap = index + (outside ? 1 : 0);
|
|
175
|
+
const at = moved[0] ?? last;
|
|
176
|
+
const kids0 = childrenIds(levels[0]);
|
|
177
|
+
const home = kids0.indexOf(last) + 1;
|
|
178
|
+
const specIds = specs.map((sp) => sp.id);
|
|
179
|
+
const [stands, part] = keep && !outside ? [next, PART.kept] : [last, PART.after];
|
|
180
|
+
// One record per run of adjacent blocks, each standing at its first: a
|
|
181
|
+
// block a peer's move takes out of a run leaves the others at their own place.
|
|
182
|
+
const runs = [];
|
|
183
|
+
for (const id of moved) {
|
|
184
|
+
const run = runs.at(-1);
|
|
185
|
+
if (run !== undefined && kids0.indexOf(run.at(-1)) + 1 === kids0.indexOf(id))
|
|
186
|
+
run.push(id);
|
|
187
|
+
else
|
|
188
|
+
runs.push([id]);
|
|
189
|
+
}
|
|
190
|
+
const writes = [
|
|
191
|
+
...insert(levels[0], home, specs),
|
|
192
|
+
...(before?.head ? insert(levels[0], home, [before.head]) : []),
|
|
193
|
+
...moveTo(specIds, parent, gap, side, stands, part),
|
|
194
|
+
...runs.reverse().flatMap((run) => moveTo(run, parent, gap, side, run[0], PART.self)),
|
|
195
|
+
...(before?.head
|
|
196
|
+
? moveTo([before.head.id], parent, gap, side, at, keep ? PART.with : PART.head)
|
|
197
|
+
: [])
|
|
198
|
+
];
|
|
178
199
|
for (const { spec, kids, inner } of heads.reverse()) {
|
|
179
|
-
|
|
180
|
-
writes.push(...moveTo(kids, spec.id, 0, inside.slice(0, kids.length)), ...(inner ? insert(spec.id, kids.length, [inner], inside.slice(-1)) : []));
|
|
200
|
+
writes.push(...moveTo(kids, spec.id, 0), ...(inner ? insert(spec.id, kids.length, [inner]) : []));
|
|
181
201
|
}
|
|
182
|
-
|
|
183
|
-
|
|
202
|
+
writes.push(...retype);
|
|
203
|
+
void next;
|
|
184
204
|
return plan(placed, emptying(levels[0], moved, writes, landing(parent)));
|
|
185
205
|
};
|
|
186
206
|
/**
|
package/dist/crdt/doc/reads.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EngineApi, EngineDoc, EngineNode } from '../engine-api.js';
|
|
2
2
|
import { type BlockId, type ContentItem, type Destination, type ModelView } from '../placement/model.js';
|
|
3
|
-
import type { DisplayRoles, RunView } from '../text/runs.js';
|
|
3
|
+
import type { DisplayRoles, KindRole, RunView } from '../text/runs.js';
|
|
4
4
|
import { type DataPatch } from '../data.js';
|
|
5
5
|
import { type JSONBlock } from '../../utils/json.js';
|
|
6
6
|
import type { BlockRole, DataTarget, JsonObj, OrderPolicy } from './types.js';
|
|
@@ -40,7 +40,7 @@ export declare const docReads: (c: DocBase) => {
|
|
|
40
40
|
parent: BlockId | null;
|
|
41
41
|
rank: string;
|
|
42
42
|
} | null;
|
|
43
|
-
|
|
43
|
+
kindOf: (id: BlockId) => KindRole | undefined;
|
|
44
44
|
isVoid: (id: BlockId) => boolean;
|
|
45
45
|
isIsland: (id: BlockId) => boolean;
|
|
46
46
|
isLines: (id: BlockId) => boolean;
|
package/dist/crdt/doc/reads.js
CHANGED
|
@@ -121,15 +121,15 @@ export const docReads = (c) => {
|
|
|
121
121
|
return slot.parent === DEAD ? pl : slot;
|
|
122
122
|
};
|
|
123
123
|
// ── roles (island/void) ───────────────────────────────────────────
|
|
124
|
-
/** The role
|
|
125
|
-
const
|
|
124
|
+
/** The role of `id`'s shown kind (`undefined` without one). */
|
|
125
|
+
const kindOf = (id) => {
|
|
126
126
|
const t = blockTypeOf(id);
|
|
127
|
-
return t
|
|
127
|
+
return t === undefined ? undefined : roles.of(t);
|
|
128
128
|
};
|
|
129
|
-
const isVoid = (id) =>
|
|
130
|
-
const isIsland = (id) =>
|
|
129
|
+
const isVoid = (id) => kindOf(id)?.childless === true;
|
|
130
|
+
const isIsland = (id) => kindOf(id)?.island === true;
|
|
131
131
|
/** An island that holds only lines (a code block: its role says `lines`). */
|
|
132
|
-
const isLines = (id) =>
|
|
132
|
+
const isLines = (id) => kindOf(id)?.line !== undefined;
|
|
133
133
|
/** Nearest island-typed display ancestor of `id`, or null. */
|
|
134
134
|
const islandOf = (id, v = view()) => ancestorsOf(id, v).find((a) => isIsland(a)) ?? null;
|
|
135
135
|
/** True iff `id` sits strictly inside an island subtree. */
|
|
@@ -139,45 +139,35 @@ export const docReads = (c) => {
|
|
|
139
139
|
const parent = positionOf(id)?.parent;
|
|
140
140
|
return parent != null && isLines(parent);
|
|
141
141
|
};
|
|
142
|
-
/**
|
|
143
|
-
const
|
|
144
|
-
const type = blockTypeOf(id);
|
|
145
|
-
return type === undefined ? undefined : roles[role](type);
|
|
146
|
-
};
|
|
147
|
-
/** `id` is of its parent's `kindOf` kind, directly in it (a column, a row). */
|
|
148
|
-
const childOf = (kindOf) => (id) => {
|
|
142
|
+
/** `id` is of its parent's `kind` kind, directly in it (a column, a row, a cell). */
|
|
143
|
+
const childOf = (kind) => (id) => {
|
|
149
144
|
const parent = positionOf(id)?.parent;
|
|
150
|
-
|
|
145
|
+
const k = parent == null ? undefined : kindOf(parent);
|
|
146
|
+
return parent != null && blockTypeOf(id) === (k && kind(k));
|
|
151
147
|
};
|
|
152
148
|
/** The item kind of `id` when it is a layout (its role says `layout`): its default child. */
|
|
153
|
-
const itemKindOf =
|
|
149
|
+
const itemKindOf = (id) => kindOf(id)?.layout;
|
|
154
150
|
/** `id` is a layout (`layout.*`). */
|
|
155
151
|
const isLayout = (id) => itemKindOf(id) !== undefined;
|
|
156
152
|
/** `id` is a layout item: of its layout's item kind, directly in it (a column). */
|
|
157
|
-
const isLayoutItem = childOf(
|
|
153
|
+
const isLayoutItem = childOf((k) => k.layout);
|
|
158
154
|
/** `id` is a layout or holds one in its shown subtree (`layout.nest`). */
|
|
159
155
|
const holdsLayout = (id) => isLayout(id) || childrenIds(id).some(holdsLayout);
|
|
160
156
|
/** `id` is a layout item or sits inside one. */
|
|
161
157
|
const insideItem = (id, v) => [id, ...ancestorsOf(id, v)].some(isLayoutItem);
|
|
162
158
|
// ── tables (`table.*`) ────────────────────────────────────────────
|
|
163
|
-
/** The row kind of `id` when it is a table (its role says `table`): its default child. */
|
|
164
|
-
const rowKindOf = childKindOf('table');
|
|
165
159
|
/** `id` is a table (`table.*`). */
|
|
166
|
-
const isTable = (id) =>
|
|
160
|
+
const isTable = (id) => kindOf(id)?.table !== undefined;
|
|
167
161
|
/** `type` is a row kind of a table kind the roles declare (`tableRow`). */
|
|
168
|
-
const isRowKind = (type) => type !== undefined &&
|
|
162
|
+
const isRowKind = (type) => type !== undefined && roles.declared().rows.has(type);
|
|
169
163
|
/** `type` is a cell kind: the default child of a row kind (`tableCell`). */
|
|
170
|
-
const isCellKind = (type) => type !== undefined &&
|
|
171
|
-
[...roles.tableKinds()].some((t) => {
|
|
172
|
-
const row = roles.table(t);
|
|
173
|
-
return row !== undefined && roles.defaultChild(row) === type;
|
|
174
|
-
});
|
|
164
|
+
const isCellKind = (type) => type !== undefined && roles.declared().cells.has(type);
|
|
175
165
|
/** `id` is a table's row: of its table's row kind, directly in it. */
|
|
176
|
-
const isTableRow = childOf(
|
|
166
|
+
const isTableRow = childOf((k) => k.table);
|
|
177
167
|
/** `id` is a table's cell: of its row's cell kind, directly in a row of a table. */
|
|
178
168
|
const isTableCell = (id) => {
|
|
179
169
|
const row = positionOf(id)?.parent;
|
|
180
|
-
return
|
|
170
|
+
return row != null && isTableRow(row) && blockTypeOf(id) === kindOf(row).child;
|
|
181
171
|
};
|
|
182
172
|
/** The table `id` is, or whose row or cell it is; `null` for any other block. */
|
|
183
173
|
const tableOf = (id) => {
|
|
@@ -301,8 +291,8 @@ export const docReads = (c) => {
|
|
|
301
291
|
const hasBlock = (id) => blockNodeOf(doc, id) !== null;
|
|
302
292
|
/** The one liveness answer (`isLive`): `id` renders in `project()`. O(depth). */
|
|
303
293
|
const live = (id) => isLive(doc, id);
|
|
304
|
-
/** A live block that can hold content
|
|
305
|
-
const contentTarget = (id) => live(id) && view().blocks.
|
|
294
|
+
/** A live block that can hold content (a streamless block gets its own text on first write). */
|
|
295
|
+
const contentTarget = (id) => live(id) && view().blocks.has(id);
|
|
306
296
|
return {
|
|
307
297
|
dataNode,
|
|
308
298
|
blockJSON,
|
|
@@ -317,7 +307,7 @@ export const docReads = (c) => {
|
|
|
317
307
|
pathOf,
|
|
318
308
|
ancestorsOf,
|
|
319
309
|
slotOf,
|
|
320
|
-
|
|
310
|
+
kindOf,
|
|
321
311
|
isVoid,
|
|
322
312
|
isIsland,
|
|
323
313
|
isLines,
|
package/dist/crdt/doc/seed.js
CHANGED
|
@@ -3,7 +3,7 @@ import { hash32, setDocRand } from '../rand.js';
|
|
|
3
3
|
import { keepingReplaced } from '../incarnations.js';
|
|
4
4
|
import { asEngineDoc, asYDoc } from '../structs.js';
|
|
5
5
|
import { DOC_DATA_ROOT, SCHEMA } from '../schema.js';
|
|
6
|
-
import {
|
|
6
|
+
import { dataWrites, isObject, writeData } from '../data.js';
|
|
7
7
|
import { jsonBlockToSpec, sanitizeSpec, sanitizeWireJson, sanitizeWireString } from '../../utils/json.js';
|
|
8
8
|
import { isInitialized, META_KEY, registryEmpty, SCHEMA_NAME, SCHEMA_VERSION } from './gate.js';
|
|
9
9
|
/** Origin of the seed update's apply — non-local, like any integrated update. */
|
|
@@ -100,7 +100,7 @@ export const bindSeed = (Y, BA) => {
|
|
|
100
100
|
};
|
|
101
101
|
scratch.transact(() => {
|
|
102
102
|
specs.forEach(records);
|
|
103
|
-
|
|
103
|
+
writeData(scratch.get(DOC_DATA_ROOT), dataWrites(own));
|
|
104
104
|
});
|
|
105
105
|
return Y.encodeStateAsUpdate(raw);
|
|
106
106
|
};
|
|
@@ -11,7 +11,7 @@ import { blockNodeOf, isSelfOrDescendant } from '../placement/model.js';
|
|
|
11
11
|
import { ref } from './plan.js';
|
|
12
12
|
/** The split-merge ops of one facade, prepared. */
|
|
13
13
|
export const splitMergeOps = (c) => {
|
|
14
|
-
const { doc, view, childrenIds, positionOf, isVoid, isCellKind, blockTypeOf, insideIsland, isLayoutItem, next, previous, displayLength, contentTarget, isContainer, fits, canMerge, rendersContent, kindToCopy, REFUSED, plan,
|
|
14
|
+
const { doc, view, childrenIds, positionOf, isVoid, isCellKind, blockTypeOf, insideIsland, isLayoutItem, next, previous, displayLength, contentTarget, isContainer, fits, canMerge, rendersContent, kindToCopy, REFUSED, plan, moveTo, move, leaving, settledKind, settle, landing, emptying, emptied, merge, clamp } = c;
|
|
15
15
|
/**
|
|
16
16
|
* Split `id` at content `offset` into a new sibling `newId` (one boundary
|
|
17
17
|
* item and the claims that follow it, no text copied; children follow).
|
|
@@ -26,7 +26,7 @@ export const splitMergeOps = (c) => {
|
|
|
26
26
|
const born = ref(newId);
|
|
27
27
|
const pos = positionOf(id);
|
|
28
28
|
const rec = view().blocks.get(id);
|
|
29
|
-
if (pos === null || isVoid(id) || !rendersContent(id) ||
|
|
29
|
+
if (pos === null || isVoid(id) || !rendersContent(id) || rec === undefined)
|
|
30
30
|
return REFUSED;
|
|
31
31
|
// A table's cell holds its lines as line breaks: it never splits (`table.split`).
|
|
32
32
|
if (isCellKind(blockTypeOf(id)))
|
|
@@ -39,7 +39,6 @@ export const splitMergeOps = (c) => {
|
|
|
39
39
|
const t = tail
|
|
40
40
|
? { type, data: tail.data && sanitizeWireJson(tail.data) }
|
|
41
41
|
: { type, data: readData(rec.node) };
|
|
42
|
-
const [rank] = pieceRanks(id, at, 1);
|
|
43
42
|
const length = displayLength(id) - at;
|
|
44
43
|
const split = {
|
|
45
44
|
op: 'splitBlock',
|
|
@@ -49,7 +48,7 @@ export const splitMergeOps = (c) => {
|
|
|
49
48
|
newId: born,
|
|
50
49
|
tail: t,
|
|
51
50
|
parent: pos.parent,
|
|
52
|
-
|
|
51
|
+
index: pos.index + 1
|
|
53
52
|
};
|
|
54
53
|
return plan([born], [split, ...move(childrenIds(id), born, 0)]);
|
|
55
54
|
};
|
|
@@ -58,11 +57,11 @@ export const splitMergeOps = (c) => {
|
|
|
58
57
|
* children ADOPTED as `into`'s last children — an island's take
|
|
59
58
|
* `into`'s default child, like a baseline merge's — and `from` is
|
|
60
59
|
* hidden via the claim (undo restores it). The children stay under
|
|
61
|
-
* `from
|
|
62
|
-
*
|
|
63
|
-
* claim and `from` comes back with its children,
|
|
64
|
-
* Role rules are `canMerge`'s; a merge that would
|
|
65
|
-
* cycle is refused. `ids`: `into`.
|
|
60
|
+
* `from` and display under `into` through the claim, after `into`'s
|
|
61
|
+
* own children (the display's claim order): a concurrent delete of
|
|
62
|
+
* `into` voids the claim and `from` comes back with its children,
|
|
63
|
+
* never below them. Role rules are `canMerge`'s; a merge that would
|
|
64
|
+
* close a display cycle is refused. `ids`: `into`.
|
|
66
65
|
*/
|
|
67
66
|
const mergeBlocks = (fromId, intoId) => {
|
|
68
67
|
const from = ref(fromId);
|
|
@@ -74,9 +73,8 @@ export const splitMergeOps = (c) => {
|
|
|
74
73
|
return REFUSED;
|
|
75
74
|
const kids = childrenIds(from);
|
|
76
75
|
const retype = settle(from, kids, into);
|
|
77
|
-
const adopt = moveTo(kids, from, kids.length, ranksFor(into, Infinity, kids.length, kids));
|
|
78
76
|
// A list its only item leaves goes, as with every sibling op.
|
|
79
|
-
return plan([into], emptied([from], [merge(from, into), ...
|
|
77
|
+
return plan([into], emptied([from], [merge(from, into), ...retype], landing(into)));
|
|
80
78
|
};
|
|
81
79
|
/**
|
|
82
80
|
* Baseline-shaped merge (both `mergeBackward` and `mergeForward`):
|
|
@@ -84,7 +82,7 @@ export const splitMergeOps = (c) => {
|
|
|
84
82
|
* NOT adopted into `into` — and take the kind they show there
|
|
85
83
|
* (`settledKind`: an island's children its parent's default child, a
|
|
86
84
|
* paragraph in a list its item); then `from`'s content claims
|
|
87
|
-
* into `into`.
|
|
85
|
+
* into `into`. Placed after `from`, not at it: a concurrent delete of
|
|
88
86
|
* `into` revives `from` above its former children.
|
|
89
87
|
*/
|
|
90
88
|
const mergeUnnesting = (from, into) => {
|
|
@@ -93,8 +91,7 @@ export const splitMergeOps = (c) => {
|
|
|
93
91
|
return REFUSED;
|
|
94
92
|
const kids = childrenIds(from);
|
|
95
93
|
const retype = settle(from, kids, pos.parent);
|
|
96
|
-
|
|
97
|
-
return plan([into], [...moveTo(kids, pos.parent, pos.index + 1, out), ...retype, merge(from, into)]);
|
|
94
|
+
return plan([into], [...moveTo(kids, pos.parent, pos.index + 1, leaving(true)), ...retype, merge(from, into)]);
|
|
98
95
|
};
|
|
99
96
|
/** Remove `container`, left empty, for the key at `id` (refused if it holds text). */
|
|
100
97
|
const gone = (id, container) => {
|
|
@@ -143,7 +140,7 @@ export const splitMergeOps = (c) => {
|
|
|
143
140
|
if (!fits(slot.parent, settledKind(prev, id, slot.parent)))
|
|
144
141
|
return REFUSED;
|
|
145
142
|
const lift = [
|
|
146
|
-
...moveTo([id], slot.parent, slot.index, leaving(
|
|
143
|
+
...moveTo([id], slot.parent, slot.index, leaving(false)),
|
|
147
144
|
...settle(prev, [id], slot.parent)
|
|
148
145
|
];
|
|
149
146
|
return plan([id], emptying(prev, [id], lift, landing(slot.parent)));
|