edytor 4.0.0 → 6.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 (193) 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 +10 -3
  6. package/dist/cloudflare/index.d.ts +1 -1
  7. package/dist/cloudflare/room/access.d.ts +8 -3
  8. package/dist/cloudflare/room/access.js +44 -20
  9. package/dist/cloudflare/room/admission.js +28 -5
  10. package/dist/cloudflare/room/comments.js +28 -1
  11. package/dist/cloudflare/room/context.d.ts +106 -109
  12. package/dist/cloudflare/room/context.js +10 -3
  13. package/dist/cloudflare/room/forged.d.ts +14 -0
  14. package/dist/cloudflare/room/forged.js +30 -2
  15. package/dist/cloudflare/room/frames.d.ts +4 -0
  16. package/dist/cloudflare/room/frames.js +5 -1
  17. package/dist/cloudflare/room/purge.js +2 -1
  18. package/dist/cloudflare/room/replicas.d.ts +4 -2
  19. package/dist/cloudflare/room/replicas.js +8 -3
  20. package/dist/cloudflare/room/suggest.d.ts +7 -0
  21. package/dist/cloudflare/room/suggest.js +143 -0
  22. package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
  23. package/dist/cloudflare/routeDocumentSocket.js +5 -3
  24. package/dist/components/Content.svelte +9 -1
  25. package/dist/crdt/anchors.js +3 -2
  26. package/dist/crdt/attribution/attribution.d.ts +3 -0
  27. package/dist/crdt/attribution/attribution.js +3 -3
  28. package/dist/crdt/doc/funnel.d.ts +1 -1
  29. package/dist/crdt/doc/funnel.js +5 -2
  30. package/dist/crdt/doc/gate.d.ts +1 -1
  31. package/dist/crdt/doc/history.d.ts +6 -0
  32. package/dist/crdt/doc/history.js +79 -59
  33. package/dist/crdt/doc/ops.d.ts +81 -0
  34. package/dist/crdt/doc/ops.js +1898 -0
  35. package/dist/crdt/doc/reads.d.ts +2 -2
  36. package/dist/crdt/doc/reads.js +18 -28
  37. package/dist/crdt/doc/suggest.d.ts +167 -0
  38. package/dist/crdt/doc/suggest.js +579 -0
  39. package/dist/crdt/document.d.ts +10 -1
  40. package/dist/crdt/document.js +19 -11
  41. package/dist/crdt/edytor-doc.d.ts +294 -272
  42. package/dist/crdt/edytor-doc.js +123 -108
  43. package/dist/crdt/engine-api.d.ts +8 -2
  44. package/dist/crdt/engine.d.ts +2 -1
  45. package/dist/crdt/engine.js +3 -1
  46. package/dist/crdt/flow.d.ts +5 -47
  47. package/dist/crdt/flow.js +3 -269
  48. package/dist/crdt/index.d.ts +102 -108
  49. package/dist/crdt/index.js +3 -0
  50. package/dist/crdt/operations.d.ts +13 -4
  51. package/dist/crdt/parts.d.ts +43 -0
  52. package/dist/crdt/parts.js +44 -0
  53. package/dist/crdt/placement/display.d.ts +1 -1
  54. package/dist/crdt/placement/display.js +3 -3
  55. package/dist/crdt/placement/keys.d.ts +5 -0
  56. package/dist/crdt/placement/keys.js +5 -0
  57. package/dist/crdt/placement/resolve.d.ts +1 -1
  58. package/dist/crdt/placement/resolve.js +2 -1
  59. package/dist/crdt/protocol.d.ts +1 -1
  60. package/dist/crdt/protocol.js +1 -1
  61. package/dist/crdt/protocols/auth.d.ts +11 -1
  62. package/dist/crdt/protocols/auth.js +17 -1
  63. package/dist/crdt/protocols/awareness.js +46 -81
  64. package/dist/crdt/protocols/comments.d.ts +16 -1
  65. package/dist/crdt/protocols/comments.js +9 -1
  66. package/dist/crdt/providers/index.d.ts +12 -22
  67. package/dist/crdt/providers/index.js +3 -1
  68. package/dist/crdt/providers/indexeddb.d.ts +6 -10
  69. package/dist/crdt/providers/indexeddb.js +10 -16
  70. package/dist/crdt/providers/room.d.ts +11 -0
  71. package/dist/crdt/providers/room.js +26 -12
  72. package/dist/crdt/providers/websocket.d.ts +8 -12
  73. package/dist/crdt/providers/websocket.js +17 -19
  74. package/dist/crdt/purge.d.ts +7 -1
  75. package/dist/crdt/purge.js +32 -2
  76. package/dist/crdt/rangeDelete.d.ts +2 -47
  77. package/dist/crdt/rangeDelete.js +1 -169
  78. package/dist/crdt/restore.d.ts +2 -1
  79. package/dist/crdt/restore.js +9 -0
  80. package/dist/crdt/schema.d.ts +9 -1
  81. package/dist/crdt/schema.js +13 -3
  82. package/dist/crdt/structs.d.ts +2 -0
  83. package/dist/crdt/structs.js +2 -0
  84. package/dist/crdt/text/index/build.d.ts +125 -0
  85. package/dist/crdt/text/index/build.js +2895 -0
  86. package/dist/crdt/text/index/shared.d.ts +2 -6
  87. package/dist/crdt/text/index/shared.js +19 -19
  88. package/dist/crdt/text/model.d.ts +10 -0
  89. package/dist/crdt/text/model.js +10 -4
  90. package/dist/crdt/text/runs.d.ts +43 -42
  91. package/dist/crdt/text/runs.js +2 -165
  92. package/dist/crdt/vendor/yjs/UPSTREAM.md +83 -0
  93. package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
  94. package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +6 -3
  95. package/dist/crdt/vendor/yjs/dts/ynode.d.ts +13 -2
  96. package/dist/crdt/vendor/yjs/src/index.js +1 -1
  97. package/dist/crdt/vendor/yjs/src/structs/Item.js +44 -5
  98. package/dist/crdt/vendor/yjs/src/ynode.js +29 -7
  99. package/dist/edytor.svelte.d.ts +1 -1
  100. package/dist/edytor.svelte.js +100 -75
  101. package/dist/events/beforeInputCommands.d.ts +2 -2
  102. package/dist/events/domTextOffset.d.ts +2 -0
  103. package/dist/events/domTextOffset.js +8 -0
  104. package/dist/events/nativeInteractiveControl.d.ts +11 -1
  105. package/dist/events/nativeInteractiveControl.js +41 -1
  106. package/dist/events/onBeforeInput.js +3 -3
  107. package/dist/events/onFocus.js +9 -11
  108. package/dist/export/forms.d.ts +75 -75
  109. package/dist/index.d.ts +4 -2
  110. package/dist/index.js +1 -1
  111. package/dist/labels.d.ts +8 -0
  112. package/dist/labels.js +6 -1
  113. package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
  114. package/dist/plugins/comments/CommentsController.svelte.js +14 -4
  115. package/dist/plugins/find/search.js +2 -2
  116. package/dist/plugins/index.d.ts +1 -0
  117. package/dist/plugins/index.js +1 -0
  118. package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
  119. package/dist/plugins/richtext/richTextOperations.js +26 -32
  120. package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
  121. package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
  122. package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
  123. package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
  124. package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
  125. package/dist/plugins/suggestions/shared.svelte.js +421 -0
  126. package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
  127. package/dist/selection/domSelection.d.ts +2 -0
  128. package/dist/selection/domSelection.js +6 -2
  129. package/dist/selection/pointer.svelte.js +75 -48
  130. package/dist/selection/replaceSelection.js +2 -6
  131. package/dist/selection/selection.svelte.d.ts +3 -3
  132. package/dist/selection/selection.svelte.js +39 -32
  133. package/dist/selection/selection.utils.d.ts +14 -0
  134. package/dist/selection/selection.utils.js +33 -11
  135. package/dist/session/commands.js +21 -11
  136. package/dist/session/composition.svelte.d.ts +5 -3
  137. package/dist/session/composition.svelte.js +21 -17
  138. package/dist/session/handles.d.ts +4 -17
  139. package/dist/session/handles.js +14 -44
  140. package/dist/session/navigation.js +6 -9
  141. package/dist/session/selection.d.ts +11 -5
  142. package/dist/session/selection.js +14 -48
  143. package/dist/session/suggestions.svelte.d.ts +38 -9
  144. package/dist/session/suggestions.svelte.js +37 -18
  145. package/dist/session/virtual.d.ts +1 -1
  146. package/dist/surface/attributes.d.ts +5 -0
  147. package/dist/surface/attributes.js +2 -2
  148. package/dist/surface/cells.d.ts +2 -26
  149. package/dist/surface/cells.js +2 -33
  150. package/dist/surface/observer.svelte.d.ts +6 -1
  151. package/dist/surface/observer.svelte.js +143 -111
  152. package/dist/surface/pin.svelte.d.ts +9 -9
  153. package/dist/surface/pin.svelte.js +15 -22
  154. package/dist/surface/projector.svelte.js +10 -16
  155. package/dist/text/text.svelte.js +14 -19
  156. package/dist/themes/notion.css +29 -0
  157. package/dist/utils.d.ts +5 -0
  158. package/dist/utils.js +7 -0
  159. package/package.json +3 -1
  160. package/dist/crdt/doc/capability.d.ts +0 -56
  161. package/dist/crdt/doc/capability.js +0 -211
  162. package/dist/crdt/doc/content.d.ts +0 -16
  163. package/dist/crdt/doc/content.js +0 -77
  164. package/dist/crdt/doc/delete.d.ts +0 -15
  165. package/dist/crdt/doc/delete.js +0 -84
  166. package/dist/crdt/doc/layout.d.ts +0 -9
  167. package/dist/crdt/doc/layout.js +0 -178
  168. package/dist/crdt/doc/meta.d.ts +0 -19
  169. package/dist/crdt/doc/meta.js +0 -199
  170. package/dist/crdt/doc/moves.d.ts +0 -21
  171. package/dist/crdt/doc/moves.js +0 -274
  172. package/dist/crdt/doc/split-merge.d.ts +0 -10
  173. package/dist/crdt/doc/split-merge.js +0 -179
  174. package/dist/crdt/doc/steps.d.ts +0 -65
  175. package/dist/crdt/doc/steps.js +0 -311
  176. package/dist/crdt/text/index/cache.d.ts +0 -16
  177. package/dist/crdt/text/index/cache.js +0 -175
  178. package/dist/crdt/text/index/claims.d.ts +0 -18
  179. package/dist/crdt/text/index/claims.js +0 -225
  180. package/dist/crdt/text/index/fold.d.ts +0 -21
  181. package/dist/crdt/text/index/fold.js +0 -656
  182. package/dist/crdt/text/index/layout.d.ts +0 -24
  183. package/dist/crdt/text/index/layout.js +0 -271
  184. package/dist/crdt/text/index/placement.d.ts +0 -47
  185. package/dist/crdt/text/index/placement.js +0 -741
  186. package/dist/crdt/text/index/records.d.ts +0 -13
  187. package/dist/crdt/text/index/records.js +0 -139
  188. package/dist/crdt/text/index/report.d.ts +0 -18
  189. package/dist/crdt/text/index/report.js +0 -337
  190. package/dist/crdt/text/index/state.d.ts +0 -197
  191. package/dist/crdt/text/index/state.js +0 -116
  192. package/dist/crdt/text/index/streams.d.ts +0 -25
  193. package/dist/crdt/text/index/streams.js +0 -248
@@ -1,274 +0,0 @@
1
- /**
2
- * Block placement ops: insert (`insertBlocks`, `duplicateBlock`), move
3
- * (`moveBlocks`), outdent (`unNestBlocks`) and the lift out of the
4
- * containers a kind does not fit (`landingOf`, `splitOut`, `liftOut`).
5
- */
6
- import { id as newId } from '../../utils.js';
7
- import { asBlockSpec, sanitizeSpec } from '../../utils/json.js';
8
- import { collides } from '../placement/model.js';
9
- import { ref } from './plan.js';
10
- import { PART } from './steps.js';
11
- /** The moves ops of one facade, prepared. */
12
- export const moveOps = (c) => {
13
- const { doc, blockTypeOf, blockDataOf, childrenIds, positionOf, isVoid, isIsland, isLine, contentItems, live, canPlace, isContainer, fits, kindToCopy, REFUSED, plan, moveTo, move, leaving, settledKind, settle, landing, emptying, emptied } = c;
14
- /**
15
- * Insert blocks (specs may carry children/content/data; the batch is
16
- * all-or-nothing). A spec is a `BlockSpec` or the `JSONBlock` that
17
- * `toJSON` and the view's `value` speak (`{ type, content: [{ text }] }`):
18
- * its ids are kept, minted where missing (`asBlockSpec`). Ids must be fresh.
19
- * Refused when the parent is not live, is `void` or is an island's
20
- * line (it holds no children), or any id collides.
21
- * Inserting INSIDE an island is allowed — island interiors are built
22
- * this way. `ids`: the inserted roots.
23
- */
24
- const insertBlocks = (dest, specs) => {
25
- const parent = ref(dest.parent);
26
- const clean = specs.map((spec) => sanitizeSpec(asBlockSpec(spec)));
27
- if (parent !== null && (isVoid(parent) || isLine(parent)))
28
- return REFUSED;
29
- if (clean.length === 0)
30
- return plan([], []);
31
- if ((parent !== null && !live(parent)) || collides(doc, clean))
32
- return REFUSED;
33
- return plan(clean.map((s) => s.id), [{ op: 'insertBlocks', parent, index: dest.index, specs: clean }]);
34
- };
35
- /**
36
- * Grouped move — one step, per-member conflict resolution; refused
37
- * exactly when `canPlace` refuses the group. `ids`: the moved blocks,
38
- * in request order.
39
- */
40
- const moveBlocks = (ids, dest) => {
41
- const moved = ids.map(ref);
42
- const parent = ref(dest.parent);
43
- if (moved.length === 0)
44
- return plan([], []);
45
- if (!canPlace(moved, parent))
46
- return REFUSED;
47
- // A list the move leaves with no item goes (not one it moves or lands in).
48
- const into = new Set([...moved, ...landing(parent)]);
49
- return plan(moved, emptied(moved, move(moved, parent, dest.index), into));
50
- };
51
- /**
52
- * Outdent: move sibling blocks `ids` (document order) right
53
- * after their parent, and hand the siblings that followed the last of
54
- * them to it as its last children — every outliner's Shift+Tab. A last
55
- * block that cannot adopt them (void, island, a container they do not
56
- * fit) leaves them with the parent. The blocks take the kind they show
57
- * in their new slot (`settledKind`: a list's item becomes a paragraph,
58
- * a paragraph outdented into a list its item); refused where one would
59
- * not fit (a paragraph out of a column into its columns layout).
60
- * Items leaving a container never take the items after them:
61
- * the list splits around them, Notion's way — outdented first items go
62
- * before it, last ones after it; from the middle, the list keeps the
63
- * items after them and a new list of its kind takes the ones before
64
- * them, so an item a peer appends meanwhile stays with the items it
65
- * follows (`splitOut`, the split Turn into's `liftOut` makes). A
66
- * container left with no child goes. One plan; refused as the
67
- * move is. `ids`: the moved blocks.
68
- */
69
- const unNestBlocks = (ids) => {
70
- const moved = ids.map(ref);
71
- const last = moved.at(-1);
72
- const pos = last === undefined ? null : positionOf(last);
73
- const ppos = pos?.parent != null ? positionOf(pos.parent) : null;
74
- if (!ppos || moved.some((id) => positionOf(id)?.parent !== pos.parent))
75
- return REFUSED;
76
- const from = pos.parent;
77
- if (!canPlace(moved, ppos.parent, (id) => settledKind(from, id, ppos.parent)))
78
- return REFUSED;
79
- const after = childrenIds(from).slice(pos.index + 1);
80
- const retype = settle(from, moved, ppos.parent);
81
- if (isContainer(from))
82
- return splitOut(moved, [from], [], false, retype);
83
- const writes = [...moveTo(moved, ppos.parent, ppos.index + 1, leaving(true)), ...retype];
84
- const adopts = after.length > 0 &&
85
- !isVoid(last) &&
86
- !isIsland(last) &&
87
- after.every((id) => fits(last, blockTypeOf(id)));
88
- if (adopts)
89
- writes.push(...move(after, last, Infinity));
90
- return plan(moved, writes);
91
- };
92
- /** Outdent one block (`unNestBlocks`). */
93
- const unNestBlock = (id) => unNestBlocks([id]);
94
- /**
95
- * Where a block of `kind` at `id`'s place lands: `id`'s parent, or the
96
- * first one up that `kind` fits (`fits`: a list holds only its items),
97
- * and the containers it leaves on the way, innermost first (`levels`).
98
- * Its own kind keeps a block where it is, as a reorder
99
- * does (`canPlace`); a block inserted after it (`after`) has no place
100
- * yet.
101
- */
102
- const landingOf = (id, kind, after = false) => {
103
- const levels = [];
104
- let parent = positionOf(id)?.parent ?? null;
105
- const own = !after && blockTypeOf(id) === kind;
106
- for (; !own && parent !== null && !fits(parent, kind); parent = positionOf(parent).parent)
107
- levels.push(parent);
108
- return { parent, levels };
109
- };
110
- /**
111
- * Lift sibling blocks `ids` (document order) out of `levels`, the
112
- * containers around them, innermost first: each level
113
- * splits around them as an outdent splits one list — the blocks
114
- * before them go to a new container of its kind, which the level above
115
- * holds the same way, and the level keeps the ones after them, so an
116
- * item a peer appends meanwhile stays with the items it follows; one
117
- * left with no child goes. `specs` (new blocks) land right after them.
118
- * With `keep`, `ids` stay before the split and only `specs` go out.
119
- * `retype` (the caller's kind steps) joins the plan. The one split
120
- * `unNestBlocks` and `liftOut` share. `ids`: the placed blocks.
121
- */
122
- const splitOut = (ids, levels, specs, keep, retype = []) => {
123
- const moved = keep ? [] : [...ids];
124
- const placed = [...moved, ...specs.map((s) => s.id)];
125
- const insert = (at, index, s) => s.length ? [{ op: 'insertBlocks', parent: at, index, specs: s }] : [];
126
- const last = ids.at(-1);
127
- const { parent, index } = positionOf(levels.at(-1) ?? last);
128
- if (levels.length === 0)
129
- return plan(placed, [...insert(parent, index + 1, specs), ...retype]);
130
- // Bottom up: what stays before the split at each level (the level
131
- // itself, or a new head holding it), and whether anything follows it.
132
- const heads = [];
133
- let before = null;
134
- let follows = false;
135
- // The block right after `last` (what the split ends at when `keep`).
136
- let next;
137
- let child = last;
138
- for (const level of levels) {
139
- const kids = childrenIds(level);
140
- const at = kids.indexOf(child);
141
- const stays = (keep && child === last) || (before !== null && !before.head);
142
- const lead = kids.slice(0, at + (stays ? 1 : 0)).filter((k) => !moved.includes(k));
143
- if (!follows && at + 1 < kids.length)
144
- [follows, next] = [true, kids[at + 1]];
145
- if ((lead.length > 0 || before?.head) && follows) {
146
- const spec = sanitizeSpec({
147
- id: newId('b'),
148
- type: kindToCopy(level),
149
- data: blockDataOf(level) ?? {}
150
- });
151
- heads.push({ spec, kids: lead, inner: before?.head });
152
- before = { id: spec.id, head: spec };
153
- }
154
- else
155
- before = lead.length > 0 ? { id: level } : null;
156
- child = level;
157
- }
158
- // Written in one gap, each right after the block before it, as an
159
- // after-record out of the level's slot or a before-record into the gap
160
- // before it (`leaving`): the engine's sort decides their order against
161
- // a peer's records there (fork patch YP22, `sourceOrder`: the block a
162
- // record stands at, then its part), so a peer's heads, pieces, lifts
163
- // and new blocks in the same gap keep the serial order whatever the
164
- // client ids (`del.merge.container`). The head stands at the first block
165
- // (with `keep`, at it: `with`), the blocks at themselves, the new
166
- // blocks after the last (`after`) — or, kept before the block that
167
- // follows, at that block (`kept`). The records are written last first,
168
- // so each stands before the one written before it, in the order the
169
- // sort gives them; the head and the new blocks are created in the
170
- // innermost level right after the block they stand at (their home,
171
- // hidden while their records show them) and moved out by them.
172
- const outside = before !== null && !before.head;
173
- const side = leaving(outside);
174
- const gap = index + (outside ? 1 : 0);
175
- const at = moved[0] ?? last;
176
- const kids0 = childrenIds(levels[0]);
177
- const home = kids0.indexOf(last) + 1;
178
- const specIds = specs.map((sp) => sp.id);
179
- const [stands, part] = keep && !outside ? [next, PART.kept] : [last, PART.after];
180
- // One record per run of adjacent blocks, each standing at its first: a
181
- // block a peer's move takes out of a run leaves the others at their own place.
182
- const runs = [];
183
- for (const id of moved) {
184
- const run = runs.at(-1);
185
- if (run !== undefined && kids0.indexOf(run.at(-1)) + 1 === kids0.indexOf(id))
186
- run.push(id);
187
- else
188
- runs.push([id]);
189
- }
190
- const writes = [
191
- ...insert(levels[0], home, specs),
192
- ...(before?.head ? insert(levels[0], home, [before.head]) : []),
193
- ...moveTo(specIds, parent, gap, side, stands, part),
194
- ...runs.reverse().flatMap((run) => moveTo(run, parent, gap, side, run[0], PART.self)),
195
- ...(before?.head
196
- ? moveTo([before.head.id], parent, gap, side, at, keep ? PART.with : PART.head)
197
- : [])
198
- ];
199
- for (const { spec, kids, inner } of heads.reverse()) {
200
- writes.push(...moveTo(kids, spec.id, 0), ...(inner ? insert(spec.id, kids.length, [inner]) : []));
201
- }
202
- writes.push(...retype);
203
- void next;
204
- return plan(placed, emptying(levels[0], moved, writes, landing(parent)));
205
- };
206
- /**
207
- * Place `id` where a block of `kind` fits, in one plan:
208
- * out of every container around it that `kind` does not fit
209
- * (`landingOf`: a list, and a list holding that list directly, for a
210
- * heading or a divider), each split around it (`splitOut`). `after`
211
- * (new blocks) lands right after it. With `keep`, `id` stays and only
212
- * `after` goes out, the split right after `id` (a divider inserted
213
- * after an item). Its own kind never moves it. The kinds are the
214
- * caller's: it composes the retype (`setBlock`). Refused where `id`
215
- * may not move there (`canPlace`). `ids`: the placed blocks.
216
- */
217
- const liftOut = (id, kind, { keep = false, after = [] } = {}) => {
218
- id = ref(id);
219
- kind = ref(kind);
220
- const specs = after.map(sanitizeSpec);
221
- const { parent, levels } = landingOf(id, kind, keep);
222
- if (!positionOf(id) || collides(doc, specs) || !canPlace([id], parent, () => kind))
223
- return REFUSED;
224
- return splitOut([id], levels, specs, keep);
225
- };
226
- /**
227
- * Explicit fresh-identity copy of a subtree (paste / drag-clone):
228
- * serializes `id`, remaps every block and inline atom id through
229
- * `freshId`, inserts the copy right after `id`. Text atoms get new
230
- * identity too — duplication is a creation op, not a relocation. An
231
- * inline id `freshId` does not answer (a block-only callback) is
232
- * minted; a block id it does not answer refuses the copy. `ids`: the
233
- * copy's root.
234
- */
235
- const duplicateBlock = (id, freshId = (_, kind) => newId(kind === 'block' ? 'b' : 'i')) => {
236
- id = ref(id);
237
- const pos = positionOf(id);
238
- if (pos === null)
239
- return REFUSED;
240
- const spec = (b) => {
241
- const fresh = freshId(b, 'block');
242
- if (typeof fresh !== 'string')
243
- return null;
244
- const content = contentItems(b).map((item) => {
245
- if (item.kind !== 'inline')
246
- return item;
247
- const atom = freshId(item.id, 'inline');
248
- return { ...item, id: typeof atom === 'string' ? atom : newId('i') };
249
- });
250
- const children = childrenIds(b).map(spec);
251
- if (children.includes(null))
252
- return null;
253
- const data = blockDataOf(b);
254
- return {
255
- id: fresh,
256
- type: kindToCopy(b),
257
- ...(data !== undefined && { data }),
258
- content,
259
- children: children
260
- };
261
- };
262
- const copy = spec(id);
263
- return copy ? insertBlocks({ parent: pos.parent, index: pos.index + 1 }, [copy]) : REFUSED;
264
- };
265
- return {
266
- insertBlocks,
267
- moveBlocks,
268
- unNestBlocks,
269
- unNestBlock,
270
- landingOf,
271
- liftOut,
272
- duplicateBlock
273
- };
274
- };
@@ -1,10 +0,0 @@
1
- import { type BlockId, type SplitTail } from '../placement/model.js';
2
- import type { Prepared } from './types.js';
3
- import type { OpsContext } from './steps.js';
4
- /** The split-merge ops of one facade, prepared. */
5
- export declare const splitMergeOps: (c: OpsContext) => {
6
- splitBlock: (id: BlockId, offset: number, newId: BlockId, tail?: SplitTail) => Prepared;
7
- mergeBlocks: (fromId: BlockId, intoId: BlockId) => Prepared;
8
- mergeBackward: (id: BlockId) => Prepared;
9
- mergeForward: (id: BlockId) => Prepared;
10
- };
@@ -1,179 +0,0 @@
1
- /**
2
- * Split and merge ops: `splitBlock` (one boundary item, no text copied),
3
- * the engine merge primitive `mergeBlocks`, and the baseline-shaped
4
- * `mergeBackward`/`mergeForward` (a first item lifts out of its container,
5
- * a layout item's first block merges across items).
6
- */
7
- import { isIncarnationId } from '../incarnations.js';
8
- import { sanitizeWireJson } from '../../utils/json.js';
9
- import { readData } from '../data.js';
10
- import { blockNodeOf, isSelfOrDescendant } from '../placement/model.js';
11
- import { ref } from './plan.js';
12
- /** The split-merge ops of one facade, prepared. */
13
- export const splitMergeOps = (c) => {
14
- const { doc, view, childrenIds, positionOf, isVoid, isCellKind, blockTypeOf, insideIsland, isLayoutItem, next, previous, displayLength, contentTarget, isContainer, fits, canMerge, rendersContent, kindToCopy, REFUSED, plan, moveTo, move, leaving, settledKind, settle, landing, emptying, emptied, merge, clamp } = c;
15
- /**
16
- * Split `id` at content `offset` into a new sibling `newId` (one boundary
17
- * item and the claims that follow it, no text copied; children follow).
18
- * `tail` decides the sibling's type/data once (default: the source's —
19
- * the kind it displays; an empty `tail.type` is the default too).
20
- * Refused on `void` blocks and on blocks that render no content (a
21
- * list, a code block: nothing to split). `ids`: the new
22
- * block.
23
- */
24
- const splitBlock = (id, offset, newId, tail) => {
25
- id = ref(id);
26
- const born = ref(newId);
27
- const pos = positionOf(id);
28
- const rec = view().blocks.get(id);
29
- if (pos === null || isVoid(id) || !rendersContent(id) || rec === undefined)
30
- return REFUSED;
31
- // A table's cell holds its lines as line breaks: it never splits (`table.split`).
32
- if (isCellKind(blockTypeOf(id)))
33
- return REFUSED;
34
- if (isIncarnationId(born) || blockNodeOf(doc, born) !== null)
35
- return REFUSED;
36
- const [at] = clamp(id, offset, 0);
37
- // An empty tail type is the one a view reads mid-retype: copy the kind.
38
- const type = tail?.type ? ref(tail.type) : kindToCopy(id);
39
- const t = tail
40
- ? { type, data: tail.data && sanitizeWireJson(tail.data) }
41
- : { type, data: readData(rec.node) };
42
- const length = displayLength(id) - at;
43
- const split = {
44
- op: 'splitBlock',
45
- id,
46
- offset: at,
47
- length,
48
- newId: born,
49
- tail: t,
50
- parent: pos.parent,
51
- index: pos.index + 1
52
- };
53
- return plan([born], [split, ...move(childrenIds(id), born, 0)]);
54
- };
55
- /**
56
- * Engine merge primitive: `from`'s content is claimed by `into`, its
57
- * children ADOPTED as `into`'s last children — an island's take
58
- * `into`'s default child, like a baseline merge's — and `from` is
59
- * hidden via the claim (undo restores it). The children stay under
60
- * `from` and display under `into` through the claim, after `into`'s
61
- * own children (the display's claim order): a concurrent delete of
62
- * `into` voids the claim and `from` comes back with its children,
63
- * never below them. Role rules are `canMerge`'s; a merge that would
64
- * close a display cycle is refused. `ids`: `into`.
65
- */
66
- const mergeBlocks = (fromId, intoId) => {
67
- const from = ref(fromId);
68
- const into = ref(intoId);
69
- const v = view();
70
- if (!canMerge(from, into) || !contentTarget(into))
71
- return REFUSED;
72
- if (isSelfOrDescendant(v.placements, v.own, into, from))
73
- return REFUSED;
74
- const kids = childrenIds(from);
75
- const retype = settle(from, kids, into);
76
- // A list its only item leaves goes, as with every sibling op.
77
- return plan([into], emptied([from], [merge(from, into), ...retype], landing(into)));
78
- };
79
- /**
80
- * Baseline-shaped merge (both `mergeBackward` and `mergeForward`):
81
- * `from`'s children unnest right after `from`'s vacated sibling slot —
82
- * NOT adopted into `into` — and take the kind they show there
83
- * (`settledKind`: an island's children its parent's default child, a
84
- * paragraph in a list its item); then `from`'s content claims
85
- * into `into`. Placed after `from`, not at it: a concurrent delete of
86
- * `into` revives `from` above its former children.
87
- */
88
- const mergeUnnesting = (from, into) => {
89
- const pos = canMerge(from, into) ? positionOf(from) : null;
90
- if (pos === null || !contentTarget(into))
91
- return REFUSED;
92
- const kids = childrenIds(from);
93
- const retype = settle(from, kids, pos.parent);
94
- return plan([into], [...moveTo(kids, pos.parent, pos.index + 1, leaving(true)), ...retype, merge(from, into)]);
95
- };
96
- /** Remove `container`, left empty, for the key at `id` (refused if it holds text). */
97
- const gone = (id, container) => {
98
- const writes = emptying(container, [], []);
99
- return writes.length > 0 ? plan([id], writes) : REFUSED;
100
- };
101
- /**
102
- * Baseline `mergeBlockBackward`: merge `id` into the previous block in
103
- * document order. No previous block → an empty block merges forward,
104
- * else refused. The first item of a container (`isContainer`) outside
105
- * any island lifts out of it instead (Notion): it takes the
106
- * container's slot, as its new parent's default child, with its
107
- * children (a first cell stays: nothing leaves an island; a block that
108
- * would land directly in a container it is no item of stays too).
109
- * The first block of a layout item merges across items
110
- * instead, in reading order (`layout.merge`, Notion): into the
111
- * previous item's last line, or, in the first item, the line before
112
- * the layout; an item it empties goes and the layout dissolves.
113
- * `ids`: the surviving block.
114
- */
115
- const mergeBackward = (id) => {
116
- id = ref(id);
117
- const prev = previous(id);
118
- if (prev === null && childrenIds(id).length === 0 && displayLength(id) === 0) {
119
- return mergeForward(id);
120
- }
121
- // A container a concurrent edit left empty shows nothing: the key removes it.
122
- if (prev !== null && isContainer(prev) && childrenIds(prev).length === 0)
123
- return gone(id, prev);
124
- if (prev !== null && prev === positionOf(id)?.parent && isLayoutItem(prev)) {
125
- const layout = positionOf(prev).parent;
126
- let into = previous(prev);
127
- if (into === layout)
128
- into = previous(layout);
129
- const merged = into === null ? REFUSED : mergeUnnesting(id, into);
130
- if (!('writes' in merged) || childrenIds(id).length > 0)
131
- return merged;
132
- return plan(merged.ids, emptying(prev, [id], merged.writes));
133
- }
134
- if (prev !== null &&
135
- prev === positionOf(id)?.parent &&
136
- isContainer(prev) &&
137
- !insideIsland(prev)) {
138
- // It lands only where it fits: never directly in a container it is no item of.
139
- const slot = positionOf(prev);
140
- if (!fits(slot.parent, settledKind(prev, id, slot.parent)))
141
- return REFUSED;
142
- const lift = [
143
- ...moveTo([id], slot.parent, slot.index, leaving(false)),
144
- ...settle(prev, [id], slot.parent)
145
- ];
146
- return plan([id], emptying(prev, [id], lift, landing(slot.parent)));
147
- }
148
- return prev ? mergeUnnesting(id, prev) : REFUSED;
149
- };
150
- /**
151
- * Baseline `mergeBlockForward`: pull the next block in document order
152
- * into `id`. A container passes the merge to its first item (`del.merge.container`:
153
- * Delete above a list pulls the item's text up and the list keeps the
154
- * rest, or goes when that was its only item). A container left
155
- * empty (by concurrent edits) is removed instead, by either key.
156
- */
157
- const mergeForward = (id) => {
158
- id = ref(id);
159
- let after = next(id);
160
- while (after !== null && isContainer(after)) {
161
- const first = childrenIds(after)[0];
162
- // A container a concurrent edit left empty shows nothing: the key removes it.
163
- if (first === undefined)
164
- return gone(id, after);
165
- after = first;
166
- }
167
- const out = after ? mergeUnnesting(after, id) : REFUSED;
168
- const parent = after && positionOf(after)?.parent;
169
- if (!('writes' in out) || !parent || childrenIds(after).length > 0)
170
- return out;
171
- return plan(out.ids, emptying(parent, [after], out.writes));
172
- };
173
- return {
174
- splitBlock,
175
- mergeBlocks,
176
- mergeBackward,
177
- mergeForward
178
- };
179
- };
@@ -1,65 +0,0 @@
1
- /**
2
- * The step writers every prepared op composes: the plan itself, moves (a
3
- * move's place and side), type steps and the kind a block shows where it
4
- * lands (`settle`), the containers and layouts a write leaves empty
5
- * (`emptying`, `dissolving`), delete and merge steps, and display spans of a
6
- * block's content.
7
- */
8
- import { TYPE } from '../schema.js';
9
- import { type BlockId, type ContentItem } from '../placement/model.js';
10
- import type { Plan, PlanStep } from './types.js';
11
- import type { DocBase, DocReads } from './reads.js';
12
- import type { DocCapability } from './capability.js';
13
- /** What the step writers read: the reads and the structural capability. */
14
- /**
15
- * A record's order among the records standing at one block, for the
16
- * engine's sort of two records at one origin (fork patch YP22): what a split
17
- * of a list around a block writes there — a new head list holding the items
18
- * before it (`head`), new blocks placed after the items before it, which
19
- * stay (`kept`), a head holding those items and the block (`with`), the
20
- * block itself leaving (`self`), new blocks after it (`after`) — in the
21
- * order every serial run of the gestures gives (`del.merge.container`).
22
- */
23
- export declare const PART: {
24
- readonly head: 0;
25
- readonly kept: 1;
26
- readonly with: 2;
27
- readonly self: 3;
28
- readonly after: 4;
29
- };
30
- export type StepsContext = DocBase & DocReads & DocCapability;
31
- /** The step writers of one facade. */
32
- export declare const planSteps: (c: StepsContext) => {
33
- REFUSED: import("./types.js").OpResult;
34
- plan: (ids: readonly BlockId[], writes: PlanStep[]) => Plan;
35
- moveTo: (ids: BlockId[], parent: BlockId | null, index: number, side?: 0 | 1, at?: BlockId, part?: number) => PlanStep[];
36
- move: (ids: BlockId[], parent: BlockId | null, index: number) => PlanStep[];
37
- leaving: (after: boolean) => 0 | 1;
38
- attr: (id: BlockId, key: typeof TYPE, value: string) => PlanStep[];
39
- leavingIsland: (island: BlockId, kind: string | undefined, to: string) => string | undefined;
40
- settledKind: (from: BlockId | null, kid: BlockId, parent: BlockId | null) => string | undefined;
41
- settle: (from: BlockId | null, kids: readonly BlockId[], parent: BlockId | null) => PlanStep[];
42
- landing: (parent: BlockId | null) => Set<BlockId | null>;
43
- emptyingAll: (from: readonly (BlockId | null)[], leaving: readonly BlockId[], writes: readonly PlanStep[], kept?: ReadonlySet<BlockId | null>, removed?: readonly BlockId[]) => PlanStep[];
44
- dissolving: (gone: ReadonlySet<BlockId>, leaving: readonly BlockId[], writes: readonly PlanStep[]) => PlanStep[];
45
- emptying: (container: BlockId, leaving: readonly BlockId[], writes: readonly PlanStep[], kept?: ReadonlySet<BlockId | null>) => PlanStep[];
46
- emptied: (leaving: readonly BlockId[], writes: PlanStep[], kept?: ReadonlySet<BlockId | null>) => PlanStep[];
47
- deleting: (id: BlockId, removes: BlockId[]) => PlanStep;
48
- remove: (id: BlockId, kept?: readonly BlockId[]) => PlanStep;
49
- merge: (from: BlockId, into: BlockId) => PlanStep;
50
- clamp: (id: BlockId, offset: number, length: number) => [number, number];
51
- atomOf: (id: BlockId, atom: string) => {
52
- item: ContentItem;
53
- at: number;
54
- end: number;
55
- } | undefined;
56
- textIn: (id: BlockId, at: number, end: number) => {
57
- kind: "text";
58
- text: string;
59
- marks?: Record<string, unknown>;
60
- }[];
61
- writeContent: (writes: PlanStep[], id: BlockId, at: number, items?: readonly ContentItem[]) => number;
62
- };
63
- export type PlanSteps = ReturnType<typeof planSteps>;
64
- /** What every op family is prepared over: the reads, the capability and the step writers. */
65
- export type OpsContext = StepsContext & PlanSteps;