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.
Files changed (143) hide show
  1. package/README.md +2 -2
  2. package/dist/cloudflare/DocumentRoom.d.ts +13 -9
  3. package/dist/cloudflare/DocumentRoom.js +10 -6
  4. package/dist/cloudflare/room/context.d.ts +27 -26
  5. package/dist/cloudflare/room/context.js +2 -2
  6. package/dist/cloudflare/room/purge.d.ts +6 -3
  7. package/dist/cloudflare/room/purge.js +44 -19
  8. package/dist/cloudflare/room/updates.d.ts +3 -4
  9. package/dist/cloudflare/room/updates.js +6 -8
  10. package/dist/crdt/attribution/attribution.d.ts +31 -12
  11. package/dist/crdt/attribution/attribution.js +56 -18
  12. package/dist/crdt/attribution/block.d.ts +3 -3
  13. package/dist/crdt/attribution/block.js +7 -8
  14. package/dist/crdt/attribution/index.d.ts +1 -1
  15. package/dist/crdt/attribution/index.js +1 -1
  16. package/dist/crdt/data.d.ts +62 -31
  17. package/dist/crdt/data.js +211 -103
  18. package/dist/crdt/doc/delete.d.ts +1 -1
  19. package/dist/crdt/doc/delete.js +13 -30
  20. package/dist/crdt/doc/funnel.js +29 -24
  21. package/dist/crdt/doc/gate.d.ts +3 -3
  22. package/dist/crdt/doc/history.d.ts +25 -9
  23. package/dist/crdt/doc/history.js +150 -40
  24. package/dist/crdt/doc/layout.js +11 -22
  25. package/dist/crdt/doc/meta.js +12 -17
  26. package/dist/crdt/doc/moves.d.ts +1 -1
  27. package/dist/crdt/doc/moves.js +52 -32
  28. package/dist/crdt/doc/reads.js +2 -2
  29. package/dist/crdt/doc/seed.js +2 -2
  30. package/dist/crdt/doc/split-merge.js +12 -15
  31. package/dist/crdt/doc/steps.d.ts +25 -13
  32. package/dist/crdt/doc/steps.js +52 -122
  33. package/dist/crdt/doc/types.d.ts +21 -9
  34. package/dist/crdt/document.d.ts +16 -5
  35. package/dist/crdt/document.js +21 -4
  36. package/dist/crdt/edytor-doc.d.ts +62 -64
  37. package/dist/crdt/edytor-doc.js +18 -13
  38. package/dist/crdt/engine-api.d.ts +33 -1
  39. package/dist/crdt/engine.d.ts +0 -1
  40. package/dist/crdt/engine.js +1 -4
  41. package/dist/crdt/flow.d.ts +1 -1
  42. package/dist/crdt/flow.js +25 -27
  43. package/dist/crdt/index.d.ts +26 -25
  44. package/dist/crdt/index.js +1 -1
  45. package/dist/crdt/placement/display.d.ts +5 -30
  46. package/dist/crdt/placement/display.js +39 -144
  47. package/dist/crdt/placement/keys.d.ts +58 -0
  48. package/dist/crdt/placement/keys.js +90 -0
  49. package/dist/crdt/placement/model.d.ts +119 -118
  50. package/dist/crdt/placement/model.js +242 -142
  51. package/dist/crdt/placement/resolve.d.ts +123 -35
  52. package/dist/crdt/placement/resolve.js +187 -153
  53. package/dist/crdt/placement/slots.d.ts +134 -0
  54. package/dist/crdt/placement/slots.js +152 -0
  55. package/dist/crdt/providers/room.d.ts +16 -2
  56. package/dist/crdt/providers/room.js +64 -17
  57. package/dist/crdt/providers/websocket.js +5 -3
  58. package/dist/crdt/purge.d.ts +17 -8
  59. package/dist/crdt/purge.js +236 -126
  60. package/dist/crdt/restore.d.ts +2 -2
  61. package/dist/crdt/restore.js +18 -34
  62. package/dist/crdt/schema.d.ts +22 -21
  63. package/dist/crdt/schema.js +29 -23
  64. package/dist/crdt/storage.d.ts +1 -1
  65. package/dist/crdt/storage.js +1 -1
  66. package/dist/crdt/structs.d.ts +15 -15
  67. package/dist/crdt/structs.js +8 -22
  68. package/dist/crdt/text/index/cache.js +4 -0
  69. package/dist/crdt/text/index/claims.js +12 -0
  70. package/dist/crdt/text/index/fold.d.ts +2 -3
  71. package/dist/crdt/text/index/fold.js +194 -44
  72. package/dist/crdt/text/index/layout.js +5 -8
  73. package/dist/crdt/text/index/placement.d.ts +26 -18
  74. package/dist/crdt/text/index/placement.js +404 -138
  75. package/dist/crdt/text/index/records.d.ts +3 -3
  76. package/dist/crdt/text/index/records.js +24 -31
  77. package/dist/crdt/text/index/shared.d.ts +11 -3
  78. package/dist/crdt/text/index/shared.js +12 -3
  79. package/dist/crdt/text/index/state.d.ts +21 -17
  80. package/dist/crdt/text/index/state.js +15 -17
  81. package/dist/crdt/text/index/streams.d.ts +10 -5
  82. package/dist/crdt/text/index/streams.js +78 -27
  83. package/dist/crdt/text/items.d.ts +10 -3
  84. package/dist/crdt/text/items.js +7 -0
  85. package/dist/crdt/text/model.d.ts +77 -62
  86. package/dist/crdt/text/model.js +84 -169
  87. package/dist/crdt/text/ranges.d.ts +1 -1
  88. package/dist/crdt/text/ranges.js +3 -3
  89. package/dist/crdt/text/rows.d.ts +13 -7
  90. package/dist/crdt/text/rows.js +24 -7
  91. package/dist/crdt/text/runs.d.ts +17 -12
  92. package/dist/crdt/text/runs.js +24 -7
  93. package/dist/crdt/text/streams.d.ts +26 -13
  94. package/dist/crdt/text/streams.js +41 -14
  95. package/dist/crdt/vendor/yjs/UPSTREAM.md +568 -21
  96. package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
  97. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  98. package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
  99. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +194 -10
  100. package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
  101. package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
  102. package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
  103. package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
  104. package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
  105. package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
  106. package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
  107. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +43 -7
  108. package/dist/crdt/vendor/yjs/global.d.ts +2 -0
  109. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  110. package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
  111. package/dist/crdt/vendor/yjs/src/structs/Item.js +610 -51
  112. package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
  113. package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
  114. package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
  115. package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
  116. package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
  117. package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
  118. package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
  119. package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
  120. package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
  121. package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
  122. package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
  123. package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
  124. package/dist/crdt/vendor/yjs/src/ynode.js +177 -12
  125. package/dist/edytor.svelte.js +4 -1
  126. package/dist/events/beforeInputCommands.js +2 -2
  127. package/dist/events/onPageHide.d.ts +3 -0
  128. package/dist/events/onPageHide.js +16 -0
  129. package/dist/export/forms.d.ts +25 -25
  130. package/dist/surface/observer.svelte.js +2 -2
  131. package/dist/surface/projector.svelte.d.ts +15 -3
  132. package/dist/surface/projector.svelte.js +59 -8
  133. package/package.json +2 -2
  134. package/dist/crdt/placement/rank.d.ts +0 -87
  135. package/dist/crdt/placement/rank.js +0 -332
  136. package/dist/crdt/placement/source.d.ts +0 -43
  137. package/dist/crdt/placement/source.js +0 -66
  138. package/dist/crdt/text/delete-index.d.ts +0 -129
  139. package/dist/crdt/text/delete-index.js +0 -365
  140. package/dist/crdt/text/deletes.d.ts +0 -76
  141. package/dist/crdt/text/deletes.js +0 -362
  142. package/dist/crdt/text/index/anchored.d.ts +0 -17
  143. package/dist/crdt/text/index/anchored.js +0 -197
@@ -37,16 +37,13 @@ export const Y = {
37
37
  createIdSet: V.createIdSet,
38
38
  diffIdSet: V.diffIdSet,
39
39
  insertIntoIdSet: V.insertIntoIdSet,
40
- // text delete marks (`text/deletes.ts`, fork patch YP11)
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
  };
@@ -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), all at plain ranks (`insertBlocks`: their order
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
- return 'writes' in p ? { ...c.plan(p.ids, [...pre, ...p.writes]), at } : p;
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 = [], ranks = c.ranksFor(to, 0, (l?.children?.length ?? 0) + moved.length)) => {
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, ranks });
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, ranks) => {
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, [], ranks);
210
+ kids(B, l);
215
211
  return end;
216
212
  };
217
- /** The placed lines at `at` under `home`, `ranks` theirs. */
218
- const place = (at, ranks) => {
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({ op: 'insertBlocks', parent: home, index: at, specs: placed, ranks });
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 lead = inside ? c.ranksFor(B, 0, k + placed.length + 1) : [];
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, c.ranksFor(parent, index, placed.length, [], true));
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, ranks });
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, inside ? lead.slice(0, k) : undefined) : 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, inside ? lead.slice(k, -1) : c.pieceRanks(B, o, placed.length));
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
- // By the offset the paste splits `B` at (SW12-crdt-4), as Enter's split.
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
- rank
279
+ index: slot
282
280
  });
283
281
  kids(T, joinsTail ? last() : undefined, moved);
284
- place(slot, ranks);
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 });
@@ -75,7 +75,7 @@ export declare const bindCrdt: (Y: EngineApi) => {
75
75
  Awareness: typeof _Awareness;
76
76
  doc: {
77
77
  SCHEMA: {
78
- readonly version: 6;
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: 6;
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
- ranksAt: (siblings: readonly {
161
- rank: string;
162
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
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, place: import("./placement/model.js").PlacementValue) => void;
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) => import("./text/model.js").MergeClaim[];
208
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
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: "right" | "left", kind?: string | undefined) => import("./edytor-doc.js").OpResult;
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
- ranksAt: (siblings: readonly {
372
- rank: string;
373
- }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
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, place: import("./placement/model.js").PlacementValue) => void;
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) => import("./text/model.js").MergeClaim[];
419
- claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
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;
@@ -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 (`b/<blockId>`
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, the text order of a
5
- * text's pieces (`textRanker`), liveness, and the inverse of the claim
6
- * owners. Pure over replicated state and the ownership the caller passes.
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, BlockRec, ChildSlot, DisplayOwnership, DocOrder, ModelView, ResolvedPlacement } from './model.js';
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, the text order of a
5
- * text's pieces (`textRanker`), liveness, and the inverse of the claim
6
- * owners. Pure over replicated state and the ownership the caller passes.
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 './source.js';
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 piece of a text shown under its own placement's parent takes its text-order rank;
205
- // a table's cell its column's (`table.columns`).
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
- const ruled = owner === null ? undefined : own.slotRank?.(owner, id, rank);
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 { parent: owner, rank: hops === 0 ? direct(owner) : rank, reset };
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";