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,84 +0,0 @@
1
- import { ref } from './plan.js';
2
- /** The delete ops of one facade, prepared. */
3
- export const deleteOps = (c) => {
4
- const { view, blockTypeOf, childrenIds, positionOf, ancestorsOf, itemKindOf, isLayout, isLayoutItem, isTable, isTableRow, live, REFUSED, plan, settle, emptyingAll, emptied, deleting, remove } = c;
5
- /**
6
- * Delete a set of blocks (`del.blocks.promote`): this writer's mark on
7
- * every member and on what it displays through merge claims (wins over
8
- * concurrent moves). Only the members leave: the unselected children of a
9
- * deleted block take its slot, in order, with their subtrees, as the
10
- * kind they show there (`settledKind`: a deleted island's children take
11
- * the slot parent's default child, a deleted list's items leave its
12
- * kind, a block promoted into a list is its item). A container the delete leaves with no child goes too
13
- * (`del.range.empty-container`). `subtree` is the explicit whole-subtree
14
- * delete. `filled`: a parent the same plan fills again (a flow placed in
15
- * the deleted blocks' slot, `flow.slot`), which is never emptied, so its
16
- * layout never dissolves (`layout.flow-slot`).
17
- */
18
- const deleteBlocks = (ids, subtree = false, filled) => {
19
- const set = new Set(ids.map(ref));
20
- if ([...set].some((id) => !live(id)))
21
- return REFUSED;
22
- // A layout goes with its items: their blocks take its slot (`layout.dissolving`).
23
- for (const id of [...set])
24
- if (isLayout(id))
25
- for (const kid of childrenIds(id))
26
- if (isLayoutItem(kid))
27
- set.add(kid);
28
- // A table goes with its rows and cells, a row with its cells: none shows outside them (`table.delete-row`).
29
- const whole = (id) => {
30
- for (const kid of childrenIds(id)) {
31
- set.add(kid);
32
- whole(kid);
33
- }
34
- };
35
- for (const id of [...set])
36
- if (isTable(id) || isTableRow(id))
37
- whole(id);
38
- if (subtree) {
39
- const roots = [...set].filter((id) => !ancestorsOf(id).some((a) => set.has(a)));
40
- return plan(roots, emptied(roots, roots.map((id) => remove(id))));
41
- }
42
- const roots = [...set].filter((id) => !set.has(positionOf(id).parent));
43
- const { kids } = view();
44
- const promoting = new Set();
45
- const members = [];
46
- const writes = roots.flatMap((root) => {
47
- const pos = positionOf(root);
48
- const chunk = [];
49
- const kept = [];
50
- // The kept children keep their home slots: the read shows them in the
51
- // root's slot, in its order (a block a peer puts under a member
52
- // meanwhile takes the slot in that order too); only their kinds are
53
- // written, as the slot's parent settles them.
54
- const walk = (b) => {
55
- chunk.push(b);
56
- for (const kid of kids.get(b) ?? []) {
57
- if (set.has(kid.id))
58
- walk(kid.id);
59
- else
60
- kept.push({ id: kid.id, from: b });
61
- }
62
- };
63
- walk(root);
64
- members.push(...chunk);
65
- if (kept.length > 0)
66
- promoting.add(root);
67
- // A layout shows only its items: a deleted item's blocks show right
68
- // after the layout (`layout.only-items`), settled for its parent.
69
- const item = pos.parent === null ? undefined : itemKindOf(pos.parent);
70
- const out = (k) => item !== undefined && blockTypeOf(k.id) !== item;
71
- return [
72
- ...kept.flatMap((k) => settle(k.from, [k.id], out(k) ? positionOf(pos.parent).parent : pos.parent)),
73
- deleting(root, chunk)
74
- ];
75
- });
76
- return plan(roots, emptyingAll(roots.filter((r) => !promoting.has(r)).map((id) => positionOf(id).parent), roots.filter((r) => !promoting.has(r)), writes, filled === undefined ? undefined : new Set([filled]), members));
77
- };
78
- /** Delete `id` — its children take its slot (`keepChildren: false`: the whole subtree). */
79
- const deleteBlock = (id, opts = {}) => deleteBlocks([id], opts.keepChildren === false);
80
- return {
81
- deleteBlocks,
82
- deleteBlock
83
- };
84
- };
@@ -1,9 +0,0 @@
1
- import type { BlockId } from '../placement/model.js';
2
- import type { Prepared } from './types.js';
3
- import type { OpsContext } from './steps.js';
4
- /** The layout ops of one facade, prepared. */
5
- export declare const layoutOps: (c: OpsContext) => {
6
- besideAt: (target: BlockId, kind?: string) => BlockId;
7
- placeBeside: (ids: readonly BlockId[], target: BlockId, side: "left" | "right", kind?: string) => Prepared;
8
- wrapInLayout: (ids: readonly BlockId[], kind?: string, columns?: number) => Prepared;
9
- };
@@ -1,178 +0,0 @@
1
- /**
2
- * Layout ops (`layout.*`): the layout kind a new layout takes, where a
3
- * beside placement lands (`besideAt`), and the two layout-making ops,
4
- * `placeBeside` and `wrapInLayout`.
5
- */
6
- import { id as newId } from '../../utils.js';
7
- import { sanitizeSpec } from '../../utils/json.js';
8
- import { ref } from './plan.js';
9
- /** The layout ops of one facade, prepared. */
10
- export const layoutOps = (c) => {
11
- const { roles, view, blockTypeOf, childrenIds, positionOf, ancestorsOf, isIsland, isLines, insideIsland, isLayout, isLayoutItem, holdsLayout, insideItem, live, canPlace, isContainer, fits, fitsIn, REFUSED, plan, moveTo, landing, emptied } = c;
12
- /** The layout kind a new layout takes: `kind` when it is one, else the only one the roles declare. */
13
- const layoutKind = (kind) => {
14
- if (kind !== undefined)
15
- return roles.layout(kind) === undefined ? undefined : kind;
16
- const kinds = [...roles.layoutKinds()];
17
- return kinds.length === 1 ? kinds[0] : undefined;
18
- };
19
- /** A fresh empty block of `type`. */
20
- const spec = (type) => sanitizeSpec({ id: newId('b'), type, data: {} });
21
- /**
22
- * The block a beside placement at `target` stands beside
23
- * (`layout.place-beside`): a layout or an item for itself; a block whose
24
- * parent shows only its children (a list item → its list, at any depth)
25
- * or is an island of lines (a code line → its code block) for that
26
- * parent; then the block itself when it sits directly in an item or at
27
- * the root, or where a new layout of `kind` (else the only layout kind)
28
- * fits beside it (a toggle's, a callout's or a nested block's child,
29
- * outside any item and island); else its outermost block below the
30
- * root or an item.
31
- */
32
- const besideAt = (target, kind) => {
33
- let at = ref(target);
34
- if (isLayout(at) || isLayoutItem(at))
35
- return at;
36
- for (let parent = positionOf(at)?.parent ?? null; parent !== null && !isLayoutItem(parent) && (isContainer(parent) || isLines(parent)); parent = positionOf(parent)?.parent ?? null)
37
- at = parent;
38
- const parent = positionOf(at)?.parent ?? null;
39
- if (parent === null || isLayoutItem(parent))
40
- return at;
41
- const wrap = layoutKind(kind);
42
- if (wrap !== undefined &&
43
- fits(parent, wrap) &&
44
- !insideItem(parent) &&
45
- !isIsland(parent) &&
46
- !insideIsland(parent))
47
- return at;
48
- for (let up = parent; up !== null && !isLayoutItem(up); up = positionOf(up).parent)
49
- at = up;
50
- return at;
51
- };
52
- /**
53
- * Place `ids` beside `target`, to its `side` (`layout.place-beside`: a
54
- * block dragged to another block's left or right edge). The target
55
- * resolves to its outermost block below the root or below a layout
56
- * item (a list item → its list, a code line → its code block); an item
57
- * stands for itself, a layout for its first or last slot. Beside a
58
- * block in an item, a new item holding `ids` goes beside that item;
59
- * otherwise the block and a new item holding `ids` are wrapped in a new
60
- * layout of the layout `kind` (else the only one the roles declare) at
61
- * its place. Refused when `ids` holds the target or an ancestor of it,
62
- * an item, a layout or a block holding one, a block that does not
63
- * fit an item, a block or a layout inside an island, and, wrapping,
64
- * when there is no layout kind. The sources are cleaned in the same
65
- * plan (`emptying`, `dissolving`); a plain move. `ids`: the moved
66
- * blocks.
67
- */
68
- const placeBeside = (ids, target, side, kind) => {
69
- const moved = ids.map(ref);
70
- target = ref(target);
71
- const v = view();
72
- if ((side !== 'left' && side !== 'right') || !canPlace(moved) || !live(target))
73
- return REFUSED;
74
- if ([target, ...ancestorsOf(target, v)].some((a) => moved.includes(a)))
75
- return REFUSED;
76
- if (moved.some((id) => isLayoutItem(id) || holdsLayout(id)))
77
- return REFUSED;
78
- const right = side === 'right';
79
- // Where the new item goes: beside an item of a layout, or a new layout wrapping `at`.
80
- const at = besideAt(target, kind && ref(kind));
81
- let layout = null;
82
- let index = 0;
83
- if (isLayout(at))
84
- [layout, index] = [at, right ? childrenIds(at).length : 0];
85
- else {
86
- const item = isLayoutItem(at) ? at : positionOf(at).parent;
87
- if (item !== null) {
88
- const pos = positionOf(item);
89
- [layout, index] = [pos.parent, pos.index + (right ? 1 : 0)];
90
- }
91
- }
92
- const wrap = layout === null ? layoutKind(kind && ref(kind)) : blockTypeOf(layout);
93
- const item = wrap === undefined ? undefined : roles.layout(wrap);
94
- if (wrap === undefined || item === undefined)
95
- return REFUSED;
96
- if (moved.some((id) => !fitsIn(item, blockTypeOf(id))))
97
- return REFUSED;
98
- if (layout !== null && (isIsland(layout) || insideIsland(layout, v)))
99
- return REFUSED;
100
- const fresh = spec(item);
101
- const into = (parent) => moveTo(moved, parent, 0);
102
- let writes;
103
- let kept;
104
- if (layout !== null) {
105
- writes = [{ op: 'insertBlocks', parent: layout, index, specs: [fresh] }, ...into(fresh.id)];
106
- kept = new Set([fresh.id, ...landing(layout)]);
107
- }
108
- else {
109
- const pos = positionOf(at);
110
- const host = spec(item);
111
- const wrapper = { ...spec(wrap), children: right ? [host, fresh] : [fresh, host] };
112
- writes = [
113
- { op: 'insertBlocks', parent: pos.parent, index: pos.index, specs: [wrapper] },
114
- ...moveTo([at], host.id, 0),
115
- ...into(fresh.id)
116
- ];
117
- kept = new Set([wrapper.id, host.id, fresh.id, ...landing(pos.parent)]);
118
- }
119
- return plan(moved, emptied(moved, writes, kept));
120
- };
121
- /**
122
- * Wrap sibling blocks in a new layout of `columns` items (default: one
123
- * per block), at the first one's place (`layout.wrap`: Turn into N
124
- * columns, as Notion): the blocks fill the first items, one each, in
125
- * document order; each item after them holds one empty block of the
126
- * item's default child (one block turned into 3 columns: it, then two
127
- * empty columns). The layout is of the layout `kind` (else the only one
128
- * the roles declare). Refused for no block, fewer than two items or
129
- * fewer items than blocks, blocks of different parents, an item, a
130
- * layout or a block holding one, where the layout does not fit
131
- * (`fits`: in a list, a code block) or lands inside an item or an
132
- * island, and for a block that does not fit an item. A plain move.
133
- * `ids`: the new layout.
134
- */
135
- const wrapInLayout = (ids, kind, columns = ids.length) => {
136
- const blocks = ids.map(ref);
137
- const v = view();
138
- if (!Number.isInteger(columns) || columns < 2 || blocks.length < 1)
139
- return REFUSED;
140
- if (columns < blocks.length || !canPlace(blocks))
141
- return REFUSED;
142
- const parent = positionOf(blocks[0]).parent;
143
- if (blocks.some((id) => positionOf(id).parent !== parent))
144
- return REFUSED;
145
- if (blocks.some((id) => isLayoutItem(id) || holdsLayout(id)))
146
- return REFUSED;
147
- const wrap = layoutKind(kind && ref(kind));
148
- const item = wrap === undefined ? undefined : roles.layout(wrap);
149
- if (wrap === undefined || item === undefined || !fits(parent, wrap))
150
- return REFUSED;
151
- if (parent !== null && (insideItem(parent, v) || isIsland(parent) || insideIsland(parent, v)))
152
- return REFUSED;
153
- if (blocks.some((id) => !fitsIn(item, blockTypeOf(id))))
154
- return REFUSED;
155
- const fill = roles.defaultChild(item);
156
- if (columns > blocks.length && !fitsIn(item, fill))
157
- return REFUSED;
158
- const ordered = blocks.toSorted((a, b) => positionOf(a).index - positionOf(b).index);
159
- const items = Array.from({ length: columns }, (_, i) => i < ordered.length ? spec(item) : { ...spec(item), children: [spec(fill)] });
160
- const wrapper = { ...spec(wrap), children: items };
161
- const at = positionOf(ordered[0]).index;
162
- const writes = [
163
- { op: 'insertBlocks', parent, index: at, specs: [wrapper] },
164
- ...ordered.flatMap((id, i) => moveTo([id], items[i].id, 0))
165
- ];
166
- const kept = new Set([
167
- wrapper.id,
168
- ...items.map((it) => it.id),
169
- ...landing(parent)
170
- ]);
171
- return plan([wrapper.id], emptied(ordered, writes, kept));
172
- };
173
- return {
174
- besideAt,
175
- placeBeside,
176
- wrapInLayout
177
- };
178
- };
@@ -1,19 +0,0 @@
1
- import { type JSONBlock, type JSONInlineBlock, type JSONText } from '../../utils/json.js';
2
- import { type DataPatch } from '../data.js';
3
- import { type BlockId, type BlockSpec, type ContentItem } from '../placement/model.js';
4
- import type { Prepared, PlanStep, DataTarget } from './types.js';
5
- import type { OpsContext } from './steps.js';
6
- /** The meta ops of one facade, prepared. */
7
- export declare const metaOps: (c: OpsContext) => {
8
- dataSteps: (target: DataTarget, patches: DataPatch[]) => PlanStep[] | null;
9
- replaceData: (data: unknown) => DataPatch[];
10
- setBlockType: (id: BlockId, type: string) => Prepared;
11
- patchData: (target: DataTarget, patches: readonly DataPatch[]) => Prepared;
12
- setBlock: (id: BlockId, value: {
13
- type?: string;
14
- data?: Record<string, unknown>;
15
- content?: (ContentItem | JSONText | JSONInlineBlock)[];
16
- children?: (BlockSpec | JSONBlock)[];
17
- }) => Prepared;
18
- };
19
- export type MetaOps = ReturnType<typeof metaOps>;
@@ -1,199 +0,0 @@
1
- /**
2
- * A block's meta: its type (`setBlockType`, the steps a retype implies) and
3
- * its data, a block's, an atom's or the document's (`patchData`, the data
4
- * steps of a patch), and the baseline `setBlock`.
5
- */
6
- import { randOf } from '../rand.js';
7
- import { TYPE } from '../schema.js';
8
- import { asBlockSpec, asContentItem, sanitizeSpec, sanitizeWireJson } from '../../utils/json.js';
9
- import { patchWrites, leafKey, isObject } from '../data.js';
10
- import { collides } from '../placement/model.js';
11
- import { ref, refused, sanitizeItem } from './plan.js';
12
- /** The meta ops of one facade, prepared. */
13
- export const metaOps = (c) => {
14
- const { doc, roles, roleOf, dataNode, view, blockTypeOf, blockDataOf, childrenIds, positionOf, is, isVoid, displayLength, live, contentTarget, REFUSED, plan, attr, leavingIsland, settle, remove, atomOf, writeContent } = c;
15
- /**
16
- * The data step of `patches` (sanitized) on a block, one of its atoms or
17
- * the document (`crdt/data.ts`): none when it changes nothing, `null`
18
- * when a patch is refused.
19
- */
20
- const dataSteps = (target, patches) => {
21
- const [id, inlineId] = typeof target === 'object' && target ? [target.block, target.atom] : [target ?? undefined];
22
- const node = dataNode(id, inlineId);
23
- // A block kind's atomic paths are written as one leaf each (`data.atomic`).
24
- const type = inlineId === undefined && id !== undefined ? blockTypeOf(id) : undefined;
25
- const atomic = (type === undefined ? undefined : roleOf(type)?.atomic) ?? [];
26
- const writes = node
27
- ? patchWrites(node, patches, doc.clientID, randOf(doc), atomic.map((p) => leafKey(typeof p === 'string' ? [p] : p)))
28
- : [];
29
- if (writes === null)
30
- return null; // a patch fits no value there
31
- if (writes.length === 0)
32
- return [];
33
- const offset = inlineId === undefined ? undefined : atomOf(id, inlineId)?.at;
34
- return [
35
- {
36
- op: 'patchData',
37
- ...(id !== undefined && { id }),
38
- ...(inlineId !== undefined && { inlineId, offset }),
39
- ops: patches,
40
- writes
41
- }
42
- ];
43
- };
44
- /** A whole-data replace: a patch of the root. */
45
- const replaceData = (data) => [{ path: [], value: sanitizeWireJson(data) }];
46
- /**
47
- * `data`'s leaves as sets over `current` (`data.retype.keep`): a
48
- * plain object's keys, recursively where `current` holds an object
49
- * there too; anything else (an array, a primitive, an empty object, an
50
- * object where `current` holds none) as one value at its path. Nothing
51
- * else under the root is touched.
52
- */
53
- const leafSets = (data, current) => {
54
- const out = [];
55
- const walk = (v, at, path) => {
56
- if (isObject(v) && Object.keys(v).length > 0 && (path.length === 0 || isObject(at)))
57
- for (const [k, x] of Object.entries(v))
58
- walk(x, isObject(at) ? at[k] : undefined, [...path, k]);
59
- else if (path.length > 0)
60
- out.push({ path, value: v });
61
- };
62
- walk(sanitizeWireJson(data), current, []);
63
- return out;
64
- };
65
- /**
66
- * The target role decides: nothing renders a void's children,
67
- * so a block retyped to a void kind hands them to the slot right after
68
- * it, as the kind they show there (`settledKind`), in their order (where
69
- * the read-time shedding shows a child a peer adds meanwhile). An island
70
- * declared `lines` retyped to an ordinary kind keeps its lines, each
71
- * retyped to the new kind's default child (the document's where that
72
- * renders no content): no line kind outside its island. Any other
73
- * island's children keep their kinds (a table's rows stay rows), as
74
- * `typeOf` shows a child a peer adds meanwhile.
75
- */
76
- const retypeSteps = (id, type) => {
77
- const { kids } = view();
78
- const pos = positionOf(id);
79
- const steps = attr(id, TYPE, type);
80
- const moved = kids.get(id) ?? [];
81
- // A code block retyped to an ordinary kind keeps its lines as that kind's children
82
- // (`leavingIsland`: never a kind that hides their text).
83
- const lined = is(id, (t) => roles.line(t) !== undefined);
84
- if (lined && !roles.island(type) && !roles.childless(type)) {
85
- const to = roles.defaultChild(type);
86
- return [
87
- ...steps,
88
- ...moved.flatMap((k) => {
89
- const kind = leavingIsland(id, blockTypeOf(k.id), to);
90
- return kind === undefined ? [] : attr(k.id, TYPE, kind);
91
- })
92
- ];
93
- }
94
- if (!roles.childless(type) || moved.length === 0 || pos === null)
95
- return steps;
96
- // The children keep their home slots: the read shows them in the void's
97
- // slot (`conc.void-children`), settled for its parent.
98
- return [
99
- ...steps,
100
- ...settle(id, moved.map((k) => k.id), pos.parent)
101
- ];
102
- };
103
- /** Set the block type (attr write — the block keeps its identity; see `retypeSteps`). */
104
- const setBlockType = (id, type) => {
105
- id = ref(id);
106
- return live(id) ? plan([id], retypeSteps(id, ref(type))) : REFUSED;
107
- };
108
- /**
109
- * Patch the data of a block, one of its inline atoms or the document
110
- * (`null`), in order (`crdt/data.ts`): each patch replaces the value at
111
- * its path (`value` absent deletes it), `splice` and `order` edit the
112
- * array there; a path addresses array items by index, resolved here
113
- * to the items' ids. Only the leaves it changes are written, so a
114
- * peer's edit of another key or item is kept. Refused for an absent
115
- * target, a path that is no array of strings, a root value that is no
116
- * object, or an op that fits no value at its path.
117
- */
118
- const patchData = (target, patches) => {
119
- const valid = (p) => Array.isArray(p?.path) &&
120
- p.path.every((k) => typeof k === 'string') &&
121
- (p.path.length > 0 || p.value === undefined || isObject(p.value));
122
- if (!Array.isArray(patches) || !patches.every(valid))
123
- return REFUSED;
124
- // Only the fields a patch has: an absent one is no non-JSON value to report.
125
- const clean = patches.map(({ path, value, splice, order }) => sanitizeWireJson({
126
- path,
127
- ...(value !== undefined && { value }),
128
- ...(splice !== undefined && { splice }),
129
- ...(order !== undefined && { order })
130
- }));
131
- const t = target === null
132
- ? null
133
- : typeof target === 'string'
134
- ? ref(target)
135
- : { block: ref(target.block), atom: ref(target.atom) };
136
- if (typeof t === 'string' ? !live(t) : t !== null && atomOf(t.block, t.atom) === undefined)
137
- return REFUSED;
138
- const steps = dataSteps(t, clean);
139
- return steps ? plan(t === null ? [] : [typeof t === 'string' ? t : t.block], steps) : REFUSED;
140
- };
141
- /**
142
- * Baseline `setBlock`: `type` updates the block in place and `data`
143
- * sets the leaves it names, removing none (`data.retype.keep`: a
144
- * retype keeps the block's properties, as Notion's Turn into does;
145
- * `setBlockData` replaces them); `content`/`children` REPLACE
146
- * wholesale — explicit replacement is a
147
- * new-identity operation; a retype to a void kind without `children`
148
- * unnests the current ones (`retypeSteps`). All-or-nothing: children
149
- * for a block that is `void` after the write, or a replacement id that
150
- * is already taken (a live or deleted
151
- * block, the replaced children included, or a duplicate inside the
152
- * replacement), refuses before any write — `id-collision` for the
153
- * latter. A caller wanting a child back re-creates it with a fresh id.
154
- */
155
- const setBlock = (id, value) => {
156
- id = ref(id);
157
- const content = value.content?.map((item) => sanitizeItem(asContentItem(item)));
158
- const children = value.children?.map((child) => sanitizeSpec(asBlockSpec(child)));
159
- if (!live(id) || (content !== undefined && !contentTarget(id)))
160
- return REFUSED;
161
- const type = value.type === undefined ? undefined : ref(value.type);
162
- const toVoid = type === undefined ? isVoid(id) : roles.childless(type);
163
- if (children?.length && toVoid)
164
- return REFUSED;
165
- if (children !== undefined && collides(doc, children))
166
- return refused('id-collision');
167
- const data = value.data === undefined ? [] : dataSteps(id, leafSets(value.data, blockDataOf(id)));
168
- if (data === null)
169
- return REFUSED;
170
- const writes = [
171
- ...(type === undefined
172
- ? []
173
- : children === undefined
174
- ? retypeSteps(id, type)
175
- : attr(id, TYPE, type)),
176
- ...data
177
- ];
178
- if (content !== undefined) {
179
- const length = displayLength(id);
180
- if (length > 0)
181
- writes.push({ op: 'deleteText', id, offset: 0, length });
182
- writeContent(writes, id, 0, content);
183
- }
184
- if (children !== undefined) {
185
- for (const kid of childrenIds(id))
186
- writes.push(remove(kid));
187
- if (children.length > 0)
188
- writes.push({ op: 'insertBlocks', parent: id, index: 0, specs: children });
189
- }
190
- return plan([id], writes);
191
- };
192
- return {
193
- dataSteps,
194
- replaceData,
195
- setBlockType,
196
- patchData,
197
- setBlock
198
- };
199
- };
@@ -1,21 +0,0 @@
1
- import { type JSONBlock } from '../../utils/json.js';
2
- import { type BlockId, type BlockSpec, type Destination } from '../placement/model.js';
3
- import type { Prepared } from './types.js';
4
- import { type OpsContext } from './steps.js';
5
- /** The moves ops of one facade, prepared. */
6
- export declare const moveOps: (c: OpsContext) => {
7
- insertBlocks: (dest: Destination, specs: readonly (BlockSpec | JSONBlock)[]) => Prepared;
8
- moveBlocks: (ids: readonly BlockId[], dest: Destination) => Prepared;
9
- unNestBlocks: (ids: readonly BlockId[]) => Prepared;
10
- unNestBlock: (id: BlockId) => Prepared;
11
- landingOf: (id: BlockId, kind: string, after?: boolean) => {
12
- parent: string | null;
13
- levels: string[];
14
- };
15
- liftOut: (id: BlockId, kind: string, { keep, after }?: {
16
- keep?: boolean;
17
- after?: readonly BlockSpec[];
18
- }) => Prepared;
19
- duplicateBlock: (id: BlockId, freshId?: (oldId: string, kind: "block" | "inline") => string) => Prepared;
20
- };
21
- export type MoveOps = ReturnType<typeof moveOps>;