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.
- package/README.md +2 -2
- package/dist/block/block.svelte.js +6 -21
- package/dist/block/inlineBlock.svelte.js +4 -6
- package/dist/clipboard/moveText.js +6 -8
- package/dist/cloudflare/DocumentRoom.d.ts +10 -3
- package/dist/cloudflare/index.d.ts +1 -1
- package/dist/cloudflare/room/access.d.ts +8 -3
- package/dist/cloudflare/room/access.js +44 -20
- package/dist/cloudflare/room/admission.js +28 -5
- package/dist/cloudflare/room/comments.js +28 -1
- package/dist/cloudflare/room/context.d.ts +106 -109
- package/dist/cloudflare/room/context.js +10 -3
- package/dist/cloudflare/room/forged.d.ts +14 -0
- package/dist/cloudflare/room/forged.js +30 -2
- package/dist/cloudflare/room/frames.d.ts +4 -0
- package/dist/cloudflare/room/frames.js +5 -1
- package/dist/cloudflare/room/purge.js +2 -1
- package/dist/cloudflare/room/replicas.d.ts +4 -2
- package/dist/cloudflare/room/replicas.js +8 -3
- package/dist/cloudflare/room/suggest.d.ts +7 -0
- package/dist/cloudflare/room/suggest.js +143 -0
- package/dist/cloudflare/routeDocumentSocket.d.ts +10 -1
- package/dist/cloudflare/routeDocumentSocket.js +5 -3
- package/dist/components/Content.svelte +9 -1
- package/dist/crdt/anchors.js +3 -2
- package/dist/crdt/attribution/attribution.d.ts +3 -0
- package/dist/crdt/attribution/attribution.js +3 -3
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +5 -2
- package/dist/crdt/doc/gate.d.ts +1 -1
- package/dist/crdt/doc/history.d.ts +6 -0
- package/dist/crdt/doc/history.js +79 -59
- package/dist/crdt/doc/ops.d.ts +81 -0
- package/dist/crdt/doc/ops.js +1898 -0
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +18 -28
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/document.d.ts +10 -1
- package/dist/crdt/document.js +19 -11
- package/dist/crdt/edytor-doc.d.ts +294 -272
- package/dist/crdt/edytor-doc.js +123 -108
- package/dist/crdt/engine-api.d.ts +8 -2
- package/dist/crdt/engine.d.ts +2 -1
- package/dist/crdt/engine.js +3 -1
- package/dist/crdt/flow.d.ts +5 -47
- package/dist/crdt/flow.js +3 -269
- package/dist/crdt/index.d.ts +102 -108
- package/dist/crdt/index.js +3 -0
- package/dist/crdt/operations.d.ts +13 -4
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +1 -1
- package/dist/crdt/placement/display.js +3 -3
- package/dist/crdt/placement/keys.d.ts +5 -0
- package/dist/crdt/placement/keys.js +5 -0
- package/dist/crdt/placement/resolve.d.ts +1 -1
- package/dist/crdt/placement/resolve.js +2 -1
- package/dist/crdt/protocol.d.ts +1 -1
- package/dist/crdt/protocol.js +1 -1
- package/dist/crdt/protocols/auth.d.ts +11 -1
- package/dist/crdt/protocols/auth.js +17 -1
- package/dist/crdt/protocols/awareness.js +46 -81
- package/dist/crdt/protocols/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +12 -22
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/indexeddb.d.ts +6 -10
- package/dist/crdt/providers/indexeddb.js +10 -16
- package/dist/crdt/providers/room.d.ts +11 -0
- package/dist/crdt/providers/room.js +26 -12
- package/dist/crdt/providers/websocket.d.ts +8 -12
- package/dist/crdt/providers/websocket.js +17 -19
- package/dist/crdt/purge.d.ts +7 -1
- package/dist/crdt/purge.js +32 -2
- package/dist/crdt/rangeDelete.d.ts +2 -47
- package/dist/crdt/rangeDelete.js +1 -169
- package/dist/crdt/restore.d.ts +2 -1
- package/dist/crdt/restore.js +9 -0
- package/dist/crdt/schema.d.ts +9 -1
- package/dist/crdt/schema.js +13 -3
- package/dist/crdt/structs.d.ts +2 -0
- package/dist/crdt/structs.js +2 -0
- package/dist/crdt/text/index/build.d.ts +125 -0
- package/dist/crdt/text/index/build.js +2895 -0
- package/dist/crdt/text/index/shared.d.ts +2 -6
- package/dist/crdt/text/index/shared.js +19 -19
- package/dist/crdt/text/model.d.ts +10 -0
- package/dist/crdt/text/model.js +10 -4
- package/dist/crdt/text/runs.d.ts +43 -42
- package/dist/crdt/text/runs.js +2 -165
- package/dist/crdt/vendor/yjs/UPSTREAM.md +83 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +13 -2
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/Item.js +44 -5
- package/dist/crdt/vendor/yjs/src/ynode.js +29 -7
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +100 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/domTextOffset.d.ts +2 -0
- package/dist/events/domTextOffset.js +8 -0
- package/dist/events/nativeInteractiveControl.d.ts +11 -1
- package/dist/events/nativeInteractiveControl.js +41 -1
- package/dist/events/onBeforeInput.js +3 -3
- package/dist/events/onFocus.js +9 -11
- package/dist/export/forms.d.ts +75 -75
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/dist/labels.d.ts +8 -0
- package/dist/labels.js +6 -1
- package/dist/plugins/comments/CommentsController.svelte.d.ts +5 -1
- package/dist/plugins/comments/CommentsController.svelte.js +14 -4
- package/dist/plugins/find/search.js +2 -2
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/plugins/richtext/RichTextPlugin.svelte +63 -61
- package/dist/plugins/richtext/richTextOperations.js +26 -32
- package/dist/plugins/suggestions/SuggestionBars.svelte +40 -23
- package/dist/plugins/suggestions/SuggestionBars.svelte.d.ts +3 -1
- package/dist/plugins/suggestions/SuggestionRuns.svelte +77 -0
- package/dist/plugins/suggestions/SuggestionRuns.svelte.d.ts +9 -0
- package/dist/plugins/suggestions/shared.svelte.d.ts +58 -0
- package/dist/plugins/suggestions/shared.svelte.js +421 -0
- package/dist/plugins/suggestions/suggestionsPlugin.d.ts +15 -4
- package/dist/selection/domSelection.d.ts +2 -0
- package/dist/selection/domSelection.js +6 -2
- package/dist/selection/pointer.svelte.js +75 -48
- package/dist/selection/replaceSelection.js +2 -6
- package/dist/selection/selection.svelte.d.ts +3 -3
- package/dist/selection/selection.svelte.js +39 -32
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +33 -11
- package/dist/session/commands.js +21 -11
- package/dist/session/composition.svelte.d.ts +5 -3
- package/dist/session/composition.svelte.js +21 -17
- package/dist/session/handles.d.ts +4 -17
- package/dist/session/handles.js +14 -44
- package/dist/session/navigation.js +6 -9
- package/dist/session/selection.d.ts +11 -5
- package/dist/session/selection.js +14 -48
- package/dist/session/suggestions.svelte.d.ts +38 -9
- package/dist/session/suggestions.svelte.js +37 -18
- package/dist/session/virtual.d.ts +1 -1
- package/dist/surface/attributes.d.ts +5 -0
- package/dist/surface/attributes.js +2 -2
- package/dist/surface/cells.d.ts +2 -26
- package/dist/surface/cells.js +2 -33
- package/dist/surface/observer.svelte.d.ts +6 -1
- package/dist/surface/observer.svelte.js +143 -111
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.js +10 -16
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/package.json +3 -1
- package/dist/crdt/doc/capability.d.ts +0 -56
- package/dist/crdt/doc/capability.js +0 -211
- 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 -199
- 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 -225
- 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 -24
- package/dist/crdt/text/index/layout.js +0 -271
- package/dist/crdt/text/index/placement.d.ts +0 -47
- package/dist/crdt/text/index/placement.js +0 -741
- 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/delete.js
DELETED
|
@@ -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
|
-
};
|
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.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
|
-
};
|
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,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
|
-
};
|
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>;
|