edytor 5.0.0 → 7.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.
- package/README.md +3 -3
- package/dist/cloudflare/DocumentRoom.d.ts +77 -2
- package/dist/cloudflare/DocumentRoom.js +37 -1
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/index.js +1 -1
- package/dist/cloudflare/room/access.d.ts +6 -1
- package/dist/cloudflare/room/access.js +30 -4
- package/dist/cloudflare/room/admission.d.ts +11 -0
- package/dist/cloudflare/room/admission.js +43 -10
- package/dist/cloudflare/room/context.d.ts +73 -76
- package/dist/cloudflare/room/context.js +17 -6
- package/dist/cloudflare/room/forged.d.ts +1 -1
- package/dist/cloudflare/room/forged.js +11 -9
- package/dist/cloudflare/room/frames.d.ts +2 -0
- package/dist/cloudflare/room/frames.js +2 -0
- package/dist/cloudflare/room/presence.js +1 -4
- package/dist/cloudflare/room/reseed.d.ts +93 -0
- package/dist/cloudflare/room/reseed.js +500 -0
- package/dist/cloudflare/room/scheduler.d.ts +1 -1
- package/dist/cloudflare/room/scheduler.js +9 -1
- package/dist/cloudflare/room/shared.d.ts +10 -0
- package/dist/cloudflare/room/shared.js +10 -0
- package/dist/cloudflare/room/storage.d.ts +7 -0
- package/dist/cloudflare/room/storage.js +33 -1
- package/dist/components/Edytor.svelte +18 -3
- package/dist/components/Edytor.svelte.d.ts +10 -1
- package/dist/components/EdytorRoot.svelte +17 -0
- package/dist/components/EdytorRoot.svelte.d.ts +4 -0
- package/dist/components/Suggestion.svelte +1 -1
- package/dist/crdt/attribution/block.d.ts +3 -0
- package/dist/crdt/attribution/block.js +3 -2
- package/dist/crdt/doc/history.js +2 -3
- package/dist/crdt/doc/ops.d.ts +81 -0
- package/dist/crdt/doc/ops.js +1898 -0
- package/dist/crdt/doc/suggest.d.ts +6 -2
- package/dist/crdt/doc/suggest.js +6 -5
- package/dist/crdt/edytor-doc.d.ts +157 -157
- package/dist/crdt/edytor-doc.js +15 -43
- package/dist/crdt/engine-room.d.ts +4 -0
- package/dist/crdt/engine-room.js +6 -0
- package/dist/crdt/flow.d.ts +4 -46
- package/dist/crdt/flow.js +2 -262
- package/dist/crdt/index.d.ts +64 -75
- package/dist/crdt/operations.d.ts +1 -1
- package/dist/crdt/placement/resolve.d.ts +1 -1
- package/dist/crdt/protocol.d.ts +2 -2
- package/dist/crdt/protocol.js +2 -2
- package/dist/crdt/protocols/auth.d.ts +8 -1
- package/dist/crdt/protocols/auth.js +14 -1
- package/dist/crdt/protocols/awareness.js +46 -81
- package/dist/crdt/providers/index.d.ts +11 -22
- package/dist/crdt/providers/index.js +22 -3
- package/dist/crdt/providers/indexeddb.d.ts +6 -10
- package/dist/crdt/providers/indexeddb.js +10 -16
- package/dist/crdt/providers/room.d.ts +26 -0
- package/dist/crdt/providers/room.js +41 -12
- package/dist/crdt/providers/websocket.d.ts +10 -14
- package/dist/crdt/providers/websocket.js +43 -23
- package/dist/crdt/rangeDelete.d.ts +2 -47
- package/dist/crdt/rangeDelete.js +1 -169
- package/dist/crdt/reseed.d.ts +127 -0
- package/dist/crdt/reseed.js +533 -0
- package/dist/crdt/restore.d.ts +13 -1
- package/dist/crdt/restore.js +8 -3
- package/dist/crdt/schema.d.ts +6 -0
- package/dist/crdt/schema.js +6 -0
- package/dist/crdt/structs.d.ts +8 -11
- package/dist/crdt/structs.js +25 -44
- package/dist/crdt/text/index/build.d.ts +119 -0
- package/dist/crdt/text/index/build.js +2801 -0
- package/dist/crdt/text/index/shared.d.ts +2 -6
- package/dist/crdt/text/index/shared.js +2 -5
- package/dist/crdt/text/model.js +2 -3
- package/dist/crdt/text/runs.d.ts +9 -12
- package/dist/crdt/text/runs.js +2 -165
- package/dist/crdt/vendor/yjs/UPSTREAM.md +101 -5
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +10 -10
- package/dist/crdt/vendor/yjs/dts/utils/encoding.d.ts +1 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +24 -13
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +12 -10
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +42 -1
- package/dist/events/domTextOffset.d.ts +2 -0
- package/dist/events/domTextOffset.js +8 -0
- package/dist/export/forms.d.ts +50 -50
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
- package/dist/plugins/richtext/richTextOperations.js +26 -32
- package/dist/selection/selection.svelte.d.ts +1 -1
- package/dist/selection/selection.svelte.js +87 -106
- package/dist/selection/selection.utils.js +5 -8
- package/dist/session/commands.js +49 -51
- package/dist/session/handles.js +3 -7
- package/dist/surface/attributes.d.ts +3 -3
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/observer.svelte.d.ts +7 -7
- package/dist/surface/observer.svelte.js +225 -336
- package/dist/surface/projector.svelte.js +2 -2
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +1 -1
- package/dist/crdt/doc/capability.d.ts +0 -56
- package/dist/crdt/doc/capability.js +0 -207
- package/dist/crdt/doc/content.d.ts +0 -16
- package/dist/crdt/doc/content.js +0 -77
- package/dist/crdt/doc/delete.d.ts +0 -15
- package/dist/crdt/doc/delete.js +0 -84
- package/dist/crdt/doc/layout.d.ts +0 -9
- package/dist/crdt/doc/layout.js +0 -178
- package/dist/crdt/doc/meta.d.ts +0 -19
- package/dist/crdt/doc/meta.js +0 -198
- package/dist/crdt/doc/moves.d.ts +0 -21
- package/dist/crdt/doc/moves.js +0 -274
- package/dist/crdt/doc/split-merge.d.ts +0 -10
- package/dist/crdt/doc/split-merge.js +0 -179
- package/dist/crdt/doc/steps.d.ts +0 -65
- package/dist/crdt/doc/steps.js +0 -311
- package/dist/crdt/text/index/cache.d.ts +0 -16
- package/dist/crdt/text/index/cache.js +0 -175
- package/dist/crdt/text/index/claims.d.ts +0 -18
- package/dist/crdt/text/index/claims.js +0 -203
- package/dist/crdt/text/index/fold.d.ts +0 -21
- package/dist/crdt/text/index/fold.js +0 -656
- package/dist/crdt/text/index/layout.d.ts +0 -22
- package/dist/crdt/text/index/layout.js +0 -258
- package/dist/crdt/text/index/placement.d.ts +0 -47
- package/dist/crdt/text/index/placement.js +0 -738
- package/dist/crdt/text/index/records.d.ts +0 -13
- package/dist/crdt/text/index/records.js +0 -139
- package/dist/crdt/text/index/report.d.ts +0 -18
- package/dist/crdt/text/index/report.js +0 -337
- package/dist/crdt/text/index/state.d.ts +0 -197
- package/dist/crdt/text/index/state.js +0 -116
- package/dist/crdt/text/index/streams.d.ts +0 -25
- package/dist/crdt/text/index/streams.js +0 -248
package/dist/crdt/doc/layout.js
DELETED
|
@@ -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.of(kind).layout === undefined ? undefined : kind;
|
|
16
|
-
const kinds = roles.declared().layouts;
|
|
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.of(wrap).layout;
|
|
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.of(wrap).layout;
|
|
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.of(item).child;
|
|
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
|
-
};
|
package/dist/crdt/doc/meta.d.ts
DELETED
|
@@ -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>;
|
package/dist/crdt/doc/meta.js
DELETED
|
@@ -1,198 +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, isLines, 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 to = roles.of(type);
|
|
84
|
-
if (isLines(id) && !to.island && !to.childless) {
|
|
85
|
-
return [
|
|
86
|
-
...steps,
|
|
87
|
-
...moved.flatMap((k) => {
|
|
88
|
-
const kind = leavingIsland(id, blockTypeOf(k.id), to.child);
|
|
89
|
-
return kind === undefined ? [] : attr(k.id, TYPE, kind);
|
|
90
|
-
})
|
|
91
|
-
];
|
|
92
|
-
}
|
|
93
|
-
if (!to.childless || moved.length === 0 || pos === null)
|
|
94
|
-
return steps;
|
|
95
|
-
// The children keep their home slots: the read shows them in the void's
|
|
96
|
-
// slot (`conc.void-children`), settled for its parent.
|
|
97
|
-
return [
|
|
98
|
-
...steps,
|
|
99
|
-
...settle(id, moved.map((k) => k.id), pos.parent)
|
|
100
|
-
];
|
|
101
|
-
};
|
|
102
|
-
/** Set the block type (attr write — the block keeps its identity; see `retypeSteps`). */
|
|
103
|
-
const setBlockType = (id, type) => {
|
|
104
|
-
id = ref(id);
|
|
105
|
-
return live(id) ? plan([id], retypeSteps(id, ref(type))) : REFUSED;
|
|
106
|
-
};
|
|
107
|
-
/**
|
|
108
|
-
* Patch the data of a block, one of its inline atoms or the document
|
|
109
|
-
* (`null`), in order (`crdt/data.ts`): each patch replaces the value at
|
|
110
|
-
* its path (`value` absent deletes it), `splice` and `order` edit the
|
|
111
|
-
* array there; a path addresses array items by index, resolved here
|
|
112
|
-
* to the items' ids. Only the leaves it changes are written, so a
|
|
113
|
-
* peer's edit of another key or item is kept. Refused for an absent
|
|
114
|
-
* target, a path that is no array of strings, a root value that is no
|
|
115
|
-
* object, or an op that fits no value at its path.
|
|
116
|
-
*/
|
|
117
|
-
const patchData = (target, patches) => {
|
|
118
|
-
const valid = (p) => Array.isArray(p?.path) &&
|
|
119
|
-
p.path.every((k) => typeof k === 'string') &&
|
|
120
|
-
(p.path.length > 0 || p.value === undefined || isObject(p.value));
|
|
121
|
-
if (!Array.isArray(patches) || !patches.every(valid))
|
|
122
|
-
return REFUSED;
|
|
123
|
-
// Only the fields a patch has: an absent one is no non-JSON value to report.
|
|
124
|
-
const clean = patches.map(({ path, value, splice, order }) => sanitizeWireJson({
|
|
125
|
-
path,
|
|
126
|
-
...(value !== undefined && { value }),
|
|
127
|
-
...(splice !== undefined && { splice }),
|
|
128
|
-
...(order !== undefined && { order })
|
|
129
|
-
}));
|
|
130
|
-
const t = target === null
|
|
131
|
-
? null
|
|
132
|
-
: typeof target === 'string'
|
|
133
|
-
? ref(target)
|
|
134
|
-
: { block: ref(target.block), atom: ref(target.atom) };
|
|
135
|
-
if (typeof t === 'string' ? !live(t) : t !== null && atomOf(t.block, t.atom) === undefined)
|
|
136
|
-
return REFUSED;
|
|
137
|
-
const steps = dataSteps(t, clean);
|
|
138
|
-
return steps ? plan(t === null ? [] : [typeof t === 'string' ? t : t.block], steps) : REFUSED;
|
|
139
|
-
};
|
|
140
|
-
/**
|
|
141
|
-
* Baseline `setBlock`: `type` updates the block in place and `data`
|
|
142
|
-
* sets the leaves it names, removing none (`data.retype.keep`: a
|
|
143
|
-
* retype keeps the block's properties, as Notion's Turn into does;
|
|
144
|
-
* `setBlockData` replaces them); `content`/`children` REPLACE
|
|
145
|
-
* wholesale — explicit replacement is a
|
|
146
|
-
* new-identity operation; a retype to a void kind without `children`
|
|
147
|
-
* unnests the current ones (`retypeSteps`). All-or-nothing: children
|
|
148
|
-
* for a block that is `void` after the write, or a replacement id that
|
|
149
|
-
* is already taken (a live or deleted
|
|
150
|
-
* block, the replaced children included, or a duplicate inside the
|
|
151
|
-
* replacement), refuses before any write — `id-collision` for the
|
|
152
|
-
* latter. A caller wanting a child back re-creates it with a fresh id.
|
|
153
|
-
*/
|
|
154
|
-
const setBlock = (id, value) => {
|
|
155
|
-
id = ref(id);
|
|
156
|
-
const content = value.content?.map((item) => sanitizeItem(asContentItem(item)));
|
|
157
|
-
const children = value.children?.map((child) => sanitizeSpec(asBlockSpec(child)));
|
|
158
|
-
if (!live(id) || (content !== undefined && !contentTarget(id)))
|
|
159
|
-
return REFUSED;
|
|
160
|
-
const type = value.type === undefined ? undefined : ref(value.type);
|
|
161
|
-
const toVoid = type === undefined ? isVoid(id) : roles.of(type).childless;
|
|
162
|
-
if (children?.length && toVoid)
|
|
163
|
-
return REFUSED;
|
|
164
|
-
if (children !== undefined && collides(doc, children))
|
|
165
|
-
return refused('id-collision');
|
|
166
|
-
const data = value.data === undefined ? [] : dataSteps(id, leafSets(value.data, blockDataOf(id)));
|
|
167
|
-
if (data === null)
|
|
168
|
-
return REFUSED;
|
|
169
|
-
const writes = [
|
|
170
|
-
...(type === undefined
|
|
171
|
-
? []
|
|
172
|
-
: children === undefined
|
|
173
|
-
? retypeSteps(id, type)
|
|
174
|
-
: attr(id, TYPE, type)),
|
|
175
|
-
...data
|
|
176
|
-
];
|
|
177
|
-
if (content !== undefined) {
|
|
178
|
-
const length = displayLength(id);
|
|
179
|
-
if (length > 0)
|
|
180
|
-
writes.push({ op: 'deleteText', id, offset: 0, length });
|
|
181
|
-
writeContent(writes, id, 0, content);
|
|
182
|
-
}
|
|
183
|
-
if (children !== undefined) {
|
|
184
|
-
for (const kid of childrenIds(id))
|
|
185
|
-
writes.push(remove(kid));
|
|
186
|
-
if (children.length > 0)
|
|
187
|
-
writes.push({ op: 'insertBlocks', parent: id, index: 0, specs: children });
|
|
188
|
-
}
|
|
189
|
-
return plan([id], writes);
|
|
190
|
-
};
|
|
191
|
-
return {
|
|
192
|
-
dataSteps,
|
|
193
|
-
replaceData,
|
|
194
|
-
setBlockType,
|
|
195
|
-
patchData,
|
|
196
|
-
setBlock
|
|
197
|
-
};
|
|
198
|
-
};
|
package/dist/crdt/doc/moves.d.ts
DELETED
|
@@ -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>;
|