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
@@ -48,10 +48,10 @@
48
48
  */
49
49
  import { setMarkEdges } from './text/marks.js';
50
50
  import { keepingReplaced } from './incarnations.js';
51
+ import { clientsOf } from './structs.js';
51
52
  import { SCHEMA } from './schema.js';
52
53
  import { bindModel, blockText, crdtId, listBlockIds, project, resolveBlock } from './placement/model.js';
53
54
  import { bindText } from './text/model.js';
54
- import { bindDeletes } from './text/deletes.js';
55
55
  import { bindPurge, readHorizon } from './purge.js';
56
56
  import { restoreDocument } from './restore.js';
57
57
  import { bindRuns, indexOf } from './text/runs.js';
@@ -84,15 +84,29 @@ export { DEFAULT_HISTORY_LIMIT } from './doc/history.js';
84
84
  * The per-doc facade factory over the engine `Y` (the editor's documents
85
85
  * use it directly; {@link bindDocModel} is the public binding around it).
86
86
  */
87
+ /**
88
+ * Give `doc` edytor's rule — a registry value a concurrent creation replaced
89
+ * keeps its subtree (fork patch YP14, `keepingReplaced`) — or refuse it
90
+ * loudly when it has integrated updates without it: the engine decides what
91
+ * a replaced value keeps when it integrates, so a document that took updates
92
+ * under the engine's default could lose a subtree its peers keep, forever.
93
+ * Create it with `createDoc()` (or `createDocument`/`loadDocument`), never
94
+ * `new Y.Doc()`.
95
+ */
96
+ export const adoptRules = (doc) => {
97
+ const d = doc;
98
+ if (d.keepReplaced == null && clientsOf(doc).size > 0)
99
+ throw new TypeError("edytor: this document took updates before it carried edytor's rules (create it with createDoc(), createDocument or loadDocument, not new Y.Doc())");
100
+ return keepingReplaced(d);
101
+ };
87
102
  export const docFacade = (Y, layers) => {
88
- const D = bindDeletes(Y);
89
103
  // Compact per-block attribution writes (`attribution/block.ts`).
90
104
  // One bound instance per engine binding; its suppression memory is
91
105
  // per-doc (WeakMap-keyed), so facades on the same doc share it.
92
106
  const BA = bindBlockAttribution(Y);
93
107
  const { init, seed } = bindSeed(Y, BA);
94
- return (doc, config = {}) => {
95
- keepingReplaced(doc);
108
+ const create = (doc, config = {}) => {
109
+ adoptRules(doc);
96
110
  setMarkEdges(doc, config.markEdge ?? (() => undefined));
97
111
  const roleOf = config.roleOf ?? (() => undefined);
98
112
  const defaultType = config.defaultType ?? 'paragraph';
@@ -109,21 +123,42 @@ export const docFacade = (Y, layers) => {
109
123
  // A layout displays only its items — its
110
124
  // `defaultChild` kind — and only two or more (`layout.*`). A table
111
125
  // displays only its rows and a row only its cells (`table.*`).
126
+ let kinds = new Map();
127
+ let declared;
112
128
  const roles = {
113
- childless: (type) => roleOf(type)?.void === true,
114
- island: (type) => roleOf(type)?.island === true,
115
- container: (type) => !rendersContentOf(type) && roleOf(type)?.void !== true && roleOf(type)?.island !== true,
116
- rendersContent: (type) => rendersContentOf(type),
117
- defaultChild: (type) => (type !== null ? defaultChildOf(type) : undefined) ?? defaultType,
118
- line: (type) => {
119
- const role = roleOf(type);
120
- return role?.island === true && role.lines === true ? defaultChildOf(type) : undefined;
129
+ of: (type) => {
130
+ let k = kinds.get(type);
131
+ if (k === undefined) {
132
+ const role = type === null ? undefined : roleOf(type);
133
+ const own = type === null ? undefined : defaultChildOf(type);
134
+ const renders = type === null || rendersContentOf(type);
135
+ kinds.set(type, (k = {
136
+ childless: role?.void === true,
137
+ island: role?.island === true,
138
+ container: !renders && role?.void !== true && role?.island !== true,
139
+ rendersContent: renders,
140
+ child: own ?? defaultType,
141
+ line: role?.island === true && role.lines === true ? own : undefined,
142
+ layout: role?.layout === true ? own : undefined,
143
+ table: role?.table === true ? own : undefined
144
+ }));
145
+ }
146
+ return k;
121
147
  },
122
- lineKinds: () => [...(config.kinds?.() ?? [])].flatMap((type) => roles.line(type) ?? []),
123
- layout: (type) => (roleOf(type)?.layout === true ? defaultChildOf(type) : undefined),
124
- layoutKinds: () => [...(config.kinds?.() ?? [])].filter((type) => roles.layout(type) !== undefined),
125
- table: (type) => (roleOf(type)?.table === true ? defaultChildOf(type) : undefined),
126
- tableKinds: () => [...(config.kinds?.() ?? [])].filter((type) => roles.table(type) !== undefined)
148
+ declared: () => {
149
+ if (declared === undefined) {
150
+ const all = [...(config.kinds?.() ?? [])].map(roles.of);
151
+ const rows = new Map(all.flatMap((k) => (k.table ? [[k.table, roles.of(k.table).child]] : [])));
152
+ declared = {
153
+ lines: new Set(all.flatMap((k) => k.line ?? [])),
154
+ layouts: [...(config.kinds?.() ?? [])].filter((t) => roles.of(t).layout !== undefined),
155
+ items: new Set(all.flatMap((k) => k.layout ?? [])),
156
+ rows,
157
+ cells: new Set(rows.values())
158
+ };
159
+ }
160
+ return declared;
161
+ }
127
162
  };
128
163
  if (config.roleOf)
129
164
  runsView.roles(roles);
@@ -141,56 +176,55 @@ export const docFacade = (Y, layers) => {
141
176
  funnel.dispose();
142
177
  events.close();
143
178
  };
144
- const { createUndoManager } = docHistory({ ...base, ...funnel, D, init });
179
+ const { createUndoManager } = docHistory({ ...base, ...funnel, init });
145
180
  const { anchorAt, followUndo, resolveAnchor } = docAnchors(reads);
146
181
  const steps = { ...shape, ...planSteps(shape) };
147
182
  const { plan } = steps;
148
- // The prepared op families (`OpsContext`: the reads, capability and step writers).
149
- const { insertBlocks, moveBlocks, unNestBlocks, unNestBlock, landingOf, liftOut, duplicateBlock } = moveOps(steps);
150
- const { besideAt, placeBeside, wrapInLayout } = layoutOps(steps);
151
- const { splitBlock, mergeBlocks, mergeBackward, mergeForward } = splitMergeOps(steps);
152
- const { deleteBlocks, deleteBlock } = deleteOps(steps);
153
- const { dataSteps, replaceData, setBlockType, patchData, setBlock } = metaOps(steps);
154
- const { insertText, deleteText, formatRange, clearMarks, insertInline, removeInline } = contentOps(steps);
183
+ // The prepared op families (`OpsContext`: the reads, capability and step
184
+ // writers): every op they answer is a document write, but for the reads
185
+ // and step writers taken out here.
186
+ const { landingOf, ...moves } = moveOps(steps);
187
+ const { besideAt, ...layouts } = layoutOps(steps);
188
+ const { dataSteps, replaceData, ...metas } = metaOps(steps);
189
+ const deletes = deleteOps(steps);
155
190
  /** What range deletion and flow placement compose: the step writers and the ops. */
156
- const ops = { ...steps, insertBlocks, deleteBlocks, dataSteps, replaceData };
191
+ const ops = { ...steps, ...moves, ...deletes, dataSteps, replaceData };
157
192
  /** Every document op, prepared — `apply(prepare.op(…))` is the op. */
158
193
  const prepare = {
159
- insertBlocks,
160
- moveBlocks,
194
+ ...moves,
195
+ ...layouts,
196
+ ...splitMergeOps(steps),
197
+ ...deletes,
198
+ ...metas,
199
+ ...contentOps(steps),
161
200
  /**
162
201
  * Move `id` to the last position under `parent` — under a container
163
202
  * it is no item of, under its last item (`nestParent`: Tab after a list).
164
203
  */
165
204
  nestBlock: (id, parent) => {
166
205
  const at = nestParent([ref(id)], ref(parent));
167
- return moveBlocks([id], { parent: at, index: childrenIds(at).length });
206
+ return moves.moveBlocks([id], { parent: at, index: childrenIds(at).length });
168
207
  },
169
- unNestBlock,
170
- unNestBlocks,
171
- liftOut,
172
- placeBeside,
173
- wrapInLayout,
174
- splitBlock,
175
- mergeBlocks,
176
- mergeBackward,
177
- mergeForward,
178
- deleteBlock,
179
- setBlock,
180
- setBlockType,
181
- patchData,
182
- duplicateBlock,
183
- insertText,
184
- deleteText,
185
- formatRange,
186
- clearMarks,
187
- insertInline,
188
- removeInline,
189
208
  /** Delete a block selection — one plan; only the members leave (`deleteBlocks` above). */
190
- deleteBlocks: (ids) => deleteBlocks(ids),
209
+ deleteBlocks: (ids) => deletes.deleteBlocks(ids),
191
210
  ...rangeDeleteOps(ops),
192
211
  ...flowOps(ops)
193
212
  };
213
+ /**
214
+ * What a part built over this document reads beyond the facade (the
215
+ * shared suggestions, `doc/suggest.ts`): the reads, a facade over
216
+ * another doc (`create`, with this `config`);
217
+ * `v` counts its changes no index fact shows (in `version`), and
218
+ * `base`, when set, is the export `toJSON()` gives unless asked for
219
+ * the suggestions.
220
+ */
221
+ const part = {
222
+ ...reads,
223
+ config,
224
+ create: create,
225
+ v: 0,
226
+ base: undefined
227
+ };
194
228
  /**
195
229
  * Plans prepared at one version, as one plan (refused if any part is;
196
230
  * stamped with the oldest part's version, so `apply` refuses a stale
@@ -212,7 +246,11 @@ export const docFacade = (Y, layers) => {
212
246
  * omitted when empty): the one serializer, {@link blockJSON}, over the
213
247
  * root's visible children.
214
248
  */
215
- const toJSON = () => {
249
+ const toJSON = (options) => {
250
+ // A part's export first (the shared suggestions': without the pending ones, `sugg.json.base`).
251
+ const base = !options?.suggestions && part.base?.();
252
+ if (base)
253
+ return base;
216
254
  const data = docData();
217
255
  const children = childrenIds(null).map(blockJSON);
218
256
  return Object.keys(data).length > 0
@@ -222,32 +260,17 @@ export const docFacade = (Y, layers) => {
222
260
  // ── facade object ─────────────────────────────────────────────────
223
261
  /** A read taking an id first: the id normalizes at ingress. */
224
262
  const byRef = (f) => (id, ...rest) => f(ref(id), ...rest);
225
- const facade = {
226
- // lifecycle
227
- init: (opts) => write(() => init(doc, opts)),
228
- /** Apply the deterministic seed of `value` and the document's `data` — the document's seed decision. */
229
- seed: (value, data) => write(() => seed(doc, value, defaultType, data)),
230
- isInitialized: () => isInitialized(doc),
231
- /** Replicated `meta.v` schema version (the module-level read, bound). */
232
- schemaVersion: () => schemaVersion(doc),
233
- /**
234
- * The schema gate for this doc — throws {@link SchemaMismatchError}
235
- * on unversioned-content or unsupported-version state. Consumers
236
- * (`Edytor.sync()`, packed consumers) call it before trusting
237
- * a synced doc.
238
- */
239
- checkSchema: () => checkSchema(doc),
240
- assertSchema: () => assertSchema(doc),
241
- dispose,
242
- createUndoManager,
263
+ /**
264
+ * The public operations (`DocumentOperations`), each name once: what
265
+ * an `EdytorDocument` forwards. The facade has them and the internal
266
+ * members below.
267
+ */
268
+ const api = {
243
269
  // reads — every id argument normalizes at ingress
244
270
  project: () => project(doc),
245
271
  toJSON,
246
272
  blockJSON: byRef(blockJSON),
247
273
  childrenIds: byRef(childrenIds),
248
- /** Visible children of `parent` with their ranks, in `(rank, id)` order. */
249
- childSlots: (parent) => view().kids.get(parent) ?? [],
250
- slotOf: byRef(slotOf),
251
274
  positionOf: byRef(positionOf),
252
275
  pathOf: byRef(pathOf),
253
276
  parentOf: byRef((id) => positionOf(id)?.parent ?? null),
@@ -265,8 +288,6 @@ export const docFacade = (Y, layers) => {
265
288
  * O(1) per call; live-replicated (remote values read the same).
266
289
  */
267
290
  blockAttribution: byRef((id) => blockAttributionOf(doc, id)),
268
- crdtId: byRef((id) => crdtId(doc, id)),
269
- resolveBlock: byRef((id) => resolveBlock(doc, id)),
270
291
  displayLength: byRef(displayLength),
271
292
  contentItems: byRef(contentItems),
272
293
  hasBlock: byRef(hasBlock),
@@ -278,10 +299,6 @@ export const docFacade = (Y, layers) => {
278
299
  // caret anchors — backing-text-bound selection endpoints
279
300
  anchorAt: byRef(anchorAt),
280
301
  resolveAnchor,
281
- followUndo,
282
- // roles
283
- /** Re-read the roles after `roleOf` answers differently (roles adopted later). */
284
- rolesChanged: () => runsView.roles(roles),
285
302
  isVoid: byRef(isVoid),
286
303
  isIsland: byRef(isIsland),
287
304
  isLines: byRef(isLines),
@@ -317,10 +334,8 @@ export const docFacade = (Y, layers) => {
317
334
  nestParent: (ids, parent) => nestParent(ids.map(ref), ref(parent)),
318
335
  /** Where a block of `kind` at `id`'s place lands, and the containers it leaves (`liftOut`). */
319
336
  landingOf: (id, kind, after) => landingOf(ref(id), ref(kind), after),
320
- defaultChild: byRef(defaultChild),
321
337
  // maintained runs (bound to this doc)
322
338
  runs: byRef(runsView.runs),
323
- contentJSON: byRef(runsView.contentJSON),
324
339
  // events
325
340
  onChange,
326
341
  // every op — each returns an {@link OpResult}; `apply(prepare.op(…))`
@@ -333,10 +348,6 @@ export const docFacade = (Y, layers) => {
333
348
  // transactions (composed ops already run in one; expose for callers
334
349
  // that batch several ops into one undo step / one event)
335
350
  transact: (fn, origin) => write(() => doc.transact(fn, origin)),
336
- // escape hatch for the history and debugging — the bound engine layers.
337
- model: layers.model,
338
- text: layers.text,
339
- runsView,
340
351
  // ── model-state version ─────────────────────────────
341
352
  /**
342
353
  * The index version — bumps on every fold that changed derived
@@ -345,11 +356,57 @@ export const docFacade = (Y, layers) => {
345
356
  * post-write state — this is the read-your-writes contract.
346
357
  */
347
358
  get version() {
348
- return runsView.version();
359
+ // A part's change may show in no index fact (a suggestion's decision).
360
+ return runsView.version() + part.v;
349
361
  }
350
362
  };
351
- return facade;
363
+ const internal = {
364
+ // lifecycle
365
+ init: (opts) => write(() => init(doc, opts)),
366
+ /** Apply the deterministic seed of `value` and the document's `data` — the document's seed decision. */
367
+ seed: (value, data) => write(() => seed(doc, value, defaultType, data)),
368
+ isInitialized: () => isInitialized(doc),
369
+ /** Replicated `meta.v` schema version (the module-level read, bound). */
370
+ schemaVersion: () => schemaVersion(doc),
371
+ /**
372
+ * The schema gate for this doc — throws {@link SchemaMismatchError}
373
+ * on unversioned-content or unsupported-version state. Consumers
374
+ * (`Edytor.sync()`, packed consumers) call it before trusting
375
+ * a synced doc.
376
+ */
377
+ checkSchema: () => checkSchema(doc),
378
+ assertSchema: () => assertSchema(doc),
379
+ dispose,
380
+ createUndoManager,
381
+ /** @internal What a part built over the document reads and writes (`doc/suggest.ts`). */
382
+ part,
383
+ /** Visible children of `parent` with their ranks, in `(rank, id)` order. */
384
+ childSlots: (parent) => view().kids.get(parent) ?? [],
385
+ slotOf: byRef(slotOf),
386
+ crdtId: byRef((id) => crdtId(doc, id)),
387
+ resolveBlock: byRef((id) => resolveBlock(doc, id)),
388
+ followUndo,
389
+ // roles
390
+ /** Re-read the roles after `roleOf` answers differently (roles adopted later). */
391
+ rolesChanged: (place = true) => {
392
+ kinds = new Map();
393
+ declared = undefined;
394
+ if (place)
395
+ runsView.roles(roles);
396
+ },
397
+ defaultChild: byRef(defaultChild),
398
+ contentJSON: byRef(runsView.contentJSON),
399
+ // escape hatch for the history and debugging — the bound engine layers.
400
+ model: layers.model,
401
+ text: layers.text,
402
+ runsView,
403
+ /** The public members' names (`EdytorDocument` forwards them). */
404
+ api: api
405
+ };
406
+ // The public members joined as they are (the `version` getter stays one).
407
+ return Object.defineProperties(internal, Object.getOwnPropertyDescriptors(api));
352
408
  };
409
+ return create;
353
410
  };
354
411
  /**
355
412
  * Bind the assembled model to a concrete engine surface. `Y` must be the
@@ -361,7 +418,6 @@ export const bindDocModel = (Y) => {
361
418
  const M = bindModel(Y);
362
419
  const T = bindText(Y);
363
420
  const R = bindRuns(Y);
364
- const D = bindDeletes(Y);
365
421
  const { init, seed } = bindSeed(Y, bindBlockAttribution(Y));
366
422
  const create = docFacade(Y, { model: M, text: T });
367
423
  return {
@@ -390,14 +446,6 @@ export const bindDocModel = (Y) => {
390
446
  * not have integrated anything yet.
391
447
  */
392
448
  newDoc: (opts) => keepingReplaced(new Y.Doc(opts)),
393
- /**
394
- * Keep, on `doc`, the text a replica may have to copy again: its
395
- * `gcFilter` then spares a deleted copy's content, as every facade's
396
- * history does. A doc that only relays updates (the room) calls it
397
- * before applying anything, so what it collects and encodes matches
398
- * what an editing replica keeps.
399
- */
400
- keepCopies: (doc) => void D.scope(doc),
401
449
  /** The bound engine layers (same instances the facades use). */
402
450
  model: M,
403
451
  text: T,
@@ -49,6 +49,10 @@ export type EngineItemRef = {
49
49
  */
50
50
  export interface EngineTransaction {
51
51
  changed?: Map<unknown, Set<string | null>>;
52
+ /** The visibility changes hold records made, in order: `client, clock, ±len` per item (`> 0` shown). */
53
+ holds?: number[];
54
+ /** The units whose mover changed (the fork's move records), in order: `client, clock, len` per item. */
55
+ moves?: number[];
52
56
  }
53
57
  /**
54
58
  * Structural minimum of the vendored `Y.Node` (v14) used by the placement
@@ -70,7 +74,34 @@ export interface EngineNode {
70
74
  forEachAttr(f: (value: unknown, key: string, node: EngineNode) => void): void;
71
75
  attrKeys(): IterableIterator<string>;
72
76
  insert(index: number, content: unknown): void;
73
- delete(index: number, length?: number): void;
77
+ /**
78
+ * Delete `length` live units at `index`. With `into` (the document's
79
+ * records list), they are held by one record appended there instead (a
80
+ * delete its writer can take back), except those `fresh` names, which are
81
+ * removed. With `propose` (a suggestion's delete), each
82
+ * unit is sorted: `1` held as above (its author's own), `2` proposed (an
83
+ * id list appended to `propose.into`) or `3` stepped over; proposed and
84
+ * stepped-over units read gone until the engine's `unhide`.
85
+ */
86
+ delete(index: number, length?: number, into?: EngineNode | null, fresh?: unknown, propose?: {
87
+ into: EngineNode;
88
+ sort: (client: number, clock: number) => number;
89
+ } | null): void;
90
+ /** Append one hold record of `ids` (`client, clock, len` triples) to this records list: they read as deleted while it lives. */
91
+ hold(ids: number[]): void;
92
+ /**
93
+ * Move the units `ids` names (`client, clock, len` triples, in order) to
94
+ * live index `index` of this sequence: one move record there (the fork's
95
+ * `ContentMove`); `side` is its class among the items of its origin (`0`
96
+ * after, `1` before), `at` the chain it stands at for the sort of two
97
+ * records at one origin — the units from the root sequence down to the
98
+ * unit it stands at, outermost first (`null`: the first unit it names) —
99
+ * and `part` its order among the records standing there.
100
+ */
101
+ move(index: number, ids: number[], side?: number, part?: number, at?: ReadonlyArray<{
102
+ client: number;
103
+ clock: number;
104
+ }> | null): void;
74
105
  }
75
106
  /** Structural minimum of the vendored `Y.Doc`. */
76
107
  export interface EngineDoc {
@@ -98,6 +129,13 @@ export interface EngineDoc {
98
129
  */
99
130
  on(name: 'commit', f: (transaction: never) => void): void;
100
131
  off(name: 'commit', f: (transaction: never) => void): void;
132
+ /**
133
+ * Doc-level `beforeCommit` channel (the fork's) — fires once per
134
+ * outermost transaction, after its function returned and while the
135
+ * transaction is still open: a listener's writes join it.
136
+ */
137
+ on(name: 'beforeCommit', f: (transaction: never) => void): void;
138
+ off(name: 'beforeCommit', f: (transaction: never) => void): void;
101
139
  /** Doc destruction event — doc-scoped state must release on destroy. */
102
140
  on(name: 'destroy', f: () => void): void;
103
141
  off(name: 'destroy', f: () => void): void;
@@ -30,7 +30,7 @@ export type EngineSymbol =
30
30
  | 'followRedone'
31
31
  | 'isParentOf'
32
32
  | 'mergeIdSets'
33
- | 'ContentAny'
34
- | 'createID';
33
+ | 'createID'
34
+ | 'unhide';
35
35
 
36
36
  export declare const Y: Pick<typeof V, EngineSymbol>;
@@ -37,16 +37,15 @@ 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
- createID: V.createID
48
+ createID: V.createID,
49
+ // a suggestion's proposed deletion reads gone until its plan ends (fork patch YP32)
50
+ unhide: V.unhide
52
51
  };
@@ -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
@@ -82,7 +82,7 @@ export type FlowContext = OpsContext & Pick<MoveOps, 'insertBlocks'> & Pick<Dele
82
82
  * paragraph (`doc.empty.virtual`) place the caret by it.
83
83
  */
84
84
  export declare const placedEnd: (roleOf: (kind: string) => {
85
- void: boolean;
85
+ childless: boolean;
86
86
  rendersContent: boolean;
87
87
  }) => (s: BlockSpec) => DocPosition | undefined;
88
88
  /** `insertFlow`, prepared. */
package/dist/crdt/flow.js CHANGED
@@ -14,7 +14,7 @@ const lengthOf = (l) => (l.content ?? []).reduce((n, i) => n + (i.kind === 'text
14
14
  export const placedEnd = (roleOf) => {
15
15
  const endOf = (s) => {
16
16
  const role = roleOf(s.type);
17
- if (role.rendersContent && !role.void)
17
+ if (role.rendersContent && !role.childless)
18
18
  return { block: s.id, offset: lengthOf(s) };
19
19
  for (const kid of [...(s.children ?? [])].reverse()) {
20
20
  const at = endOf(kid);
@@ -27,15 +27,9 @@ export const placedEnd = (roleOf) => {
27
27
  };
28
28
  /** `insertFlow`, prepared. */
29
29
  export const flowOps = (c) => {
30
- /** A kind's roles, as placement reads them. */
31
- const roleOf = (kind) => ({
32
- void: c.roles.childless(kind),
33
- island: c.roles.island(kind),
34
- rendersContent: c.roles.rendersContent(kind),
35
- layout: c.roles.layout(kind)
36
- });
30
+ const roleOf = c.roles.of;
37
31
  /** What a kind is in a table: the table, its row or its cell. */
38
- const tableKind = (kind) => c.roles.table(kind) !== undefined
32
+ const tableKind = (kind) => roleOf(kind).table !== undefined
39
33
  ? 'table'
40
34
  : c.isRowKind(kind)
41
35
  ? 'row'
@@ -94,7 +88,9 @@ export const flowOps = (c) => {
94
88
  const placed = specs(dest.parent);
95
89
  const p = c.insertBlocks(dest, placed);
96
90
  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;
91
+ // The lines are placed while the replaced blocks still show (each step reads the
92
+ // display as it stands), then those go.
93
+ return 'writes' in p ? { ...c.plan(p.ids, [...p.writes, ...pre]), at } : p;
98
94
  };
99
95
  if ('replace' in target || 'slot' in target) {
100
96
  let slot;
@@ -148,7 +144,7 @@ export const flowOps = (c) => {
148
144
  const apart = (l, under) => {
149
145
  const kind = l.type && fit(under, l.type);
150
146
  const role = kind ? roleOf(kind) : undefined;
151
- return !!role && (!role.rendersContent || role.void || role.island);
147
+ return !!role && (!role.rendersContent || role.childless || role.island);
152
148
  };
153
149
  // At the end of a container's header whose body shows, what follows the caret
154
150
  // leads its children and the body stays, as Enter opens a first child
@@ -188,55 +184,53 @@ export const flowOps = (c) => {
188
184
  return c.REFUSED;
189
185
  const writes = [];
190
186
  /** 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)) => {
187
+ const kids = (to, l, moved = []) => {
192
188
  const k = l?.children?.length ?? 0;
193
189
  if (k)
194
- writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children, ranks });
190
+ writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children });
195
191
  if (moved.length)
196
- writes.push({
197
- op: 'moveBlocks',
198
- ids: moved,
199
- parent: to,
200
- index: k,
201
- ranks: ranks.slice(k)
202
- });
192
+ writes.push({ op: 'moveBlocks', ids: moved, parent: to, index: k });
203
193
  };
204
194
  /**
205
195
  * `l` joins `B` at `at`: its content, its kind when `B` shows no text (a header
206
196
  * keeps its own, as Enter does), its children first.
207
197
  */
208
- const join = (l, at, ranks) => {
198
+ const join = (l, at) => {
209
199
  const end = c.writeContent(writes, B, at, l.content);
210
200
  const kind = l.type && fit(parent, l.type);
211
201
  if (len === 0 && !header && kind && kind === l.type)
212
202
  writes.push(...c.attr(B, TYPE, kind), ...(c.dataSteps(B, c.replaceData(l.data ?? {})) ?? []));
213
203
  if (!shut)
214
- kids(B, l, [], ranks);
204
+ kids(B, l);
215
205
  return end;
216
206
  };
217
- /** The placed lines at `at` under `home`, `ranks` theirs. */
218
- const place = (at, ranks) => {
207
+ /** The placed lines at `at` under `home` (`split`: the tail of the split they come with). */
208
+ const place = (at, split) => {
219
209
  if (placed.length)
220
- writes.push({ op: 'insertBlocks', parent: home, index: at, specs: placed, ranks });
210
+ writes.push({
211
+ op: 'insertBlocks',
212
+ parent: home,
213
+ index: at,
214
+ specs: placed,
215
+ ...(split !== undefined && { split })
216
+ });
221
217
  };
222
218
  // Under a header, the first line's children, the placed lines and the rest, in order.
223
219
  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;
220
+ const slot = inside ? k : index + 1;
226
221
  // Where the placed lines end the caret: after their last shown line (`flow.apart`).
227
222
  const end = placed.length ? endOf(placed.at(-1)) : undefined;
228
223
  // Nothing before the position and the first line stands apart: the lines go before `B`,
229
224
  // which keeps its text and takes a joining last line, or goes when empty and not needed.
230
225
  if (!joinsHead && o === 0 && !inside) {
231
- place(index, c.ranksFor(parent, index, placed.length, [], true));
226
+ place(index);
232
227
  if (joinsTail) {
233
228
  const at = join(last(), 0);
234
229
  // A closed header's taken line's nested lines follow it, the caret ending them.
235
230
  const after = shut ? specs(parent, last().children ?? []) : [];
236
231
  if (after.length) {
237
- const ranks = c.ranksFor(parent, index + 1, after.length, [], true);
238
232
  const slot = index + placed.length + 1;
239
- writes.push({ op: 'insertBlocks', parent, index: slot, specs: after, ranks });
233
+ writes.push({ op: 'insertBlocks', parent, index: slot, specs: after });
240
234
  }
241
235
  const caret = after.length ? endOf(after.at(-1)) : undefined;
242
236
  const touched = [...ids, B, ...after.map((s) => s.id)];
@@ -246,13 +240,13 @@ export const flowOps = (c) => {
246
240
  return { ...c.plan(ids, [...writes, c.remove(B, [])]), at: end };
247
241
  return { ...c.plan([...ids, B], writes), at: end ?? { block: B, offset: 0 } };
248
242
  }
249
- const head = joinsHead ? join(first, o, inside ? lead.slice(0, k) : undefined) : o;
243
+ const head = joinsHead ? join(first, o) : o;
250
244
  if (lines.length === 1 && joinsHead)
251
245
  return { ...c.plan([B], writes), at: { block: B, offset: head } };
252
246
  const moved = inside ? [] : c.childrenIds(B).filter((id) => !view.hidden?.(id));
253
247
  // Nothing after the position to keep, and the caret has a line: no split.
254
248
  if (!joinsTail && o === len && moved.length === 0 && end) {
255
- place(slot, inside ? lead.slice(k, -1) : c.pieceRanks(B, o, placed.length));
249
+ place(slot);
256
250
  return { ...c.plan([B, ...ids], writes), at: end };
257
251
  }
258
252
  // The rest of `B` stays in a shown line: the last line's block when it joins, else
@@ -266,9 +260,7 @@ export const flowOps = (c) => {
266
260
  : bare || inside
267
261
  ? { type: c.defaultChild(home), data: {} }
268
262
  : { 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();
263
+ // After `B` (under a header: after the first line's children); the placed lines go before it.
272
264
  const length = len - o;
273
265
  writes.push({
274
266
  op: 'splitBlock',
@@ -278,10 +270,10 @@ export const flowOps = (c) => {
278
270
  newId: T,
279
271
  tail,
280
272
  parent: home,
281
- rank
273
+ index: slot
282
274
  });
283
275
  kids(T, joinsTail ? last() : undefined, moved);
284
- place(slot, ranks);
276
+ place(slot, T);
285
277
  const at = joinsTail
286
278
  ? { block: T, offset: c.writeContent(writes, T, 0, last().content) }
287
279
  : (end ?? { block: T, offset: 0 });