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
|
@@ -3,41 +3,42 @@
|
|
|
3
3
|
* ownership the display reads (`ownShim`), and THE shown-kind rule
|
|
4
4
|
* (`typeOf`).
|
|
5
5
|
*
|
|
6
|
-
* A structural change re-decides only what it can change: the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* (`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
6
|
+
* A structural change re-decides only what it can change: the display
|
|
7
|
+
* edge of the blocks whose slot, record or mover changed (fork patch
|
|
8
|
+
* YP22), and the display slot of every block whose slot walk
|
|
9
|
+
* (`displaySlotOf`) reads one that changed — its stored subtree, through
|
|
10
|
+
* the blocks it displays — patched into the parents' child lists. A
|
|
11
|
+
* block's candidate edges are its live move records, strongest first, then
|
|
12
|
+
* its home slot; it shows at the first that closes no cycle through the
|
|
13
|
+
* accepted edges (`settle`): the later move wins, and the block whose edge
|
|
14
|
+
* is the oldest of a cycle falls back to its next candidate, back where it
|
|
15
|
+
* stood (its previous record, else home; the root's tail only when every
|
|
16
|
+
* candidate closes a cycle), so a cycle resolves locally: no global pass.
|
|
17
|
+
* The first build places every block the same way (their unique acyclic
|
|
18
|
+
* acceptance, whatever the order; the tests' greedy rebuild,
|
|
19
|
+
* `src/tests/oracles/placements.ts`, is what the self-checks compare). A
|
|
20
|
+
* placed block's order key is found from its sequence neighbours
|
|
21
|
+
* (`placement/keys.ts`), never by walking the parent's sequence; a rebuild
|
|
22
|
+
* keys every list once, in its sequence's order. The layout rules re-run
|
|
23
|
+
* only on the layouts a change reaches.
|
|
16
24
|
*/
|
|
17
|
-
import { bySlot, childrenIndex, displaySlotOf, documentOrder
|
|
25
|
+
import { bySlot, childrenIndex, displaySlotOf, documentOrder } from '../../placement/model.js';
|
|
26
|
+
import { denseKey, keyBetween } from '../../placement/keys.js';
|
|
27
|
+
import { NO_SLOT, byTail, candidatesOf, cycleThrough, edgeParentOf, collectSlots, homeOf, registryOwners, ownerOfNode, positionsOfItem, slotOwner, slotsBefore } from '../../placement/resolve.js';
|
|
28
|
+
import { cmpStamp, sameUnit } from '../../placement/slots.js';
|
|
29
|
+
import { Y } from '../../engine.js';
|
|
30
|
+
import { engineOps, firstItem } from '../../structs.js';
|
|
18
31
|
import { DEAD, displayOf } from '../model.js';
|
|
19
|
-
import { addTo, dropFrom } from './shared.js';
|
|
20
32
|
/** Placement maintenance of one doc's index, over its state, claims, streams and layout rules. */
|
|
21
33
|
export const indexPlacement = (ix) => {
|
|
22
|
-
const {
|
|
34
|
+
const { doc, registry, blocks, displaysMap, ownerChanged, placementsMap, following, dirtyLists, leftLists, placementSeeds, stateSeeds, kindsOf, slotsByBlock, rawOrder, edges, fallen, rejected, ensureOwners, ownerOf, top, streamOf, streamsIn, streamAt, kindOf, lineKind, itemKind, rowKind, rowOfTable, cellKind, cellRank, dissolve, redissolve } = ix;
|
|
35
|
+
const kept = engineOps(Y).isKeptReplaced;
|
|
23
36
|
// ── placements and the children index (maintained) ────────────
|
|
24
|
-
/** The blocks of `home`'s text, in text order (segment 0's first). */
|
|
25
|
-
const rowBlocks = (home) => {
|
|
26
|
-
const at = placed.get(home);
|
|
27
|
-
if (at === undefined)
|
|
28
|
-
return [];
|
|
29
|
-
return at.head === null ? at.blocks : [at.head, ...at.blocks];
|
|
30
|
-
};
|
|
31
|
-
const ranker = textRanker(blocks, placementsMap, (b) => streamIx.get(b)?.home, rowBlocks);
|
|
32
|
-
/** Resolved parent → the blocks placed under it. */
|
|
33
|
-
const kidsOf = new Map();
|
|
34
37
|
/** The document holds a layout or an item kind: `kids0` and `kidsMap` differ. */
|
|
35
38
|
let layoutMode = false;
|
|
36
39
|
/** The line kinds the roles declare, and those of the lines islands the document holds. */
|
|
37
40
|
let lineKinds = new Set();
|
|
38
41
|
let orderCache = null;
|
|
39
|
-
/** Some block shows no argmax candidate (a cycle rejected one, or none): passes run whole. */
|
|
40
|
-
let irregular = false;
|
|
41
42
|
let layoutBlocks = 0;
|
|
42
43
|
let itemBlocks = 0;
|
|
43
44
|
/** Tables, rows and cells (`table.*`): their rules run in the layout pass. */
|
|
@@ -46,9 +47,9 @@ export const indexPlacement = (ix) => {
|
|
|
46
47
|
const ownShim = {
|
|
47
48
|
ownerOf,
|
|
48
49
|
hidden: (b) => ownerOf(b) !== b || ix.dissolved.has(b),
|
|
49
|
-
childless: (b) =>
|
|
50
|
-
island: (b) =>
|
|
51
|
-
container: (b) =>
|
|
50
|
+
childless: (b) => kindOf(b)?.childless === true,
|
|
51
|
+
island: (b) => kindOf(b)?.island === true,
|
|
52
|
+
container: (b) => kindOf(b)?.container === true,
|
|
52
53
|
lined: (b) => lineKind(b) !== undefined,
|
|
53
54
|
passes: (b) => ix.dissolved.has(b),
|
|
54
55
|
sheds: (owner, child) => {
|
|
@@ -58,11 +59,11 @@ export const indexPlacement = (ix) => {
|
|
|
58
59
|
},
|
|
59
60
|
slotRank: (owner, id) => cellRank(owner, id),
|
|
60
61
|
top,
|
|
62
|
+
claimIndex: (holder, m) => blocks.get(holder)?.claims.findIndex((c) => c.m === m) ?? -1,
|
|
61
63
|
streamOf,
|
|
62
64
|
streamsIn,
|
|
63
65
|
streamAt,
|
|
64
|
-
display: (b) => displayOf(b, blocks, ownShim)
|
|
65
|
-
textRank: (id, parent, rank) => ranker.rank(id, parent, rank)
|
|
66
|
+
display: (b) => displayOf(b, blocks, ownShim)
|
|
66
67
|
};
|
|
67
68
|
/** Re-count `id`'s kind facts; a fact the roles never declared forces a full pass. */
|
|
68
69
|
const noteKind = (id) => {
|
|
@@ -108,17 +109,6 @@ export const indexPlacement = (ix) => {
|
|
|
108
109
|
if (layoutMode !== layoutBlocks + itemBlocks + tableBlocks > 0)
|
|
109
110
|
ix.placementFull = true;
|
|
110
111
|
};
|
|
111
|
-
/** The argmax candidate's parent (a registry entry or the root), and its index entry. */
|
|
112
|
-
const argParent = (rec) => rec?.cands[0]?.p;
|
|
113
|
-
const noteCands = (id, before) => {
|
|
114
|
-
const after = argParent(blocks.get(id));
|
|
115
|
-
if (before === after)
|
|
116
|
-
return;
|
|
117
|
-
if (typeof before === 'string')
|
|
118
|
-
dropFrom(byArgParent, before, id);
|
|
119
|
-
if (typeof after === 'string')
|
|
120
|
-
addTo(byArgParent, after, id);
|
|
121
|
-
};
|
|
122
112
|
/** The ownership the layout rules read: none of their decisions applied. */
|
|
123
113
|
const own0 = {
|
|
124
114
|
...ownShim,
|
|
@@ -210,12 +200,12 @@ export const indexPlacement = (ix) => {
|
|
|
210
200
|
if (out.has(x))
|
|
211
201
|
continue;
|
|
212
202
|
out.add(x);
|
|
213
|
-
for (const c of
|
|
214
|
-
stack.push(c);
|
|
203
|
+
for (const c of rawOrder.get(x) ?? [])
|
|
204
|
+
stack.push(c.id);
|
|
215
205
|
for (const d of displaysMap.get(x) ?? [])
|
|
216
206
|
if (d !== x)
|
|
217
|
-
for (const c of
|
|
218
|
-
stack.push(c);
|
|
207
|
+
for (const c of rawOrder.get(d) ?? [])
|
|
208
|
+
stack.push(c.id);
|
|
219
209
|
}
|
|
220
210
|
return out;
|
|
221
211
|
};
|
|
@@ -231,59 +221,294 @@ export const indexPlacement = (ix) => {
|
|
|
231
221
|
else
|
|
232
222
|
following.delete(id);
|
|
233
223
|
};
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
224
|
+
// ── edges, keys and acceptance ─────────────────────────────────
|
|
225
|
+
/**
|
|
226
|
+
* Whether the slot map has been read from the sequences: once, before the
|
|
227
|
+
* first rebuild (a shell settled on the first fold asks for a home), then
|
|
228
|
+
* the fold keeps it and every rebuild fills it again.
|
|
229
|
+
*/
|
|
230
|
+
let slotsRead = false;
|
|
231
|
+
/** What the first read collected, for a rebuild while no sequence changed since (one scan). */
|
|
232
|
+
let collected = null;
|
|
233
|
+
let collectedAt = -1;
|
|
234
|
+
const readSlots = () => {
|
|
235
|
+
slotsRead = true;
|
|
236
|
+
slotsByBlock.clear();
|
|
237
|
+
// Every registry node, its kept incarnations too (the records may not all be built yet).
|
|
238
|
+
collected = collectSlots(doc, registryOwners(registry, kept));
|
|
239
|
+
collectedAt = ix.seqEpoch;
|
|
240
|
+
for (const [b, refs] of collected.slots)
|
|
241
|
+
slotsByBlock.set(b, refs);
|
|
244
242
|
};
|
|
245
|
-
/**
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
243
|
+
/** Put `ref` (`null`: none) where the slot map holds unit `u` of the slots naming id `b`. */
|
|
244
|
+
const putSlot = (b, u, ref) => {
|
|
245
|
+
const list = (slotsByBlock.get(b) ?? []).filter((x) => !sameUnit(x, u));
|
|
246
|
+
if (ref !== null)
|
|
247
|
+
list.push(ref);
|
|
248
|
+
if (list.length === 0)
|
|
249
|
+
slotsByBlock.delete(b);
|
|
250
|
+
else
|
|
251
|
+
slotsByBlock.set(b, list);
|
|
252
|
+
};
|
|
253
|
+
/** `id`'s home slot among the slots naming its registry id (`null`: none). */
|
|
254
|
+
const homeFor = (id) => {
|
|
255
|
+
if (!slotsRead)
|
|
256
|
+
readSlots();
|
|
257
|
+
return homeOf(registry, kept, slotsByBlock, id);
|
|
259
258
|
};
|
|
259
|
+
/**
|
|
260
|
+
* Re-own the home slots of `node`'s sequence after the registry value it
|
|
261
|
+
* stands for changed (a concurrent creation of one id won or lost, its
|
|
262
|
+
* losing incarnation shown or not): each slot's parent is the node's owner
|
|
263
|
+
* now, and its block re-placed; a node that is no block of the document
|
|
264
|
+
* any more holds no slot (as a rebuild reads it).
|
|
265
|
+
*/
|
|
266
|
+
const reslot = (node) => {
|
|
267
|
+
if (!slotsRead)
|
|
268
|
+
return; // the first read collects them as they are
|
|
269
|
+
const owner = ownerOfNode(registry, kept, node);
|
|
270
|
+
for (let it = firstItem(node); it !== null; it = it.right)
|
|
271
|
+
for (const { value, unit, via } of positionsOfItem(doc, it)) {
|
|
272
|
+
if (via !== null)
|
|
273
|
+
continue; // a record's unit is another sequence's slot
|
|
274
|
+
const b = value.b;
|
|
275
|
+
putSlot(b, unit, owner === undefined ? null : { parent: owner, ...unit });
|
|
276
|
+
for (const id of new Set([b, slotOwner(registry, kept, b, unit.c)])) {
|
|
277
|
+
const rec = blocks.get(id);
|
|
278
|
+
if (rec !== undefined)
|
|
279
|
+
rec.home = homeOf(registry, kept, slotsByBlock, id);
|
|
280
|
+
placementSeeds.add(id);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
/** The slot items a rebuild collected (its homes' items, read once), while it places every block. */
|
|
285
|
+
let known = null;
|
|
286
|
+
/** `id`'s candidate edges, read from the engine: its live records, strongest first, then home (`[]`: no slot, or gone). */
|
|
287
|
+
const candidatesFor = (id) => {
|
|
288
|
+
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
289
|
+
return home === null ? [] : candidatesOf(doc, registry, kept, home, known?.get(home));
|
|
290
|
+
};
|
|
291
|
+
/** The owner of the sequence `id`'s slot shows in as the engine stands (its mover's, else home's; `null`: the root). */
|
|
292
|
+
const edgeParent = (id) => {
|
|
293
|
+
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
294
|
+
return home === null ? undefined : edgeParentOf(doc, registry, kept, home);
|
|
295
|
+
};
|
|
296
|
+
/** The raw children of `parent` in order (every block whose edge parent it is), kept sorted by key. */
|
|
297
|
+
const rawList = (parent) => {
|
|
298
|
+
let l = rawOrder.get(parent);
|
|
299
|
+
if (l === undefined)
|
|
300
|
+
rawOrder.set(parent, (l = []));
|
|
301
|
+
return l;
|
|
302
|
+
};
|
|
303
|
+
/** Counts the placement writes: a key's walk that saw one happen under it walks again. */
|
|
304
|
+
let placedEpoch = 0;
|
|
305
|
+
/** Whether a placement is keyed as it is decided: not while a rebuild places every block (it keys them after, in one pass). */
|
|
306
|
+
let keyed = true;
|
|
260
307
|
const setPlacement = (id, pl) => {
|
|
308
|
+
ix.foldStats.placed++;
|
|
309
|
+
placedEpoch++;
|
|
261
310
|
const old = placementsMap.get(id);
|
|
262
|
-
if (old !== undefined)
|
|
263
|
-
|
|
311
|
+
if (keyed && old !== undefined) {
|
|
312
|
+
const l = rawList(old.parent);
|
|
313
|
+
const at = seek(l, { id, rank: old.rank });
|
|
314
|
+
if (l[at]?.id === id)
|
|
315
|
+
l.splice(at, 1);
|
|
316
|
+
if (l.length === 0)
|
|
317
|
+
rawOrder.delete(old.parent);
|
|
318
|
+
}
|
|
264
319
|
if (pl === undefined)
|
|
265
320
|
placementsMap.delete(id);
|
|
266
321
|
else {
|
|
267
322
|
placementsMap.set(id, pl);
|
|
268
|
-
|
|
323
|
+
if (!keyed)
|
|
324
|
+
return;
|
|
325
|
+
const l = rawList(pl.parent);
|
|
326
|
+
const slot = { id, rank: pl.rank };
|
|
327
|
+
l.splice(seek(l, slot), 0, slot);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* The key `id` takes under `edge.parent`: between the key of the nearest
|
|
332
|
+
* block shown where it stands before `id`'s place in the sequence (placed
|
|
333
|
+
* first when it is a pending seed; walked past when it shows elsewhere)
|
|
334
|
+
* and the next key of that list. Placing a pending neighbour can drop a
|
|
335
|
+
* block the walk already passed back into that stretch (a cycle's
|
|
336
|
+
* fallback, back where it stood): a walk under which a placement changed
|
|
337
|
+
* walks again, so the key it returns reads one state.
|
|
338
|
+
*/
|
|
339
|
+
const keyFor = (id, edge, place) => {
|
|
340
|
+
for (;;) {
|
|
341
|
+
const before = placedEpoch;
|
|
342
|
+
const lrank = leftKey(id, edge, place);
|
|
343
|
+
if (placedEpoch !== before)
|
|
344
|
+
continue;
|
|
345
|
+
const list = rawOrder.get(edge.parent) ?? [];
|
|
346
|
+
const i = lrank === undefined ? 0 : seek(list, { id: '', rank: lrank }) + 1;
|
|
347
|
+
return keyBetween(lrank, list[i]?.rank);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
/** The key of the nearest block shown before `id`'s place at `edge`, under its parent (`undefined`: none). */
|
|
351
|
+
const leftKey = (id, edge, place) => {
|
|
352
|
+
const home = blocks.get(id)?.home ?? homeFor(id);
|
|
353
|
+
for (const e of slotsBefore(doc, edge, home)) {
|
|
354
|
+
const b = slotOwner(registry, kept, e.value.b, e.unit.c);
|
|
355
|
+
// `id` itself (its place before this one) keys nothing.
|
|
356
|
+
if (b === id)
|
|
357
|
+
continue;
|
|
358
|
+
// A slot that is not its block's home (a second creation of one id) places nothing.
|
|
359
|
+
const h = blocks.get(b)?.home ?? homeFor(b);
|
|
360
|
+
if (h === null || !sameUnit(h, e.unit))
|
|
361
|
+
continue;
|
|
362
|
+
place(b);
|
|
363
|
+
const pl = placementsMap.get(b);
|
|
364
|
+
const shows = edges.get(b);
|
|
365
|
+
if (pl !== undefined &&
|
|
366
|
+
pl.parent === edge.parent &&
|
|
367
|
+
!rejected.has(b) &&
|
|
368
|
+
shows != null &&
|
|
369
|
+
shows.via === e.via)
|
|
370
|
+
return pl.rank;
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
};
|
|
374
|
+
/** The key of `id` at the root's tail, among the rejected by stamp then id, after the last top-level block. */
|
|
375
|
+
const tailKey = (id, stamp) => {
|
|
376
|
+
const tail = [...rejected]
|
|
377
|
+
.filter((x) => x !== id)
|
|
378
|
+
.map((x) => ({ id: x, stamp: edges.get(x)?.stamp ?? NO_SLOT }))
|
|
379
|
+
.sort(byTail);
|
|
380
|
+
const list = rawOrder.get(null) ?? [];
|
|
381
|
+
const me = { id, stamp };
|
|
382
|
+
let at = 0;
|
|
383
|
+
while (at < tail.length && byTail(tail[at], me) < 0)
|
|
384
|
+
at++;
|
|
385
|
+
const prev = at > 0
|
|
386
|
+
? placementsMap.get(tail[at - 1].id)?.rank
|
|
387
|
+
: list[list.length - tail.length - 1]?.rank;
|
|
388
|
+
const next = at < tail.length ? placementsMap.get(tail[at].id)?.rank : undefined;
|
|
389
|
+
return keyBetween(prev, next);
|
|
390
|
+
};
|
|
391
|
+
/** Show `id` at the root's tail: every edge closes a cycle, or none. */
|
|
392
|
+
const reject = (id, edge) => {
|
|
393
|
+
edges.set(id, edge);
|
|
394
|
+
if (placementsMap.has(id))
|
|
395
|
+
setPlacement(id, undefined);
|
|
396
|
+
fallen.delete(id);
|
|
397
|
+
rejected.add(id);
|
|
398
|
+
const rank = keyed ? tailKey(id, edge?.stamp ?? NO_SLOT) : '';
|
|
399
|
+
setPlacement(id, { parent: null, rank });
|
|
400
|
+
};
|
|
401
|
+
/** The same display edge: the same sequence, the same record, the same stamp. */
|
|
402
|
+
const sameEdge = (a, b) => a.parent === b.parent && a.via === b.via && cmpStamp(a.stamp, b.stamp) === 0;
|
|
403
|
+
/** Whether `edge` (of `id`) is a place `id` may take now: a live parent, and no cycle it would be the oldest edge of. */
|
|
404
|
+
const admits = (id, edge) => {
|
|
405
|
+
const parent = edge.parent;
|
|
406
|
+
if (parent === undefined || (parent !== null && !blocks.has(parent)))
|
|
407
|
+
return { ok: false, drop: null };
|
|
408
|
+
const cycle = cycleThrough(placementsMap, ownerOf, id, parent);
|
|
409
|
+
if (cycle === null)
|
|
410
|
+
return { ok: true, drop: null };
|
|
411
|
+
let [min, minStamp] = [id, edge.stamp];
|
|
412
|
+
for (const m of cycle) {
|
|
413
|
+
const e = m === id ? edge : edges.get(m);
|
|
414
|
+
if (m !== id && e && cmpStamp(e.stamp, minStamp) < 0)
|
|
415
|
+
[min, minStamp] = [m, e.stamp];
|
|
416
|
+
}
|
|
417
|
+
return min === id ? { ok: false, drop: null } : { ok: true, drop: min };
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Place `id` at its strongest candidate edge from `from` on that closes no
|
|
421
|
+
* cycle it would be the oldest edge of (the later move wins). When the
|
|
422
|
+
* edge closes a cycle through an older edge, that edge's block is dropped
|
|
423
|
+
* to its next candidate (back where it stood before that move), and so on
|
|
424
|
+
* down its records to home; a block every candidate of which fails shows
|
|
425
|
+
* at the root's tail. `place` places a pending neighbour first. Returns
|
|
426
|
+
* the blocks whose placement changed.
|
|
427
|
+
*/
|
|
428
|
+
const settle = (id, place, from = 0) => {
|
|
429
|
+
const changed = [id];
|
|
430
|
+
const cands = candidatesFor(id);
|
|
431
|
+
for (let n = from; n < cands.length; n++) {
|
|
432
|
+
const edge = cands[n];
|
|
433
|
+
// The key first: its walk places the pending neighbours, so the cycle
|
|
434
|
+
// check reads them placed (a neighbour placed after it could close a
|
|
435
|
+
// cycle through `id` that no check saw).
|
|
436
|
+
const rank = keyed ? keyFor(id, edge, place) : '';
|
|
437
|
+
const { ok, drop } = admits(id, edge);
|
|
438
|
+
if (!ok)
|
|
439
|
+
continue;
|
|
440
|
+
edges.set(id, edge);
|
|
441
|
+
rejected.delete(id);
|
|
442
|
+
if (n > 0)
|
|
443
|
+
fallen.add(id);
|
|
444
|
+
else
|
|
445
|
+
fallen.delete(id);
|
|
446
|
+
if (placementsMap.has(id))
|
|
447
|
+
setPlacement(id, undefined);
|
|
448
|
+
setPlacement(id, { parent: edge.parent, rank });
|
|
449
|
+
if (drop !== null) {
|
|
450
|
+
const now = edges.get(drop);
|
|
451
|
+
const at = now ? candidatesFor(drop).findIndex((c) => sameEdge(c, now)) : -1;
|
|
452
|
+
changed.push(...settle(drop, () => { }, at + 1));
|
|
453
|
+
}
|
|
454
|
+
return changed;
|
|
455
|
+
}
|
|
456
|
+
reject(id, cands[0] ?? null);
|
|
457
|
+
return changed;
|
|
458
|
+
};
|
|
459
|
+
/** Whether a candidate stronger than `id`'s accepted edge would take it now. */
|
|
460
|
+
const canRise = (id) => {
|
|
461
|
+
const cands = candidatesFor(id);
|
|
462
|
+
const now = edges.get(id);
|
|
463
|
+
const at = rejected.has(id) || !now ? cands.length : cands.findIndex((c) => sameEdge(c, now));
|
|
464
|
+
for (let n = 0; n < (at < 0 ? cands.length : at); n++)
|
|
465
|
+
if (admits(id, cands[n]).ok)
|
|
466
|
+
return true;
|
|
467
|
+
return false;
|
|
468
|
+
};
|
|
469
|
+
/**
|
|
470
|
+
* Try every block below its strongest edge again (a changed edge may have
|
|
471
|
+
* broken its cycle, or made another the one to drop): those that rise join
|
|
472
|
+
* `moved`.
|
|
473
|
+
*/
|
|
474
|
+
const retryFallen = (moved) => {
|
|
475
|
+
for (let round = 0; round < fallen.size + rejected.size + 2; round++) {
|
|
476
|
+
let changed = false;
|
|
477
|
+
const below = [...rejected, ...fallen]
|
|
478
|
+
.map((x) => ({ id: x, stamp: candidatesFor(x)[0]?.stamp ?? NO_SLOT }))
|
|
479
|
+
.sort((a, b) => -byTail(a, b));
|
|
480
|
+
for (const { id } of below) {
|
|
481
|
+
if (!canRise(id))
|
|
482
|
+
continue;
|
|
483
|
+
for (const x of settle(id, () => { }))
|
|
484
|
+
moved.add(x);
|
|
485
|
+
changed = true;
|
|
486
|
+
}
|
|
487
|
+
if (!changed)
|
|
488
|
+
break;
|
|
269
489
|
}
|
|
270
490
|
};
|
|
271
|
-
/**
|
|
491
|
+
/**
|
|
492
|
+
* Everything rebuilt from the sequences (the first build, roles): every
|
|
493
|
+
* block placed as an edit places one (`settle`, then `retryFallen`) —
|
|
494
|
+
* the one acyclic acceptance, whatever the order the blocks come in —
|
|
495
|
+
* then keyed in one pass over each sequence's places (dense keys in its
|
|
496
|
+
* order), the root's tail after the last top-level block.
|
|
497
|
+
*/
|
|
272
498
|
const rebuildPlacements = () => {
|
|
273
499
|
ix.placementFull = false;
|
|
274
|
-
ranker.forget();
|
|
275
|
-
regroup.clear();
|
|
276
500
|
placementSeeds.clear();
|
|
277
501
|
stateSeeds.clear();
|
|
278
502
|
ownerChanged.clear();
|
|
279
|
-
|
|
503
|
+
const declared = ix.roles?.declared();
|
|
504
|
+
ix.itemKinds = new Set(declared?.items);
|
|
280
505
|
for (const b of blocks.keys()) {
|
|
281
506
|
const item = itemKind(b);
|
|
282
507
|
if (item !== undefined)
|
|
283
508
|
ix.itemKinds.add(item);
|
|
284
509
|
}
|
|
285
|
-
lineKinds = new Set(
|
|
286
|
-
ix.rowKinds =
|
|
510
|
+
lineKinds = new Set(declared?.lines);
|
|
511
|
+
ix.rowKinds = new Map(declared?.rows);
|
|
287
512
|
for (const b of blocks.keys()) {
|
|
288
513
|
const type = blocks.get(b)?.type;
|
|
289
514
|
const rows = type === undefined ? undefined : rowOfTable(type);
|
|
@@ -299,32 +524,60 @@ export const indexPlacement = (ix) => {
|
|
|
299
524
|
for (const line of lineCounts.keys())
|
|
300
525
|
lineKinds.add(line);
|
|
301
526
|
ix.placementFull = false;
|
|
302
|
-
|
|
527
|
+
// The first read's slots while no sequence changed since (one scan).
|
|
528
|
+
const { slots, items, places } = collected !== null && collectedAt === ix.seqEpoch
|
|
529
|
+
? collected
|
|
530
|
+
: collectSlots(doc, blocks.values());
|
|
531
|
+
collected = null;
|
|
532
|
+
slotsByBlock.clear();
|
|
533
|
+
for (const [b, refs] of slots)
|
|
534
|
+
slotsByBlock.set(b, refs);
|
|
535
|
+
slotsRead = true;
|
|
303
536
|
placementsMap.clear();
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
537
|
+
edges.clear();
|
|
538
|
+
rawOrder.clear();
|
|
539
|
+
fallen.clear();
|
|
540
|
+
rejected.clear();
|
|
541
|
+
[keyed, known] = [false, items];
|
|
542
|
+
for (const id of blocks.keys())
|
|
543
|
+
settle(id, () => { });
|
|
544
|
+
if (fallen.size + rejected.size > 0)
|
|
545
|
+
retryFallen(new Set());
|
|
546
|
+
[keyed, known] = [true, null];
|
|
547
|
+
// The lists in key order: a block where its edge shows it, in its sequence's order.
|
|
548
|
+
for (const [owner, list] of places)
|
|
549
|
+
for (const e of list) {
|
|
550
|
+
const b = slotOwner(registry, kept, e.value.b, e.unit.c);
|
|
551
|
+
const pl = placementsMap.get(b);
|
|
552
|
+
const h = blocks.get(b)?.home;
|
|
553
|
+
if (pl?.parent !== owner || rejected.has(b) || !h || !sameUnit(h, e.unit))
|
|
554
|
+
continue;
|
|
555
|
+
if (edges.get(b)?.via !== e.via)
|
|
556
|
+
continue;
|
|
557
|
+
const l = rawList(owner);
|
|
558
|
+
pl.rank = denseKey(l.length);
|
|
559
|
+
l.push({ id: b, rank: pl.rank });
|
|
560
|
+
}
|
|
561
|
+
// The root's tail, keyed as an edit keys a rejected block, in its order.
|
|
562
|
+
const tail = [...rejected].map((x) => ({ id: x, stamp: edges.get(x)?.stamp ?? NO_SLOT }));
|
|
563
|
+
rejected.clear();
|
|
564
|
+
for (const { id } of tail.sort(byTail))
|
|
565
|
+
reject(id, edges.get(id) ?? null);
|
|
312
566
|
layoutMode = layoutBlocks + itemBlocks + tableBlocks > 0;
|
|
313
567
|
ix.dissolved = new Set();
|
|
314
|
-
const index = (
|
|
568
|
+
const index = (map) => {
|
|
315
569
|
const at = new Map();
|
|
316
570
|
for (const [parent, list] of map)
|
|
317
571
|
for (const { id, rank, reset } of list)
|
|
318
572
|
at.set(id, reset === undefined ? { parent, rank } : { parent, rank, reset });
|
|
319
|
-
void own;
|
|
320
573
|
return at;
|
|
321
574
|
};
|
|
322
575
|
ix.kids0 = childrenIndex(placementsMap, own0);
|
|
323
|
-
ix.slots0 = index(
|
|
576
|
+
ix.slots0 = index(ix.kids0);
|
|
324
577
|
if (layoutMode) {
|
|
325
578
|
ix.dissolved = dissolve(ix.kids0);
|
|
326
579
|
ix.kidsMap = childrenIndex(placementsMap, ownShim);
|
|
327
|
-
ix.slots = index(
|
|
580
|
+
ix.slots = index(ix.kidsMap);
|
|
328
581
|
}
|
|
329
582
|
else
|
|
330
583
|
[ix.kidsMap, ix.slots] = [ix.kids0, ix.slots0];
|
|
@@ -337,48 +590,61 @@ export const indexPlacement = (ix) => {
|
|
|
337
590
|
};
|
|
338
591
|
const ensurePlacements = () => {
|
|
339
592
|
ensureOwners();
|
|
340
|
-
if (ix.placementFull
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
if (placementSeeds.size + stateSeeds.size + ownerChanged.size + regroup.size === 0)
|
|
593
|
+
if (ix.placementFull)
|
|
594
|
+
return rebuildPlacements();
|
|
595
|
+
if (placementSeeds.size + stateSeeds.size + ownerChanged.size === 0)
|
|
346
596
|
return;
|
|
347
597
|
const moved = new Set();
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
598
|
+
const seeds = new Set(placementSeeds);
|
|
599
|
+
const done = new Set();
|
|
600
|
+
/** Place one seed (its left neighbour first, when it is a seed too). */
|
|
601
|
+
const place = (id) => {
|
|
602
|
+
if (done.has(id) || !seeds.has(id))
|
|
603
|
+
return;
|
|
604
|
+
done.add(id);
|
|
605
|
+
if (!blocks.has(id)) {
|
|
606
|
+
if (placementsMap.has(id)) {
|
|
607
|
+
setPlacement(id, undefined);
|
|
608
|
+
edges.delete(id);
|
|
609
|
+
fallen.delete(id);
|
|
610
|
+
rejected.delete(id);
|
|
611
|
+
moved.add(id);
|
|
612
|
+
}
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
const top = candidatesFor(id)[0] ?? null;
|
|
616
|
+
const old = edges.get(id);
|
|
617
|
+
// The same edge, placed at its strongest: its key still orders it (its neighbours' keys are theirs).
|
|
618
|
+
if (top !== null &&
|
|
619
|
+
old &&
|
|
620
|
+
!rejected.has(id) &&
|
|
621
|
+
!fallen.has(id) &&
|
|
622
|
+
placementsMap.has(id) &&
|
|
623
|
+
sameEdge(top, old))
|
|
624
|
+
return;
|
|
625
|
+
for (const x of settle(id, place))
|
|
626
|
+
moved.add(x);
|
|
627
|
+
};
|
|
628
|
+
for (const id of [...seeds])
|
|
629
|
+
place(id);
|
|
630
|
+
// A changed claim owner can close a cycle through a display edge that did not change.
|
|
366
631
|
for (const o of ownerChanged)
|
|
367
|
-
for (const c of
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
632
|
+
for (const c of [...(rawOrder.get(o) ?? [])]) {
|
|
633
|
+
const edge = edges.get(c.id);
|
|
634
|
+
if (!edge || rejected.has(c.id) || edge.parent === undefined)
|
|
635
|
+
continue;
|
|
636
|
+
if (cycleThrough(placementsMap, ownerOf, c.id, edge.parent) !== null)
|
|
637
|
+
for (const x of settle(c.id, () => { }))
|
|
638
|
+
moved.add(x);
|
|
639
|
+
}
|
|
640
|
+
if (rejected.size + fallen.size > 0 && (moved.size > 0 || ownerChanged.size > 0))
|
|
641
|
+
retryFallen(moved);
|
|
642
|
+
const seeds2 = new Set([...moved, ...stateSeeds, ...ownerChanged]);
|
|
377
643
|
const kinds = [...stateSeeds];
|
|
378
644
|
placementSeeds.clear();
|
|
379
645
|
stateSeeds.clear();
|
|
380
646
|
ownerChanged.clear();
|
|
381
|
-
const affected = reach(
|
|
647
|
+
const affected = reach(seeds2);
|
|
382
648
|
let touched = affected;
|
|
383
649
|
const changed = patch(ix.kids0, ix.slots0, own0, affected);
|
|
384
650
|
let any = changed.size > 0;
|
|
@@ -395,6 +661,19 @@ export const indexPlacement = (ix) => {
|
|
|
395
661
|
ix.kidsVersion++;
|
|
396
662
|
}
|
|
397
663
|
};
|
|
664
|
+
/**
|
|
665
|
+
* A plain block (the document's default kind) directly in a container
|
|
666
|
+
* whose item is a kind of its own that renders content (a list) shows
|
|
667
|
+
* as that item — the read-time side of the facade's `fitted`: a race
|
|
668
|
+
* (a peer's lift of an item another peer's outdent moves, a concurrent
|
|
669
|
+
* undo of a retype) or an explicit write never shows a bare paragraph
|
|
670
|
+
* in a list. Anywhere else it shows `plain`.
|
|
671
|
+
*/
|
|
672
|
+
const itemOf = (under, plain) => {
|
|
673
|
+
const parent = typeOf(under);
|
|
674
|
+
const { container, child } = ix.roles.of(parent);
|
|
675
|
+
return container && ix.roles.of(child).rendersContent ? child : plain;
|
|
676
|
+
};
|
|
398
677
|
/**
|
|
399
678
|
* The kind `id` displays as — THE shown-kind rule, read from its slot
|
|
400
679
|
* and the role table. A block directly in an island declared `lines`
|
|
@@ -411,25 +690,11 @@ export const indexPlacement = (ix) => {
|
|
|
411
690
|
* (`itemOf`). Any other shows its stored kind (a retype shows). Read from the stored kinds
|
|
412
691
|
* at call time: a retype rebuilds no placement.
|
|
413
692
|
*/
|
|
414
|
-
/**
|
|
415
|
-
* A plain block (the document's default kind) directly in a container
|
|
416
|
-
* whose item is a kind of its own that renders content (a list) shows
|
|
417
|
-
* as that item — the read-time side of the facade's `fitted`: a race
|
|
418
|
-
* (a peer's lift of an item another peer's outdent moves, a concurrent
|
|
419
|
-
* undo of a retype) or an explicit write never shows a bare paragraph
|
|
420
|
-
* in a list. Anywhere else it shows `plain`.
|
|
421
|
-
*/
|
|
422
|
-
const itemOf = (under, plain) => {
|
|
423
|
-
const parent = typeOf(under);
|
|
424
|
-
if (!ix.roles.container(parent))
|
|
425
|
-
return plain;
|
|
426
|
-
const item = ix.roles.defaultChild(parent);
|
|
427
|
-
return ix.roles.rendersContent(item) ? item : plain;
|
|
428
|
-
};
|
|
429
693
|
const typeOf = (id) => {
|
|
430
694
|
const stored = blocks.get(id)?.type ?? 'unknown';
|
|
431
695
|
const slot = ix.slots.get(id);
|
|
432
|
-
|
|
696
|
+
const roles = ix.roles;
|
|
697
|
+
if (roles === null || slot === undefined)
|
|
433
698
|
return stored;
|
|
434
699
|
const { parent: under, reset } = slot;
|
|
435
700
|
const line = under === null || lineKinds.size === 0 ? undefined : lineKind(under);
|
|
@@ -437,38 +702,36 @@ export const indexPlacement = (ix) => {
|
|
|
437
702
|
return line;
|
|
438
703
|
const lined = lineKinds.has(stored);
|
|
439
704
|
const from = reset === undefined ? undefined : (blocks.get(reset)?.type ?? null);
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
: stored;
|
|
705
|
+
const plain = roles.of(null).child;
|
|
706
|
+
if (!lined && (from === undefined || stored !== roles.of(from).child))
|
|
707
|
+
return stored === plain && under !== null ? itemOf(under, stored) : stored;
|
|
444
708
|
// Out of a removed list, inside an outer list of its kind: still an item. A
|
|
445
709
|
// container whose item is the document's default kind (a column) holds no
|
|
446
710
|
// items of its own: a paragraph under one is no list's item.
|
|
447
|
-
if (!lined && typeof from === 'string' &&
|
|
711
|
+
if (!lined && typeof from === 'string' && roles.of(from).container)
|
|
448
712
|
for (let u = under; u !== null; u = ix.slots.get(u)?.parent ?? null) {
|
|
449
|
-
const t = typeOf(u);
|
|
450
|
-
if (
|
|
451
|
-
ix.roles.defaultChild(t) === stored &&
|
|
452
|
-
stored !== ix.roles.defaultChild(null))
|
|
713
|
+
const t = roles.of(typeOf(u));
|
|
714
|
+
if (t.container && t.child === stored && stored !== plain)
|
|
453
715
|
return stored;
|
|
454
716
|
}
|
|
455
|
-
const kind =
|
|
456
|
-
if (
|
|
717
|
+
const kind = roles.of(under === null ? null : typeOf(under)).child;
|
|
718
|
+
if (roles.of(kind).rendersContent || !roles.of(stored).rendersContent)
|
|
457
719
|
return kind;
|
|
458
720
|
// Never a line kind outside its island, even where the slot's kind shows nothing.
|
|
459
|
-
return lined ?
|
|
721
|
+
return lined ? plain : stored;
|
|
460
722
|
};
|
|
461
723
|
/** The document order, cached until a child list changes. */
|
|
462
724
|
const order = () => (orderCache ??= documentOrder(ix.kidsMap));
|
|
463
725
|
return {
|
|
464
|
-
ranker,
|
|
465
726
|
ownShim,
|
|
466
727
|
own0,
|
|
467
728
|
noteKind,
|
|
468
|
-
|
|
469
|
-
|
|
729
|
+
homeFor,
|
|
730
|
+
putSlot,
|
|
731
|
+
edgeParent,
|
|
470
732
|
noteFollowing,
|
|
471
733
|
ensurePlacements,
|
|
734
|
+
reslot,
|
|
472
735
|
typeOf,
|
|
473
736
|
order
|
|
474
737
|
};
|