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.
Files changed (231) hide show
  1. package/README.md +2 -2
  2. package/dist/block/block.svelte.js +6 -21
  3. package/dist/block/inlineBlock.svelte.js +4 -6
  4. package/dist/clipboard/moveText.js +6 -8
  5. package/dist/cloudflare/DocumentRoom.d.ts +23 -12
  6. package/dist/cloudflare/DocumentRoom.js +10 -6
  7. package/dist/cloudflare/index.d.ts +1 -1
  8. package/dist/cloudflare/room/access.d.ts +8 -3
  9. package/dist/cloudflare/room/access.js +44 -20
  10. package/dist/cloudflare/room/admission.js +28 -5
  11. package/dist/cloudflare/room/comments.js +28 -1
  12. package/dist/cloudflare/room/context.d.ts +139 -129
  13. package/dist/cloudflare/room/context.js +11 -4
  14. package/dist/cloudflare/room/forged.d.ts +14 -0
  15. package/dist/cloudflare/room/forged.js +30 -2
  16. package/dist/cloudflare/room/frames.d.ts +4 -0
  17. package/dist/cloudflare/room/frames.js +5 -1
  18. package/dist/cloudflare/room/purge.d.ts +6 -3
  19. package/dist/cloudflare/room/purge.js +45 -19
  20. package/dist/cloudflare/room/replicas.d.ts +4 -2
  21. package/dist/cloudflare/room/replicas.js +8 -3
  22. package/dist/cloudflare/room/suggest.d.ts +7 -0
  23. package/dist/cloudflare/room/suggest.js +143 -0
  24. package/dist/cloudflare/room/updates.d.ts +3 -4
  25. package/dist/cloudflare/room/updates.js +6 -8
  26. package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
  27. package/dist/cloudflare/routeDocumentSocket.js +5 -3
  28. package/dist/components/Content.svelte +9 -1
  29. package/dist/crdt/anchors.js +3 -2
  30. package/dist/crdt/attribution/attribution.d.ts +34 -12
  31. package/dist/crdt/attribution/attribution.js +59 -21
  32. package/dist/crdt/attribution/block.d.ts +3 -3
  33. package/dist/crdt/attribution/block.js +7 -8
  34. package/dist/crdt/attribution/index.d.ts +1 -1
  35. package/dist/crdt/attribution/index.js +1 -1
  36. package/dist/crdt/data.d.ts +62 -31
  37. package/dist/crdt/data.js +211 -103
  38. package/dist/crdt/doc/capability.js +9 -13
  39. package/dist/crdt/doc/delete.d.ts +1 -1
  40. package/dist/crdt/doc/delete.js +13 -30
  41. package/dist/crdt/doc/funnel.d.ts +1 -1
  42. package/dist/crdt/doc/funnel.js +34 -26
  43. package/dist/crdt/doc/gate.d.ts +3 -3
  44. package/dist/crdt/doc/history.d.ts +31 -9
  45. package/dist/crdt/doc/history.js +204 -73
  46. package/dist/crdt/doc/layout.js +16 -27
  47. package/dist/crdt/doc/meta.js +17 -23
  48. package/dist/crdt/doc/moves.d.ts +1 -1
  49. package/dist/crdt/doc/moves.js +52 -32
  50. package/dist/crdt/doc/reads.d.ts +2 -2
  51. package/dist/crdt/doc/reads.js +20 -30
  52. package/dist/crdt/doc/seed.js +2 -2
  53. package/dist/crdt/doc/split-merge.js +12 -15
  54. package/dist/crdt/doc/steps.d.ts +25 -13
  55. package/dist/crdt/doc/steps.js +55 -125
  56. package/dist/crdt/doc/suggest.d.ts +167 -0
  57. package/dist/crdt/doc/suggest.js +579 -0
  58. package/dist/crdt/doc/types.d.ts +21 -9
  59. package/dist/crdt/document.d.ts +26 -6
  60. package/dist/crdt/document.js +40 -15
  61. package/dist/crdt/edytor-doc.d.ts +410 -390
  62. package/dist/crdt/edytor-doc.js +143 -95
  63. package/dist/crdt/engine-api.d.ts +39 -1
  64. package/dist/crdt/engine.d.ts +2 -2
  65. package/dist/crdt/engine.js +4 -5
  66. package/dist/crdt/flow.d.ts +2 -2
  67. package/dist/crdt/flow.js +29 -37
  68. package/dist/crdt/index.d.ts +134 -127
  69. package/dist/crdt/index.js +4 -1
  70. package/dist/crdt/operations.d.ts +12 -3
  71. package/dist/crdt/parts.d.ts +43 -0
  72. package/dist/crdt/parts.js +44 -0
  73. package/dist/crdt/placement/display.d.ts +6 -31
  74. package/dist/crdt/placement/display.js +41 -146
  75. package/dist/crdt/placement/keys.d.ts +63 -0
  76. package/dist/crdt/placement/keys.js +95 -0
  77. package/dist/crdt/placement/model.d.ts +119 -118
  78. package/dist/crdt/placement/model.js +242 -142
  79. package/dist/crdt/placement/resolve.d.ts +123 -35
  80. package/dist/crdt/placement/resolve.js +188 -153
  81. package/dist/crdt/placement/slots.d.ts +134 -0
  82. package/dist/crdt/placement/slots.js +152 -0
  83. package/dist/crdt/protocol.d.ts +1 -1
  84. package/dist/crdt/protocol.js +1 -1
  85. package/dist/crdt/protocols/auth.d.ts +11 -1
  86. package/dist/crdt/protocols/auth.js +17 -1
  87. package/dist/crdt/protocols/comments.d.ts +16 -1
  88. package/dist/crdt/protocols/comments.js +9 -1
  89. package/dist/crdt/providers/index.d.ts +6 -4
  90. package/dist/crdt/providers/index.js +3 -1
  91. package/dist/crdt/providers/room.d.ts +16 -2
  92. package/dist/crdt/providers/room.js +64 -17
  93. package/dist/crdt/providers/websocket.d.ts +6 -4
  94. package/dist/crdt/providers/websocket.js +14 -7
  95. package/dist/crdt/purge.d.ts +24 -9
  96. package/dist/crdt/purge.js +266 -126
  97. package/dist/crdt/restore.d.ts +4 -3
  98. package/dist/crdt/restore.js +27 -34
  99. package/dist/crdt/schema.d.ts +30 -21
  100. package/dist/crdt/schema.js +39 -23
  101. package/dist/crdt/storage.d.ts +1 -1
  102. package/dist/crdt/storage.js +1 -1
  103. package/dist/crdt/structs.d.ts +17 -15
  104. package/dist/crdt/structs.js +10 -22
  105. package/dist/crdt/text/index/cache.js +4 -0
  106. package/dist/crdt/text/index/claims.js +13 -23
  107. package/dist/crdt/text/index/fold.d.ts +2 -3
  108. package/dist/crdt/text/index/fold.js +194 -44
  109. package/dist/crdt/text/index/layout.d.ts +2 -4
  110. package/dist/crdt/text/index/layout.js +14 -30
  111. package/dist/crdt/text/index/placement.d.ts +26 -18
  112. package/dist/crdt/text/index/placement.js +420 -157
  113. package/dist/crdt/text/index/records.d.ts +3 -3
  114. package/dist/crdt/text/index/records.js +24 -31
  115. package/dist/crdt/text/index/shared.d.ts +11 -3
  116. package/dist/crdt/text/index/shared.js +29 -17
  117. package/dist/crdt/text/index/state.d.ts +21 -17
  118. package/dist/crdt/text/index/state.js +15 -17
  119. package/dist/crdt/text/index/streams.d.ts +10 -5
  120. package/dist/crdt/text/index/streams.js +78 -27
  121. package/dist/crdt/text/items.d.ts +10 -3
  122. package/dist/crdt/text/items.js +7 -0
  123. package/dist/crdt/text/model.d.ts +87 -62
  124. package/dist/crdt/text/model.js +91 -169
  125. package/dist/crdt/text/ranges.d.ts +1 -1
  126. package/dist/crdt/text/ranges.js +3 -3
  127. package/dist/crdt/text/rows.d.ts +13 -7
  128. package/dist/crdt/text/rows.js +24 -7
  129. package/dist/crdt/text/runs.d.ts +51 -42
  130. package/dist/crdt/text/runs.js +24 -7
  131. package/dist/crdt/text/streams.d.ts +26 -13
  132. package/dist/crdt/text/streams.js +41 -14
  133. package/dist/crdt/vendor/yjs/UPSTREAM.md +651 -21
  134. package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
  135. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  136. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
  137. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +197 -10
  138. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
  139. package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
  140. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
  141. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
  142. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
  143. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
  144. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
  145. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +54 -7
  146. package/dist/crdt/vendor/yjs/global.d.ts +2 -0
  147. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  148. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
  149. package/dist/crdt/vendor/yjs/src/structs/Item.js +649 -51
  150. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
  151. package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
  152. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
  153. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
  154. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
  155. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
  156. package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
  157. package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
  158. package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
  159. package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
  160. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
  161. package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
  162. package/dist/crdt/vendor/yjs/src/ynode.js +199 -12
  163. package/dist/edytor.svelte.d.ts +1 -1
  164. package/dist/edytor.svelte.js +103 -75
  165. package/dist/events/beforeInputCommands.d.ts +2 -2
  166. package/dist/events/beforeInputCommands.js +2 -2
  167. package/dist/events/nativeInteractiveControl.d.ts +11 -1
  168. package/dist/events/nativeInteractiveControl.js +41 -1
  169. package/dist/events/onBeforeInput.js +3 -3
  170. package/dist/events/onFocus.js +9 -11
  171. package/dist/events/onPageHide.d.ts +3 -0
  172. package/dist/events/onPageHide.js +16 -0
  173. package/dist/export/forms.d.ts +50 -50
  174. package/dist/index.d.ts +4 -2
  175. package/dist/index.js +1 -1
  176. package/dist/labels.d.ts +8 -0
  177. package/dist/labels.js +6 -1
  178. package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
  179. package/dist/plugins/comments/CommentsController.svelte.js +14 -4
  180. package/dist/plugins/find/search.js +2 -2
  181. package/dist/plugins/index.d.ts +1 -0
  182. package/dist/plugins/index.js +1 -0
  183. package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
  184. package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
  185. package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
  186. package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
  187. package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
  188. package/dist/plugins/suggestions/shared.svelte.js +421 -0
  189. package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
  190. package/dist/selection/domSelection.d.ts +2 -0
  191. package/dist/selection/domSelection.js +6 -2
  192. package/dist/selection/pointer.svelte.js +75 -48
  193. package/dist/selection/replaceSelection.js +2 -6
  194. package/dist/selection/selection.svelte.d.ts +3 -3
  195. package/dist/selection/selection.svelte.js +39 -32
  196. package/dist/selection/selection.utils.d.ts +14 -0
  197. package/dist/selection/selection.utils.js +28 -3
  198. package/dist/session/commands.js +21 -11
  199. package/dist/session/composition.svelte.d.ts +5 -3
  200. package/dist/session/composition.svelte.js +21 -17
  201. package/dist/session/handles.d.ts +4 -17
  202. package/dist/session/handles.js +11 -37
  203. package/dist/session/navigation.js +6 -9
  204. package/dist/session/selection.d.ts +11 -5
  205. package/dist/session/selection.js +14 -48
  206. package/dist/session/suggestions.svelte.d.ts +38 -9
  207. package/dist/session/suggestions.svelte.js +37 -18
  208. package/dist/session/virtual.d.ts +1 -1
  209. package/dist/surface/attributes.d.ts +5 -0
  210. package/dist/surface/attributes.js +2 -2
  211. package/dist/surface/cells.d.ts +2 -26
  212. package/dist/surface/cells.js +2 -33
  213. package/dist/surface/observer.svelte.d.ts +6 -1
  214. package/dist/surface/observer.svelte.js +130 -83
  215. package/dist/surface/pin.svelte.d.ts +9 -9
  216. package/dist/surface/pin.svelte.js +15 -22
  217. package/dist/surface/projector.svelte.d.ts +15 -3
  218. package/dist/surface/projector.svelte.js +67 -22
  219. package/dist/text/text.svelte.js +14 -19
  220. package/dist/themes/notion.css +29 -0
  221. package/package.json +4 -2
  222. package/dist/crdt/placement/rank.d.ts +0 -87
  223. package/dist/crdt/placement/rank.js +0 -332
  224. package/dist/crdt/placement/source.d.ts +0 -43
  225. package/dist/crdt/placement/source.js +0 -66
  226. package/dist/crdt/text/delete-index.d.ts +0 -129
  227. package/dist/crdt/text/delete-index.js +0 -365
  228. package/dist/crdt/text/deletes.d.ts +0 -76
  229. package/dist/crdt/text/deletes.js +0 -362
  230. package/dist/crdt/text/index/anchored.d.ts +0 -17
  231. 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 winning
7
- * placement of the blocks whose candidates (or whose parent's
8
- * registry entry) changed, and the display slot of every block whose
9
- * slot walk (`displaySlotOf`) reads one that changed — its stored
10
- * subtree, through the blocks it displays — patched into the parents'
11
- * child lists. While every block shows its winning candidate (no
12
- * cycle rejected, none rehomed), a block's placement is its argmax
13
- * (`resolvePlacements` accepts it), so only a cycle through a changed
14
- * display edge needs the global resolution, which then runs whole.
15
- * The layout rules re-run only on the layouts a change reaches.
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, resolvePlacements, textRanker } from '../../placement/model.js';
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 { blocks, displaysMap, ownerChanged, streamIx, placed, placementsMap, regroup, byArgParent, following, dirtyLists, leftLists, placementSeeds, stateSeeds, kindsOf, ensureOwners, ownerOf, top, streamOf, streamsIn, streamAt, role, lineKind, itemKind, rowKind, rowOfTable, declaredRows, cellKind, cellRank, declaredItems, dissolve, redissolve } = ix;
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) => role(b, (r, type) => r.childless(type)) === true,
50
- island: (b) => role(b, (r, type) => r.island(type)) === true,
51
- container: (b) => role(b, (r, type) => r.container(type)) === true,
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 kidsOf.get(x) ?? [])
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 kidsOf.get(d) ?? [])
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
- /** The placement `resolvePlacements` gives `id` while every block shows its argmax. */
235
- const argmaxOf = (id) => {
236
- const rec = blocks.get(id);
237
- if (rec === undefined)
238
- return undefined;
239
- const c = rec.cands[0];
240
- if (c === undefined)
241
- return null;
242
- const p = c.p !== null && !blocks.has(c.p) ? null : c.p;
243
- return { parent: p, rank: c.r };
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
- /** A display edge walk from `id`'s parent reaches `id` (or loops): the argmax graph has a cycle. */
246
- const cyclic = (id) => {
247
- let steps = 0;
248
- for (let cur = placementsMap.get(id)?.parent ?? null; cur !== null;) {
249
- const o = ownerOf(cur);
250
- const at = o === DEAD ? cur : o;
251
- if (at === id || ++steps > blocks.size)
252
- return true;
253
- const cp = placementsMap.get(at);
254
- if (cp === undefined)
255
- return false;
256
- cur = cp.parent;
257
- }
258
- return false;
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
- dropFrom(kidsOf, old.parent, id);
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
- addTo(kidsOf, pl.parent, id);
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
- /** Everything rebuilt from the records (the first build, roles, an irregular state). */
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
- ix.itemKinds = declaredItems();
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(ix.roles?.lineKinds());
286
- ix.rowKinds = declaredRows();
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
- const resolved = resolvePlacements(blocks, ownerOf);
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
- kidsOf.clear();
305
- irregular = false;
306
- for (const [id, pl] of resolved) {
307
- setPlacement(id, pl);
308
- const arg = argmaxOf(id);
309
- if (!arg || arg.parent !== pl.parent || arg.rank !== pl.rank)
310
- irregular = true;
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 = (own, map) => {
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(own0, ix.kids0);
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(ownShim, ix.kidsMap);
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 || irregular) {
341
- if (ix.placementFull || placementSeeds.size + stateSeeds.size + ownerChanged.size > 0)
342
- rebuildPlacements();
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
- for (const id of placementSeeds) {
349
- // A candidate change (a move, even to the same slot) re-decides its text's order.
350
- const home = streamIx.get(id)?.home;
351
- if (home !== undefined)
352
- regroup.add(home);
353
- const next = argmaxOf(id);
354
- if (next === null)
355
- return rebuildPlacements();
356
- const old = placementsMap.get(id);
357
- if (old?.parent === next?.parent && old?.rank === next?.rank)
358
- continue;
359
- setPlacement(id, next);
360
- moved.add(id);
361
- }
362
- // A changed display edge closing a cycle needs the global resolution.
363
- for (const id of moved)
364
- if (cyclic(id))
365
- return rebuildPlacements();
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 kidsOf.get(o) ?? [])
368
- if (cyclic(c))
369
- return rebuildPlacements();
370
- const seeds = new Set([...moved, ...stateSeeds, ...ownerChanged]);
371
- // Every block of a text whose pieces' order may have changed re-reads its slot.
372
- for (const home of regroup)
373
- for (const b of ranker.regroup(home))
374
- if (placementsMap.has(b))
375
- seeds.add(b);
376
- regroup.clear();
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(seeds);
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
- if (ix.roles === null || slot === undefined)
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
- if (!lined && (from === undefined || stored !== ix.roles.defaultChild(from)))
441
- return stored === ix.roles.defaultChild(null) && under !== null
442
- ? itemOf(under, stored)
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' && ix.roles.container(from))
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 (ix.roles.container(t) &&
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 = ix.roles.defaultChild(under === null ? null : typeOf(under));
456
- if (ix.roles.rendersContent(kind) || !ix.roles.rendersContent(stored))
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 ? ix.roles.defaultChild(null) : stored;
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
- argParent,
469
- noteCands,
729
+ homeFor,
730
+ putSlot,
731
+ edgeParent,
470
732
  noteFollowing,
471
733
  ensurePlacements,
734
+ reslot,
472
735
  typeOf,
473
736
  order
474
737
  };