edytor 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cloudflare/DocumentRoom.d.ts +13 -9
- package/dist/cloudflare/DocumentRoom.js +10 -6
- package/dist/cloudflare/room/context.d.ts +27 -26
- package/dist/cloudflare/room/context.js +2 -2
- package/dist/cloudflare/room/purge.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +44 -19
- package/dist/cloudflare/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- package/dist/crdt/attribution/attribution.d.ts +31 -12
- package/dist/crdt/attribution/attribution.js +56 -18
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.js +29 -24
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +25 -9
- package/dist/crdt/doc/history.js +150 -40
- package/dist/crdt/doc/layout.js +11 -22
- package/dist/crdt/doc/meta.js +12 -17
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.js +2 -2
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +52 -122
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +16 -5
- package/dist/crdt/document.js +21 -4
- package/dist/crdt/edytor-doc.d.ts +62 -64
- package/dist/crdt/edytor-doc.js +18 -13
- package/dist/crdt/engine-api.d.ts +33 -1
- package/dist/crdt/engine.d.ts +0 -1
- package/dist/crdt/engine.js +1 -4
- package/dist/crdt/flow.d.ts +1 -1
- package/dist/crdt/flow.js +25 -27
- package/dist/crdt/index.d.ts +26 -25
- package/dist/crdt/index.js +1 -1
- package/dist/crdt/placement/display.d.ts +5 -30
- package/dist/crdt/placement/display.js +39 -144
- package/dist/crdt/placement/keys.d.ts +58 -0
- package/dist/crdt/placement/keys.js +90 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +187 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.js +5 -3
- package/dist/crdt/purge.d.ts +17 -8
- package/dist/crdt/purge.js +236 -126
- package/dist/crdt/restore.d.ts +2 -2
- package/dist/crdt/restore.js +18 -34
- package/dist/crdt/schema.d.ts +22 -21
- package/dist/crdt/schema.js +29 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +15 -15
- package/dist/crdt/structs.js +8 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +12 -0
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.js +5 -8
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +404 -138
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +12 -3
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +77 -62
- package/dist/crdt/text/model.js +84 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +17 -12
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
- package/dist/edytor.svelte.js +4 -1
- package/dist/events/beforeInputCommands.js +2 -2
- package/dist/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +25 -25
- package/dist/surface/observer.svelte.js +2 -2
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +59 -8
- package/package.json +2 -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/engine.js
CHANGED
|
@@ -37,16 +37,13 @@ export const Y = {
|
|
|
37
37
|
createIdSet: V.createIdSet,
|
|
38
38
|
diffIdSet: V.diffIdSet,
|
|
39
39
|
insertIntoIdSet: V.insertIntoIdSet,
|
|
40
|
-
//
|
|
40
|
+
// the document's undo manager (`doc/history.ts`, fork patch YP18)
|
|
41
41
|
iterateStructsByIdSet: V.iterateStructsByIdSet,
|
|
42
42
|
getItemCleanStart: V.getItemCleanStart,
|
|
43
43
|
redoItem: V.redoItem,
|
|
44
|
-
// the document's undo manager (`doc/history.ts`, fork patch YP18)
|
|
45
44
|
keepItem: V.keepItem,
|
|
46
45
|
followRedone: V.followRedone,
|
|
47
46
|
isParentOf: V.isParentOf,
|
|
48
47
|
mergeIdSets: V.mergeIdSets,
|
|
49
|
-
// a folded delete record appended after the one it replaces
|
|
50
|
-
ContentAny: V.ContentAny,
|
|
51
48
|
createID: V.createID
|
|
52
49
|
};
|
package/dist/crdt/flow.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - a `whole` flow (a block-selection copy) goes after the block, replacing
|
|
22
22
|
* it when empty (`flow.whole`); over selected blocks the lines take their
|
|
23
23
|
* slot (`flow.slot`), and at a slot they are placed with nothing replaced
|
|
24
|
-
* (`flow.place`: an accepted suggestion),
|
|
24
|
+
* (`flow.place`: an accepted suggestion), as plain inserts (`insertBlocks`: their order
|
|
25
25
|
* against a peer's split beside them is not claimed); a void
|
|
26
26
|
* takes one run (`flow.void`);
|
|
27
27
|
* - a table's cell is never split: it takes the lines a flow shows, nested
|
package/dist/crdt/flow.js
CHANGED
|
@@ -94,7 +94,9 @@ export const flowOps = (c) => {
|
|
|
94
94
|
const placed = specs(dest.parent);
|
|
95
95
|
const p = c.insertBlocks(dest, placed);
|
|
96
96
|
const at = endOf(placed.at(-1)) ?? { block: last().id, offset: lengthOf(last()) };
|
|
97
|
-
|
|
97
|
+
// The lines are placed while the replaced blocks still show (each step reads the
|
|
98
|
+
// display as it stands), then those go.
|
|
99
|
+
return 'writes' in p ? { ...c.plan(p.ids, [...p.writes, ...pre]), at } : p;
|
|
98
100
|
};
|
|
99
101
|
if ('replace' in target || 'slot' in target) {
|
|
100
102
|
let slot;
|
|
@@ -188,55 +190,53 @@ export const flowOps = (c) => {
|
|
|
188
190
|
return c.REFUSED;
|
|
189
191
|
const writes = [];
|
|
190
192
|
/** A joined line's children lead the block it joins; `moved` follow them. */
|
|
191
|
-
const kids = (to, l, moved = []
|
|
193
|
+
const kids = (to, l, moved = []) => {
|
|
192
194
|
const k = l?.children?.length ?? 0;
|
|
193
195
|
if (k)
|
|
194
|
-
writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children
|
|
196
|
+
writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children });
|
|
195
197
|
if (moved.length)
|
|
196
|
-
writes.push({
|
|
197
|
-
op: 'moveBlocks',
|
|
198
|
-
ids: moved,
|
|
199
|
-
parent: to,
|
|
200
|
-
index: k,
|
|
201
|
-
ranks: ranks.slice(k)
|
|
202
|
-
});
|
|
198
|
+
writes.push({ op: 'moveBlocks', ids: moved, parent: to, index: k });
|
|
203
199
|
};
|
|
204
200
|
/**
|
|
205
201
|
* `l` joins `B` at `at`: its content, its kind when `B` shows no text (a header
|
|
206
202
|
* keeps its own, as Enter does), its children first.
|
|
207
203
|
*/
|
|
208
|
-
const join = (l, at
|
|
204
|
+
const join = (l, at) => {
|
|
209
205
|
const end = c.writeContent(writes, B, at, l.content);
|
|
210
206
|
const kind = l.type && fit(parent, l.type);
|
|
211
207
|
if (len === 0 && !header && kind && kind === l.type)
|
|
212
208
|
writes.push(...c.attr(B, TYPE, kind), ...(c.dataSteps(B, c.replaceData(l.data ?? {})) ?? []));
|
|
213
209
|
if (!shut)
|
|
214
|
-
kids(B, l
|
|
210
|
+
kids(B, l);
|
|
215
211
|
return end;
|
|
216
212
|
};
|
|
217
|
-
/** The placed lines at `at` under `home
|
|
218
|
-
const place = (at,
|
|
213
|
+
/** The placed lines at `at` under `home` (`split`: the tail of the split they come with). */
|
|
214
|
+
const place = (at, split) => {
|
|
219
215
|
if (placed.length)
|
|
220
|
-
writes.push({
|
|
216
|
+
writes.push({
|
|
217
|
+
op: 'insertBlocks',
|
|
218
|
+
parent: home,
|
|
219
|
+
index: at,
|
|
220
|
+
specs: placed,
|
|
221
|
+
...(split !== undefined && { split })
|
|
222
|
+
});
|
|
221
223
|
};
|
|
222
224
|
// Under a header, the first line's children, the placed lines and the rest, in order.
|
|
223
225
|
const k = joinsHead ? (first.children?.length ?? 0) : 0;
|
|
224
|
-
const
|
|
225
|
-
const slot = inside ? 0 : index + 1;
|
|
226
|
+
const slot = inside ? k : index + 1;
|
|
226
227
|
// Where the placed lines end the caret: after their last shown line (`flow.apart`).
|
|
227
228
|
const end = placed.length ? endOf(placed.at(-1)) : undefined;
|
|
228
229
|
// Nothing before the position and the first line stands apart: the lines go before `B`,
|
|
229
230
|
// which keeps its text and takes a joining last line, or goes when empty and not needed.
|
|
230
231
|
if (!joinsHead && o === 0 && !inside) {
|
|
231
|
-
place(index
|
|
232
|
+
place(index);
|
|
232
233
|
if (joinsTail) {
|
|
233
234
|
const at = join(last(), 0);
|
|
234
235
|
// A closed header's taken line's nested lines follow it, the caret ending them.
|
|
235
236
|
const after = shut ? specs(parent, last().children ?? []) : [];
|
|
236
237
|
if (after.length) {
|
|
237
|
-
const ranks = c.ranksFor(parent, index + 1, after.length, [], true);
|
|
238
238
|
const slot = index + placed.length + 1;
|
|
239
|
-
writes.push({ op: 'insertBlocks', parent, index: slot, specs: after
|
|
239
|
+
writes.push({ op: 'insertBlocks', parent, index: slot, specs: after });
|
|
240
240
|
}
|
|
241
241
|
const caret = after.length ? endOf(after.at(-1)) : undefined;
|
|
242
242
|
const touched = [...ids, B, ...after.map((s) => s.id)];
|
|
@@ -246,13 +246,13 @@ export const flowOps = (c) => {
|
|
|
246
246
|
return { ...c.plan(ids, [...writes, c.remove(B, [])]), at: end };
|
|
247
247
|
return { ...c.plan([...ids, B], writes), at: end ?? { block: B, offset: 0 } };
|
|
248
248
|
}
|
|
249
|
-
const head = joinsHead ? join(first, o
|
|
249
|
+
const head = joinsHead ? join(first, o) : o;
|
|
250
250
|
if (lines.length === 1 && joinsHead)
|
|
251
251
|
return { ...c.plan([B], writes), at: { block: B, offset: head } };
|
|
252
252
|
const moved = inside ? [] : c.childrenIds(B).filter((id) => !view.hidden?.(id));
|
|
253
253
|
// Nothing after the position to keep, and the caret has a line: no split.
|
|
254
254
|
if (!joinsTail && o === len && moved.length === 0 && end) {
|
|
255
|
-
place(slot
|
|
255
|
+
place(slot);
|
|
256
256
|
return { ...c.plan([B, ...ids], writes), at: end };
|
|
257
257
|
}
|
|
258
258
|
// The rest of `B` stays in a shown line: the last line's block when it joins, else
|
|
@@ -266,9 +266,7 @@ export const flowOps = (c) => {
|
|
|
266
266
|
: bare || inside
|
|
267
267
|
? { type: c.defaultChild(home), data: {} }
|
|
268
268
|
: { type: c.kindToCopy(B), data: c.blockDataOf(B) };
|
|
269
|
-
//
|
|
270
|
-
const ranks = inside ? lead.slice(k) : c.pieceRanks(B, o, placed.length + 1);
|
|
271
|
-
const rank = ranks.pop();
|
|
269
|
+
// After `B` (under a header: after the first line's children); the placed lines go before it.
|
|
272
270
|
const length = len - o;
|
|
273
271
|
writes.push({
|
|
274
272
|
op: 'splitBlock',
|
|
@@ -278,10 +276,10 @@ export const flowOps = (c) => {
|
|
|
278
276
|
newId: T,
|
|
279
277
|
tail,
|
|
280
278
|
parent: home,
|
|
281
|
-
|
|
279
|
+
index: slot
|
|
282
280
|
});
|
|
283
281
|
kids(T, joinsTail ? last() : undefined, moved);
|
|
284
|
-
place(slot,
|
|
282
|
+
place(slot, T);
|
|
285
283
|
const at = joinsTail
|
|
286
284
|
? { block: T, offset: c.writeContent(writes, T, 0, last().content) }
|
|
287
285
|
: (end ?? { block: T, offset: 0 });
|
package/dist/crdt/index.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
75
75
|
Awareness: typeof _Awareness;
|
|
76
76
|
doc: {
|
|
77
77
|
SCHEMA: {
|
|
78
|
-
readonly version:
|
|
78
|
+
readonly version: 10;
|
|
79
79
|
readonly name: "edytor-doc";
|
|
80
80
|
readonly roots: {
|
|
81
81
|
readonly registry: "blocks";
|
|
@@ -86,7 +86,7 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
86
86
|
readonly schema: "schema";
|
|
87
87
|
};
|
|
88
88
|
};
|
|
89
|
-
SCHEMA_VERSION:
|
|
89
|
+
SCHEMA_VERSION: 10;
|
|
90
90
|
SCHEMA_NAME: "edytor-doc";
|
|
91
91
|
META_KEY: "meta";
|
|
92
92
|
init: (doc: import("./engine-api.js").EngineDoc, opts?: {
|
|
@@ -150,29 +150,30 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
150
150
|
blockNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
|
|
151
151
|
liveNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
|
|
152
152
|
isLive: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => boolean;
|
|
153
|
-
candidatesOf: (node: import("./engine-api.js").EngineNode) => import("./placement/model.js").PlacementCand[];
|
|
154
153
|
view: (doc: import("./engine-api.js").EngineDoc) => import("./placement/model.js").ModelView;
|
|
155
|
-
resolvePlacements: (blocks: Map<import("./index.js").BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: import("./index.js").BlockId) => import("./text/model.js").Owner) => Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>;
|
|
156
154
|
childrenIndex: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<import("./index.js").BlockId | null, import("./placement/model.js").ChildSlot[]>;
|
|
157
155
|
positionInView: (v: import("./placement/model.js").ModelView, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
|
|
158
156
|
buildInline: (atom: import("./index.js").InlineSpec) => import("./engine-api.js").EngineNode;
|
|
159
157
|
isSelfOrDescendant: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: import("./index.js").BlockId, maybeAncestor: import("./index.js").BlockId) => boolean;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
writePlacement: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, p: import("./index.js").BlockId | null, r: string) => void;
|
|
158
|
+
placeBlocks: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, parent: import("./index.js").BlockId | null, left: import("./index.js").BlockId | null, right: import("./index.js").BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
|
|
159
|
+
moveBlocksTo: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, ids: readonly import("./index.js").BlockId[], parent: import("./index.js").BlockId | null, index: number, side?: number, part?: number, at?: import("./index.js").BlockId | null) => void;
|
|
160
|
+
insertSpecsAt: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, parent: import("./index.js").BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
|
|
164
161
|
withdrawOnUndo: (doc: import("./engine-api.js").EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
|
|
165
162
|
inserts: {
|
|
166
163
|
has(client: number, clock: number): boolean;
|
|
167
164
|
};
|
|
168
165
|
}) => boolean;
|
|
169
|
-
materializeSpec: (doc: import("./engine-api.js").EngineDoc, sp: import("./index.js").BlockSpec, parent: import("./index.js").BlockId | null, rank: string) => void;
|
|
170
166
|
collides: (doc: import("./engine-api.js").EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
|
|
171
|
-
writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail,
|
|
167
|
+
writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail, parent: import("./index.js").BlockId | null, index: number) => import("./placement/slots.js").Unit;
|
|
172
168
|
ownText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => {
|
|
173
169
|
from: unknown;
|
|
174
170
|
to: number;
|
|
175
171
|
};
|
|
172
|
+
recordsOf: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, v?: import("./placement/model.js").ModelView) => {
|
|
173
|
+
item: import("./placement/slots.js").SeqItem;
|
|
174
|
+
stamp: import("./placement/slots.js").Stamp;
|
|
175
|
+
wins: boolean;
|
|
176
|
+
}[];
|
|
176
177
|
insertBlocks: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
|
|
177
178
|
insertBlock: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
|
|
178
179
|
restoreBlocks: (doc: import("./engine-api.js").EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
|
|
@@ -204,8 +205,8 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
204
205
|
at: number;
|
|
205
206
|
node: import("./engine-api.js").EngineNode;
|
|
206
207
|
} | null;
|
|
207
|
-
splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) =>
|
|
208
|
-
claimInto: (
|
|
208
|
+
splitAt: (doc: import("./engine-api.js").EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
|
|
209
|
+
claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
|
|
209
210
|
ownText: (doc: import("./engine-api.js").EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
|
|
210
211
|
from: unknown;
|
|
211
212
|
to: number;
|
|
@@ -225,7 +226,7 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
225
226
|
keep?: boolean;
|
|
226
227
|
after?: readonly import("./index.js").BlockSpec[];
|
|
227
228
|
} | undefined) => import("./edytor-doc.js").OpResult;
|
|
228
|
-
placeBeside: (ids: readonly string[], target: string, side: "
|
|
229
|
+
placeBeside: (ids: readonly string[], target: string, side: "left" | "right", kind?: string | undefined) => import("./edytor-doc.js").OpResult;
|
|
229
230
|
wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => import("./edytor-doc.js").OpResult;
|
|
230
231
|
splitBlock: (id: string, offset: number, newId: string, tail?: import("./placement/model.js").SplitTail | undefined) => import("./edytor-doc.js").OpResult;
|
|
231
232
|
mergeBlocks: (fromId: string, intoId: string) => import("./edytor-doc.js").OpResult;
|
|
@@ -348,11 +349,10 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
348
349
|
onChange: (cb: (change: import("./edytor-doc.js").DocChange) => void) => (() => void);
|
|
349
350
|
};
|
|
350
351
|
newDoc: (opts?: ConstructorParameters<EngineApi["Doc"]>[0]) => YDoc;
|
|
351
|
-
keepCopies: (doc: import("./engine-api.js").EngineDoc) => void;
|
|
352
352
|
purge: (doc: import("./engine-api.js").EngineDoc, facade: {
|
|
353
353
|
model: import("./placement/model.js").PlacementModel;
|
|
354
354
|
text: import("./text/model.js").TextEngine;
|
|
355
|
-
}, horizon: import("./purge.js").Horizon) => import("./purge.js").PurgeReport;
|
|
355
|
+
}, horizon: import("./purge.js").Horizon | null) => import("./purge.js").PurgeReport;
|
|
356
356
|
horizonOf: (doc: import("./engine-api.js").EngineDoc) => import("./purge.js").Horizon | null;
|
|
357
357
|
restoreTo: <Plan>(doc: import("./engine-api.js").EngineDoc, facade: import("./restore.js").RestoreFacade<Plan>, json: import("./index.js").JSONDoc) => import("./restore.js").RestoreReport;
|
|
358
358
|
model: {
|
|
@@ -361,29 +361,30 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
361
361
|
blockNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
|
|
362
362
|
liveNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
|
|
363
363
|
isLive: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => boolean;
|
|
364
|
-
candidatesOf: (node: import("./engine-api.js").EngineNode) => import("./placement/model.js").PlacementCand[];
|
|
365
364
|
view: (doc: import("./engine-api.js").EngineDoc) => import("./placement/model.js").ModelView;
|
|
366
|
-
resolvePlacements: (blocks: Map<import("./index.js").BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: import("./index.js").BlockId) => import("./text/model.js").Owner) => Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>;
|
|
367
365
|
childrenIndex: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<import("./index.js").BlockId | null, import("./placement/model.js").ChildSlot[]>;
|
|
368
366
|
positionInView: (v: import("./placement/model.js").ModelView, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
|
|
369
367
|
buildInline: (atom: import("./index.js").InlineSpec) => import("./engine-api.js").EngineNode;
|
|
370
368
|
isSelfOrDescendant: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: import("./index.js").BlockId, maybeAncestor: import("./index.js").BlockId) => boolean;
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
writePlacement: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, p: import("./index.js").BlockId | null, r: string) => void;
|
|
369
|
+
placeBlocks: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, parent: import("./index.js").BlockId | null, left: import("./index.js").BlockId | null, right: import("./index.js").BlockId | null, slots: readonly import("./placement/slots.js").BlockSlot[] | null, moved?: readonly number[] | null, side?: number, part?: number, at?: import("./placement/slots.js").Unit | null) => void;
|
|
370
|
+
moveBlocksTo: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, ids: readonly import("./index.js").BlockId[], parent: import("./index.js").BlockId | null, index: number, side?: number, part?: number, at?: import("./index.js").BlockId | null) => void;
|
|
371
|
+
insertSpecsAt: (doc: import("./engine-api.js").EngineDoc, v: import("./placement/model.js").ModelView, parent: import("./index.js").BlockId | null, index: number, specs: readonly import("./index.js").BlockSpec[], split?: import("./placement/slots.js").Unit | null) => void;
|
|
375
372
|
withdrawOnUndo: (doc: import("./engine-api.js").EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
|
|
376
373
|
inserts: {
|
|
377
374
|
has(client: number, clock: number): boolean;
|
|
378
375
|
};
|
|
379
376
|
}) => boolean;
|
|
380
|
-
materializeSpec: (doc: import("./engine-api.js").EngineDoc, sp: import("./index.js").BlockSpec, parent: import("./index.js").BlockId | null, rank: string) => void;
|
|
381
377
|
collides: (doc: import("./engine-api.js").EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
|
|
382
|
-
writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail,
|
|
378
|
+
writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail, parent: import("./index.js").BlockId | null, index: number) => import("./placement/slots.js").Unit;
|
|
383
379
|
ownText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => {
|
|
384
380
|
from: unknown;
|
|
385
381
|
to: number;
|
|
386
382
|
};
|
|
383
|
+
recordsOf: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, v?: import("./placement/model.js").ModelView) => {
|
|
384
|
+
item: import("./placement/slots.js").SeqItem;
|
|
385
|
+
stamp: import("./placement/slots.js").Stamp;
|
|
386
|
+
wins: boolean;
|
|
387
|
+
}[];
|
|
387
388
|
insertBlocks: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
|
|
388
389
|
insertBlock: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
|
|
389
390
|
restoreBlocks: (doc: import("./engine-api.js").EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
|
|
@@ -415,8 +416,8 @@ export declare const bindCrdt: (Y: EngineApi) => {
|
|
|
415
416
|
at: number;
|
|
416
417
|
node: import("./engine-api.js").EngineNode;
|
|
417
418
|
} | null;
|
|
418
|
-
splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) =>
|
|
419
|
-
claimInto: (
|
|
419
|
+
splitAt: (doc: import("./engine-api.js").EngineDoc, blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => void;
|
|
420
|
+
claimInto: (own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
|
|
420
421
|
ownText: (doc: import("./engine-api.js").EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
|
|
421
422
|
from: unknown;
|
|
422
423
|
to: number;
|
package/dist/crdt/index.js
CHANGED
|
@@ -109,7 +109,7 @@ export { SchemaMismatchError, UndecodableUpdateError } from './admission.js';
|
|
|
109
109
|
// the public read vocabulary; ordinary editing writes no per-edit
|
|
110
110
|
// attribution.
|
|
111
111
|
export {} from './attribution/index.js';
|
|
112
|
-
// Compact per-BLOCK attribution read vocabulary (`
|
|
112
|
+
// Compact per-BLOCK attribution read vocabulary (`blockattr` `<blockId>`
|
|
113
113
|
// records + the block-node `l` attr — see `attribution/block.ts`).
|
|
114
114
|
// `document.attribution.block(id)` / `document.blockAttribution(id)`
|
|
115
115
|
// are the read entry points.
|
|
@@ -1,38 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Where a block displays: its display slot (read-time promotion out of a
|
|
3
3
|
* deleted, childless or passing parent, the line and layout rules), the
|
|
4
|
-
* children index and document order built from it,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* children index and document order built from it, liveness, and the
|
|
5
|
+
* inverse of the claim owners. Pure over replicated state and the
|
|
6
|
+
* ownership the caller passes. The order of a text's pieces is the slots'
|
|
7
|
+
* (`order.split.text`: a split-born slot's key, fork patch YP22).
|
|
7
8
|
*/
|
|
8
9
|
import { type Owner } from '../text/model.js';
|
|
9
|
-
import type { BlockId,
|
|
10
|
-
/**
|
|
11
|
-
* Split pieces in text order (`order.split.text`): the blocks whose streams lie in one
|
|
12
|
-
* backing text and that still stand where they were made (their winning
|
|
13
|
-
* placement is their first candidate, `1.<client>`: no move, outdent or
|
|
14
|
-
* lift since) under one parent show in their streams' order in that text —
|
|
15
|
-
* the order of the boundaries that delimit them, the same on every
|
|
16
|
-
* replica — at the ranks they hold between them: the i-th of them in the
|
|
17
|
-
* text takes the i-th smallest of their ranks. Ranks written at a split
|
|
18
|
-
* (`sourceRank`) are a guess made on one replica's text; the boundaries are
|
|
19
|
-
* the replicated fact, so two peers splitting one block at once keep the
|
|
20
|
-
* text's order whatever each saw (an unseen edit after one's own split
|
|
21
|
-
* point included). A block moved since stands where it was moved.
|
|
22
|
-
*
|
|
23
|
-
* `inText(home)`: the blocks of `home`'s text, in text order (segment 0's
|
|
24
|
-
* first). Memoized per (text, parent) until `forget(home)`.
|
|
25
|
-
*/
|
|
26
|
-
export declare const textRanker: (blocks: ReadonlyMap<BlockId, BlockRec>, placements: ReadonlyMap<BlockId, ResolvedPlacement>, homeOf: (b: BlockId) => BlockId | undefined, inText: (home: BlockId) => readonly BlockId[]) => {
|
|
27
|
-
rank: (id: BlockId, parent: BlockId | null, rank: string) => string;
|
|
28
|
-
/**
|
|
29
|
-
* Decide `home`'s text again (its segments or a member's placement
|
|
30
|
-
* changed); the blocks whose text-order rank changed.
|
|
31
|
-
*/
|
|
32
|
-
regroup: (home: BlockId) => Set<BlockId>;
|
|
33
|
-
/** Drop what was decided for `home`'s text (every one, without it). */
|
|
34
|
-
forget: (home?: BlockId) => void;
|
|
35
|
-
};
|
|
10
|
+
import type { BlockId, ChildSlot, DisplayOwnership, DocOrder, ModelView, ResolvedPlacement } from './model.js';
|
|
36
11
|
/**
|
|
37
12
|
* THE liveness answer: `id` carries no live delete mark, owns
|
|
38
13
|
* itself (`own.hidden` covers both: a delete-marked or unknown block owns
|
|
@@ -1,144 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Where a block displays: its display slot (read-time promotion out of a
|
|
3
3
|
* deleted, childless or passing parent, the line and layout rules), the
|
|
4
|
-
* children index and document order built from it,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* children index and document order built from it, liveness, and the
|
|
5
|
+
* inverse of the claim owners. Pure over replicated state and the
|
|
6
|
+
* ownership the caller passes. The order of a text's pieces is the slots'
|
|
7
|
+
* (`order.split.text`: a split-born slot's key, fork patch YP22).
|
|
7
8
|
*/
|
|
8
9
|
import { DEAD } from '../text/model.js';
|
|
9
|
-
import { promotedRank } from './
|
|
10
|
-
/**
|
|
11
|
-
* Split pieces in text order (`order.split.text`): the blocks whose streams lie in one
|
|
12
|
-
* backing text and that still stand where they were made (their winning
|
|
13
|
-
* placement is their first candidate, `1.<client>`: no move, outdent or
|
|
14
|
-
* lift since) under one parent show in their streams' order in that text —
|
|
15
|
-
* the order of the boundaries that delimit them, the same on every
|
|
16
|
-
* replica — at the ranks they hold between them: the i-th of them in the
|
|
17
|
-
* text takes the i-th smallest of their ranks. Ranks written at a split
|
|
18
|
-
* (`sourceRank`) are a guess made on one replica's text; the boundaries are
|
|
19
|
-
* the replicated fact, so two peers splitting one block at once keep the
|
|
20
|
-
* text's order whatever each saw (an unseen edit after one's own split
|
|
21
|
-
* point included). A block moved since stands where it was moved.
|
|
22
|
-
*
|
|
23
|
-
* `inText(home)`: the blocks of `home`'s text, in text order (segment 0's
|
|
24
|
-
* first). Memoized per (text, parent) until `forget(home)`.
|
|
25
|
-
*/
|
|
26
|
-
export const textRanker = (blocks, placements, homeOf, inText) => {
|
|
27
|
-
const memo = new Map();
|
|
28
|
-
const keysOf = new Map();
|
|
29
|
-
/** `b` stands where it was made: its placement is its first candidate's. */
|
|
30
|
-
const unmoved = (b) => {
|
|
31
|
-
const c = blocks.get(b)?.cands[0];
|
|
32
|
-
const pl = placements.get(b);
|
|
33
|
-
if (c === undefined || pl === undefined || c.seq !== 1 || c.r !== pl.rank)
|
|
34
|
-
return false;
|
|
35
|
-
return pl.parent === (c.p !== null && !blocks.has(c.p) ? null : c.p);
|
|
36
|
-
};
|
|
37
|
-
const group = (home, parent) => {
|
|
38
|
-
const members = [];
|
|
39
|
-
for (const b of inText(home))
|
|
40
|
-
if (placements.get(b)?.parent === parent && unmoved(b))
|
|
41
|
-
members.push(b);
|
|
42
|
-
const out = new Map();
|
|
43
|
-
if (members.length < 2)
|
|
44
|
-
return out;
|
|
45
|
-
const rankOf = (b) => placements.get(b).rank;
|
|
46
|
-
// One client's blocks keep its own order (it ranked them knowing each
|
|
47
|
-
// other); the text decides between different clients' (concurrent
|
|
48
|
-
// splits). Each client's members, sorted by rank, fill that client's
|
|
49
|
-
// places in the text order.
|
|
50
|
-
const byClient = new Map();
|
|
51
|
-
for (const b of members) {
|
|
52
|
-
const c = blocks.get(b).cands[0].client;
|
|
53
|
-
const list = byClient.get(c);
|
|
54
|
-
if (list === undefined)
|
|
55
|
-
byClient.set(c, [b]);
|
|
56
|
-
else
|
|
57
|
-
list.push(b);
|
|
58
|
-
}
|
|
59
|
-
if (byClient.size < 2)
|
|
60
|
-
return out;
|
|
61
|
-
for (const list of byClient.values())
|
|
62
|
-
list.sort((x, y) => bySlot({ id: x, rank: rankOf(x) }, { id: y, rank: rankOf(y) }));
|
|
63
|
-
const next = new Map();
|
|
64
|
-
const order = members.map((b) => {
|
|
65
|
-
const c = blocks.get(b).cands[0].client;
|
|
66
|
-
const i = next.get(c) ?? 0;
|
|
67
|
-
next.set(c, i + 1);
|
|
68
|
-
return byClient.get(c)[i];
|
|
69
|
-
});
|
|
70
|
-
const ranks = members.map(rankOf).sort();
|
|
71
|
-
order.forEach((b, i) => out.set(b, ranks[i]));
|
|
72
|
-
return out;
|
|
73
|
-
};
|
|
74
|
-
return {
|
|
75
|
-
rank: (id, parent, rank) => {
|
|
76
|
-
const home = homeOf(id);
|
|
77
|
-
// A text no other block shares (most blocks' own): nothing to order.
|
|
78
|
-
if (home === undefined || inText(home).length < 2)
|
|
79
|
-
return rank;
|
|
80
|
-
const key = `${home}\u0000${parent}`;
|
|
81
|
-
let ranks = memo.get(key);
|
|
82
|
-
if (ranks === undefined) {
|
|
83
|
-
memo.set(key, (ranks = group(home, parent)));
|
|
84
|
-
let keys = keysOf.get(home);
|
|
85
|
-
if (keys === undefined)
|
|
86
|
-
keysOf.set(home, (keys = new Set()));
|
|
87
|
-
keys.add(key);
|
|
88
|
-
}
|
|
89
|
-
return ranks.get(id) ?? rank;
|
|
90
|
-
},
|
|
91
|
-
/**
|
|
92
|
-
* Decide `home`'s text again (its segments or a member's placement
|
|
93
|
-
* changed); the blocks whose text-order rank changed.
|
|
94
|
-
*/
|
|
95
|
-
regroup: (home) => {
|
|
96
|
-
const before = new Map();
|
|
97
|
-
for (const key of keysOf.get(home) ?? [])
|
|
98
|
-
for (const [b, r] of memo.get(key) ?? [])
|
|
99
|
-
before.set(b, r);
|
|
100
|
-
for (const key of keysOf.get(home) ?? [])
|
|
101
|
-
memo.delete(key);
|
|
102
|
-
const keys = new Set();
|
|
103
|
-
keysOf.set(home, keys);
|
|
104
|
-
const changed = new Set();
|
|
105
|
-
const parents = new Set();
|
|
106
|
-
const row = inText(home);
|
|
107
|
-
if (row.length >= 2)
|
|
108
|
-
for (const b of row) {
|
|
109
|
-
const pl = placements.get(b);
|
|
110
|
-
if (pl !== undefined)
|
|
111
|
-
parents.add(pl.parent);
|
|
112
|
-
}
|
|
113
|
-
for (const parent of parents) {
|
|
114
|
-
const key = `${home}\u0000${parent}`;
|
|
115
|
-
const ranks = group(home, parent);
|
|
116
|
-
memo.set(key, ranks);
|
|
117
|
-
keys.add(key);
|
|
118
|
-
for (const [b, r] of ranks)
|
|
119
|
-
if (before.get(b) !== r)
|
|
120
|
-
changed.add(b);
|
|
121
|
-
}
|
|
122
|
-
for (const [b, r] of before) {
|
|
123
|
-
const now = memo.get(`${home}\u0000${placements.get(b)?.parent}`)?.get(b);
|
|
124
|
-
if (now !== r)
|
|
125
|
-
changed.add(b);
|
|
126
|
-
}
|
|
127
|
-
return changed;
|
|
128
|
-
},
|
|
129
|
-
/** Drop what was decided for `home`'s text (every one, without it). */
|
|
130
|
-
forget: (home) => {
|
|
131
|
-
if (home === undefined) {
|
|
132
|
-
memo.clear();
|
|
133
|
-
keysOf.clear();
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
for (const key of keysOf.get(home) ?? [])
|
|
137
|
-
memo.delete(key);
|
|
138
|
-
keysOf.delete(home);
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
};
|
|
10
|
+
import { claimedRank, promotedRank } from './keys.js';
|
|
142
11
|
/**
|
|
143
12
|
* THE liveness answer: `id` carries no live delete mark, owns
|
|
144
13
|
* itself (`own.hidden` covers both: a delete-marked or unknown block owns
|
|
@@ -201,15 +70,13 @@ export const isLiveIn = (v, id) => {
|
|
|
201
70
|
*/
|
|
202
71
|
export const displaySlotOf = (own, placements, pl, id) => {
|
|
203
72
|
let { parent, rank } = pl;
|
|
204
|
-
// A
|
|
205
|
-
//
|
|
73
|
+
// A table's cell shown under its own placement's parent takes its column's rank
|
|
74
|
+
// (`table.columns`). Shown under its parent's claim owner (the parent merged away),
|
|
75
|
+
// it follows the owner's own children, by the claims down to its parent (`claimedRank`).
|
|
206
76
|
const direct = (owner) => {
|
|
207
77
|
if (owner !== pl.parent)
|
|
208
|
-
return rank;
|
|
209
|
-
|
|
210
|
-
if (ruled !== undefined)
|
|
211
|
-
return ruled;
|
|
212
|
-
return own.textRank === undefined ? rank : own.textRank(id, owner, rank);
|
|
78
|
+
return claimedRank(claimPath(own, owner, pl.parent), rank);
|
|
79
|
+
return (owner === null ? undefined : own.slotRank?.(owner, id, rank)) ?? rank;
|
|
213
80
|
};
|
|
214
81
|
let reset = null;
|
|
215
82
|
/** The first parent it is promoted out of (a deleted or childless one). */
|
|
@@ -240,8 +107,20 @@ export const displaySlotOf = (own, placements, pl, id) => {
|
|
|
240
107
|
// Promoted into a container: it shows as one of its items.
|
|
241
108
|
if (shows && reset === null && promoted !== null && own.container?.(owner) === true)
|
|
242
109
|
reset = promoted;
|
|
110
|
+
// Under its parent's claim owner — promoted there or not — it is keyed in the
|
|
111
|
+
// owner's claimed space: the keys are local to one index, so a key of
|
|
112
|
+
// `parent`'s sequence compares with the owner's own children's on no replica
|
|
113
|
+
// but this one.
|
|
243
114
|
if (shows)
|
|
244
|
-
return {
|
|
115
|
+
return {
|
|
116
|
+
parent: owner,
|
|
117
|
+
rank: owner !== parent
|
|
118
|
+
? claimedRank(claimPath(own, owner, parent), rank)
|
|
119
|
+
: hops === 0
|
|
120
|
+
? direct(owner)
|
|
121
|
+
: rank,
|
|
122
|
+
reset
|
|
123
|
+
};
|
|
245
124
|
promoted ??= out;
|
|
246
125
|
const up = placements.get(out);
|
|
247
126
|
if (up === undefined || hops > placements.size)
|
|
@@ -253,6 +132,22 @@ export const displaySlotOf = (own, placements, pl, id) => {
|
|
|
253
132
|
}
|
|
254
133
|
return { parent: null, rank: pl.parent === null ? direct(null) : rank, reset };
|
|
255
134
|
};
|
|
135
|
+
/**
|
|
136
|
+
* The claims from `owner` down to `parent` (a block it displays through
|
|
137
|
+
* merge claims), as each holder's claim index: the order the merged blocks'
|
|
138
|
+
* children take after the owner's own children.
|
|
139
|
+
*/
|
|
140
|
+
const claimPath = (own, owner, parent) => {
|
|
141
|
+
const path = [];
|
|
142
|
+
for (let cur = parent, hops = 0; cur !== null && cur !== owner && hops < 256; hops++) {
|
|
143
|
+
const holder = own.top(cur);
|
|
144
|
+
if (holder === undefined)
|
|
145
|
+
break;
|
|
146
|
+
path.unshift(Math.max(0, own.claimIndex(holder, cur)));
|
|
147
|
+
cur = holder;
|
|
148
|
+
}
|
|
149
|
+
return path;
|
|
150
|
+
};
|
|
256
151
|
/**
|
|
257
152
|
* The slot of `b` when it is a line — it displays (or, deleted, would
|
|
258
153
|
* display) directly under a `lines` island ({@link DisplayOwnership.lined}) — else
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order keys — the in-memory keys the index sorts a parent's children by.
|
|
3
|
+
*
|
|
4
|
+
* A block's place is its slot in its parent's sequence (fork patch YP22:
|
|
5
|
+
* the engine orders slots and move records); nothing about the order is
|
|
6
|
+
* stored. The index still needs a comparable key per child, so that
|
|
7
|
+
* `displaySlotOf`'s promotion, the layout rules, `positionInView`'s binary
|
|
8
|
+
* search and `anchors.seam` keep working over `(rank, id)`-sorted lists:
|
|
9
|
+
* each child gets a key between its sequence neighbours' when it is placed
|
|
10
|
+
* (`keyBetween`), or a dense one when a whole sequence is read
|
|
11
|
+
* (`denseKey`). Keys are plain strings compared with `<`; they are local to
|
|
12
|
+
* one index and never leave it (two replicas may hold different keys for
|
|
13
|
+
* one order).
|
|
14
|
+
*
|
|
15
|
+
* Three key spaces compose with them:
|
|
16
|
+
*
|
|
17
|
+
* - a promoted child's key is its slot's key, {@link PROMOTED} and its own
|
|
18
|
+
* ({@link promotedRank}): `PROMOTED` sorts below every key character, so
|
|
19
|
+
* the promoted children follow the slot and precede the next sibling;
|
|
20
|
+
* - a merged-away block's children show after the owner's own children,
|
|
21
|
+
* by the owner's claim order ({@link claimedRank}): {@link CLAIMED} sorts
|
|
22
|
+
* above every key character;
|
|
23
|
+
* - a table's cells show in column order ({@link cellKey}).
|
|
24
|
+
*/
|
|
25
|
+
/** Sorts below every key character: the separator of a promoted child's key. */
|
|
26
|
+
export declare const PROMOTED = "!";
|
|
27
|
+
/**
|
|
28
|
+
* A key strictly between `a` and `b` (`undefined`: no bound). Both bounds
|
|
29
|
+
* are keys of this alphabet with `a < b`: place by place, a digit halfway
|
|
30
|
+
* between theirs when one fits, else `a`'s digit and one place deeper
|
|
31
|
+
* (classic fractional indexing). A key never ends in the lowest digit
|
|
32
|
+
* (`denseKey` writes none), so a key fits between any two.
|
|
33
|
+
*/
|
|
34
|
+
export declare const keyBetween: (a: string | undefined, b: string | undefined) => string;
|
|
35
|
+
/** The `i`-th of a whole sequence's dense keys, in order (none ends in the lowest digit). */
|
|
36
|
+
export declare const denseKey: (i: number) => string;
|
|
37
|
+
/**
|
|
38
|
+
* The key of a block promoted into the slot keyed `slot` (read-time
|
|
39
|
+
* promotion, `del.blocks.promote`): after the slot, before every later key of
|
|
40
|
+
* the slot's list, in the promoted children's own order.
|
|
41
|
+
*/
|
|
42
|
+
export declare const promotedRank: (slot: string, rank: string) => string;
|
|
43
|
+
/**
|
|
44
|
+
* The key of a child of a merged-away block shown under its owner: after the
|
|
45
|
+
* owner's own children, by the owner's claims (`path`: the claim index at
|
|
46
|
+
* each hop down to the child's parent), then the child's own key.
|
|
47
|
+
*/
|
|
48
|
+
export declare const claimedRank: (path: readonly number[], rank: string) => string;
|
|
49
|
+
/**
|
|
50
|
+
* The key a table's cell displays at (`table.columns`): its column's
|
|
51
|
+
* position, before every unlisted column (`unlistedCellKey`) and the cells
|
|
52
|
+
* of no listed column (`LAST_CELL`).
|
|
53
|
+
*/
|
|
54
|
+
export declare const cellKey: (at: number) => string;
|
|
55
|
+
/** A withdrawn cell's column the table no longer lists: after the listed ones, by `unlistedRank`. */
|
|
56
|
+
export declare const unlistedCellKey: (hash: number) => string;
|
|
57
|
+
/** A cell of no listed column: last (it does not display, `table.cell`). */
|
|
58
|
+
export declare const LAST_CELL = "2";
|