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/steps.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The step writers every prepared op composes: the plan itself, moves (a
|
|
3
|
-
* move's place and side), type steps and the kind a block shows where it
|
|
4
|
-
* lands (`settle`), the containers and layouts a write leaves empty
|
|
5
|
-
* (`emptying`, `dissolving`), delete and merge steps, and display spans of a
|
|
6
|
-
* block's content.
|
|
7
|
-
*/
|
|
8
|
-
import { TYPE } from '../schema.js';
|
|
9
|
-
import { blockNodeOf } from '../placement/model.js';
|
|
10
|
-
import { REFUSED, effectOf } from './plan.js';
|
|
11
|
-
/** What the step writers read: the reads and the structural capability. */
|
|
12
|
-
/**
|
|
13
|
-
* A record's order among the records standing at one block, for the
|
|
14
|
-
* engine's sort of two records at one origin (fork patch YP22): what a split
|
|
15
|
-
* of a list around a block writes there — a new head list holding the items
|
|
16
|
-
* before it (`head`), new blocks placed after the items before it, which
|
|
17
|
-
* stay (`kept`), a head holding those items and the block (`with`), the
|
|
18
|
-
* block itself leaving (`self`), new blocks after it (`after`) — in the
|
|
19
|
-
* order every serial run of the gestures gives (`del.merge.container`).
|
|
20
|
-
*/
|
|
21
|
-
export const PART = { head: 0, kept: 1, with: 2, self: 3, after: 4 };
|
|
22
|
-
/** The step writers of one facade. */
|
|
23
|
-
export const planSteps = (c) => {
|
|
24
|
-
const { doc, runsView, roles, rendersContentOf, view, blockTypeOf, childrenIds, positionOf, ancestorsOf, is, isIsland, itemKindOf, isLayout, displayLength, contentItems, live, isContainer, fitted, emptiable, defaultChild } = c;
|
|
25
|
-
// ── prepared ops ─────────────────────────────────────────────
|
|
26
|
-
// Every op is `prepare` (pure: `refused`, or a plan of named steps plus
|
|
27
|
-
// its effect summary, against the current version) then `apply(plan)`;
|
|
28
|
-
// composites compose their steps into one plan, so a hook sees the
|
|
29
|
-
// whole command before any write and a refusal refuses before one.
|
|
30
|
-
// Each prepare normalizes its inputs once, here at ingress: ids
|
|
31
|
-
// and strings as the wire would deliver them, payloads cloned.
|
|
32
|
-
const plan = (ids, writes) => ({
|
|
33
|
-
ids,
|
|
34
|
-
writes,
|
|
35
|
-
effect: effectOf(writes),
|
|
36
|
-
version: runsView.version()
|
|
37
|
-
});
|
|
38
|
-
/**
|
|
39
|
-
* Move `ids` to display index `index` among `parent`'s children (the
|
|
40
|
-
* slot hooks see). `side`: the move record's class among the items of
|
|
41
|
-
* its origin (`placement/slots.ts`) — `0`, the blocks stand after what
|
|
42
|
-
* precedes them (a move; blocks leaving a block for the gap after it:
|
|
43
|
-
* its children, a list's last items); `1`, before what follows them
|
|
44
|
-
* (blocks leaving a block for the gap before it: a list's first item
|
|
45
|
-
* lifted out). In one gap every serial order of the gestures puts a
|
|
46
|
-
* split's pieces first, then the after-records, then the before-records
|
|
47
|
-
* (`del.merge.container`), which the engine's sort gives whatever the
|
|
48
|
-
* client ids.
|
|
49
|
-
* Every planned move carries the type steps that keep what the moved
|
|
50
|
-
* blocks show: one displayed out of an island as another kind than its
|
|
51
|
-
* stored one (`displayType`) gets that kind written, so leaving the
|
|
52
|
-
* island's slot never brings the island's child kind back.
|
|
53
|
-
* `at`, `part`: the block the record stands at and its order among the
|
|
54
|
-
* records standing there, for the engine's sort of two records at one
|
|
55
|
-
* origin (`PART`; by default the first moved block, as itself).
|
|
56
|
-
*/
|
|
57
|
-
const moveTo = (ids, parent, index, side = 0, at, part) => ids.length === 0
|
|
58
|
-
? []
|
|
59
|
-
: [
|
|
60
|
-
{
|
|
61
|
-
op: 'moveBlocks',
|
|
62
|
-
ids,
|
|
63
|
-
parent,
|
|
64
|
-
index,
|
|
65
|
-
...(side === 1 && { side }),
|
|
66
|
-
...(at !== undefined && { at }),
|
|
67
|
-
...(part !== undefined && { part })
|
|
68
|
-
},
|
|
69
|
-
...ids.flatMap((id) => {
|
|
70
|
-
const shown = runsView.displayType(id);
|
|
71
|
-
return shown === undefined ? [] : attr(id, TYPE, shown);
|
|
72
|
-
})
|
|
73
|
-
];
|
|
74
|
-
/** Move `ids` to `index` among `parent`'s children. */
|
|
75
|
-
const move = (ids, parent, index) => moveTo(ids, parent, index);
|
|
76
|
-
/** The side of blocks leaving a block for the gap right after it (`after`) or right before it. */
|
|
77
|
-
const leaving = (after) => (after ? 0 : 1);
|
|
78
|
-
/**
|
|
79
|
-
* A type step, planned only when the kind differs (the one same-value
|
|
80
|
-
* guard). A kind counts as the same only when the block is stored and
|
|
81
|
-
* shown as it: a move in the same plan pins the kind a block shows
|
|
82
|
-
* (`moveTo`), so a paragraph shown as its list's item (`itemOf`) that
|
|
83
|
-
* an outdent settles back to a paragraph is written back.
|
|
84
|
-
*/
|
|
85
|
-
const attr = (id, key, value) => {
|
|
86
|
-
const same = blockNodeOf(doc, id).getAttr(key) === value;
|
|
87
|
-
if (same && (runsView.displayType(id) ?? value) === value)
|
|
88
|
-
return [];
|
|
89
|
-
return [{ op: 'setBlockType', id, type: value }];
|
|
90
|
-
};
|
|
91
|
-
/**
|
|
92
|
-
* The kind `kid` shows once it leaves `from` for a slot under `parent`
|
|
93
|
-
* — the island and container rules, one answer:
|
|
94
|
-
* - an island's child takes `parent`'s default child (it leaves the
|
|
95
|
-
* island's kinds) — unless that renders no content while the child
|
|
96
|
-
* does: a line then takes the document's default kind (a code line
|
|
97
|
-
* shed into a columns layout is a paragraph), any other child
|
|
98
|
-
* keeps its kind, as `typeOf` shows one a peer adds meanwhile;
|
|
99
|
-
* - a container's item (its default child) takes `parent`'s default
|
|
100
|
-
* child — an item never shows outside its list — unless it
|
|
101
|
-
* stays inside an outer container of that kind (a nested list),
|
|
102
|
-
* or that kind renders no content (a column in a
|
|
103
|
-
* columns layout: its text would vanish);
|
|
104
|
-
* - then a plain block that does not fit `parent` (`fits`: a paragraph
|
|
105
|
-
* shed into a list) becomes its item, when that item renders content
|
|
106
|
-
* (`fitted`). A block that cannot (a paragraph in a columns
|
|
107
|
-
* layout) keeps its kind: its text never vanishes; any other kind (an
|
|
108
|
-
* image, a code block, a heading) keeps its kind.
|
|
109
|
-
*/
|
|
110
|
-
/**
|
|
111
|
-
* The kind a child of `island` (now `kind`) takes where the default
|
|
112
|
-
* child is `to`: `to` — unless `to` renders no content while the child
|
|
113
|
-
* does (its text would vanish): a line then takes the document's
|
|
114
|
-
* default kind, any other child keeps its kind (as `typeOf`).
|
|
115
|
-
*/
|
|
116
|
-
const leavingIsland = (island, kind, to) => {
|
|
117
|
-
if (rendersContentOf(to) || (kind !== undefined && !rendersContentOf(kind)))
|
|
118
|
-
return to;
|
|
119
|
-
return is(island, (type) => roles.line(type) !== undefined) ? defaultChild(null) : kind;
|
|
120
|
-
};
|
|
121
|
-
const settledKind = (from, kid, parent) => {
|
|
122
|
-
let kind = blockTypeOf(kid);
|
|
123
|
-
if (from !== null && isIsland(from))
|
|
124
|
-
kind = leavingIsland(from, kind, defaultChild(parent));
|
|
125
|
-
else if (from !== null && isContainer(from) && kind === defaultChild(from)) {
|
|
126
|
-
const within = parent === null ? [] : [parent, ...ancestorsOf(parent)];
|
|
127
|
-
const to = defaultChild(parent);
|
|
128
|
-
// An outer container of its kind keeps it an item (a nested list); a
|
|
129
|
-
// column's default child is the document's: it holds no items of its own.
|
|
130
|
-
const outer = kind !== defaultChild(null) &&
|
|
131
|
-
within.some((a) => isContainer(a) && defaultChild(a) === kind);
|
|
132
|
-
if (!outer && rendersContentOf(to))
|
|
133
|
-
kind = to;
|
|
134
|
-
}
|
|
135
|
-
return fitted(parent, kind);
|
|
136
|
-
};
|
|
137
|
-
/** The kind steps for the `kids` of `from` (`null`: the root) landing under `parent` (`settledKind`). */
|
|
138
|
-
const settle = (from, kids, parent) => kids.flatMap((kid) => {
|
|
139
|
-
const kind = settledKind(from, kid, parent);
|
|
140
|
-
return kind === undefined || kind === blockTypeOf(kid) ? [] : attr(kid, TYPE, kind);
|
|
141
|
-
});
|
|
142
|
-
/** `parent` and its display ancestors: where blocks landing under `parent` keep alive. */
|
|
143
|
-
const landing = (parent) => new Set(parent === null ? [] : [parent, ...ancestorsOf(parent)]);
|
|
144
|
-
/**
|
|
145
|
-
* `writes`, then every container one of `from` (the blocks' former
|
|
146
|
-
* parents) is left with no child but `leaving` removed
|
|
147
|
-
* (`del.range.empty-container`), and so on upward — never one of
|
|
148
|
-
* `kept`, and only an `emptiable` one (a container holding text of its
|
|
149
|
-
* own stays). The containers are found together, deepest first: a
|
|
150
|
-
* list that loses its last item along with a list nested in it goes
|
|
151
|
-
* too. Then every layout the plan leaves with one
|
|
152
|
-
* item or none dissolves (`dissolving`); `removed`: blocks the plan
|
|
153
|
-
* deletes besides (a block delete's members), which only that counts.
|
|
154
|
-
*/
|
|
155
|
-
const emptyingAll = (from, leaving, writes, kept = new Set(), removed = []) => {
|
|
156
|
-
const gone = new Set(leaving);
|
|
157
|
-
const tops = [];
|
|
158
|
-
const deepest = [...new Set(from)]
|
|
159
|
-
.filter((c) => c !== null)
|
|
160
|
-
.sort((a, b) => ancestorsOf(b).length - ancestorsOf(a).length);
|
|
161
|
-
for (const start of deepest) {
|
|
162
|
-
let top = null;
|
|
163
|
-
for (let c = start; c !== null && !gone.has(c) && !kept.has(c) && emptiable(c); c = positionOf(c).parent) {
|
|
164
|
-
if (!childrenIds(c).every((kid) => gone.has(kid)))
|
|
165
|
-
break;
|
|
166
|
-
gone.add((top = c));
|
|
167
|
-
}
|
|
168
|
-
if (top !== null)
|
|
169
|
-
tops.push(top);
|
|
170
|
-
}
|
|
171
|
-
const roots = tops.filter((t) => !ancestorsOf(t).some((a) => tops.includes(a)));
|
|
172
|
-
const steps = [...writes, ...roots.map((t) => remove(t, leaving))];
|
|
173
|
-
for (const id of removed)
|
|
174
|
-
gone.add(id);
|
|
175
|
-
return [...steps, ...dissolving(gone, leaving, steps)];
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* `layout.dissolving`: the steps that delete each layout a plan leaves
|
|
179
|
-
* with one item or none — `gone` (blocks the plan removes) and
|
|
180
|
-
* `leaving` (blocks it moves) no longer count, an item `writes` move or
|
|
181
|
-
* insert into it does. With one left, the layout and that item are
|
|
182
|
-
* deleted and the item's children moved right after the layout, where
|
|
183
|
-
* `layout.single` shows them, as the kind they show there (`settle`);
|
|
184
|
-
* with none, the layout is deleted. One plan with the write that caused
|
|
185
|
-
* it.
|
|
186
|
-
*/
|
|
187
|
-
const dissolving = (gone, leaving, writes) => {
|
|
188
|
-
const away = new Set([...gone, ...leaving]);
|
|
189
|
-
const layouts = new Set();
|
|
190
|
-
for (const id of away) {
|
|
191
|
-
const parent = positionOf(id)?.parent;
|
|
192
|
-
if (parent != null && isLayout(parent))
|
|
193
|
-
layouts.add(parent);
|
|
194
|
-
}
|
|
195
|
-
const { kids } = view();
|
|
196
|
-
const out = [];
|
|
197
|
-
for (const layout of layouts) {
|
|
198
|
-
if ([layout, ...ancestorsOf(layout)].some((a) => gone.has(a)))
|
|
199
|
-
continue;
|
|
200
|
-
const item = itemKindOf(layout);
|
|
201
|
-
const arriving = writes.reduce((n, w) => w.op === 'moveBlocks' && w.parent === layout
|
|
202
|
-
? n + w.ids.filter((id) => blockTypeOf(id) === item).length
|
|
203
|
-
: w.op === 'insertBlocks' && w.parent === layout
|
|
204
|
-
? n + w.specs.filter((spec) => spec.type === item).length
|
|
205
|
-
: n, 0);
|
|
206
|
-
const slots = kids.get(layout) ?? [];
|
|
207
|
-
const items = slots.filter((k) => !away.has(k.id) && blockTypeOf(k.id) === item);
|
|
208
|
-
if (items.length + arriving > 1)
|
|
209
|
-
continue;
|
|
210
|
-
const { parent } = positionOf(layout);
|
|
211
|
-
const last = items[0];
|
|
212
|
-
if (last === undefined) {
|
|
213
|
-
out.push(deleting(layout, [layout]));
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
// Its blocks keep their home slots: the read shows them in the layout's
|
|
217
|
-
// slot (`layout.single`), settled for its parent.
|
|
218
|
-
const moved = (kids.get(last.id) ?? []).filter((k) => !away.has(k.id));
|
|
219
|
-
const ids = moved.map((k) => k.id);
|
|
220
|
-
out.push(...settle(last.id, ids, parent), deleting(layout, [layout, last.id]));
|
|
221
|
-
}
|
|
222
|
-
return out;
|
|
223
|
-
};
|
|
224
|
-
/** `writes`, then `container` removed when they leave it no child but `leaving` (`emptyingAll`). */
|
|
225
|
-
const emptying = (container, leaving, writes, kept) => emptyingAll([container], leaving, writes, kept);
|
|
226
|
-
/**
|
|
227
|
-
* Delete: `removes` leave. Every one is marked with what it
|
|
228
|
-
* displays — an unmarked one would be promoted into the deleted slot
|
|
229
|
-
* (`displaySlotOf`).
|
|
230
|
-
*/
|
|
231
|
-
const deleting = (id, removes) => {
|
|
232
|
-
const marks = [...new Set(removes.flatMap((b) => view().displays(b)))];
|
|
233
|
-
return { op: 'deleteBlock', id, marks, removes };
|
|
234
|
-
};
|
|
235
|
-
/** Delete `id` and its subtree, `kept` children aside. */
|
|
236
|
-
const remove = (id, kept = []) => {
|
|
237
|
-
const removes = [];
|
|
238
|
-
const skip = new Set(kept);
|
|
239
|
-
const walk = (b) => {
|
|
240
|
-
removes.push(b);
|
|
241
|
-
for (const kid of childrenIds(b))
|
|
242
|
-
if (!skip.has(kid))
|
|
243
|
-
walk(kid);
|
|
244
|
-
};
|
|
245
|
-
walk(id);
|
|
246
|
-
return deleting(id, removes);
|
|
247
|
-
};
|
|
248
|
-
const merge = (from, into) => ({
|
|
249
|
-
op: 'mergeBlocks',
|
|
250
|
-
from,
|
|
251
|
-
into,
|
|
252
|
-
at: displayLength(into),
|
|
253
|
-
length: displayLength(from)
|
|
254
|
-
});
|
|
255
|
-
/** `[at, end)` — `[offset, offset + length)` clamped to `id`'s display. */
|
|
256
|
-
const clamp = (id, offset, length) => {
|
|
257
|
-
const total = displayLength(id);
|
|
258
|
-
const at = Math.max(0, Math.min(offset, total));
|
|
259
|
-
return [at, Math.min(total, at + Math.max(0, length))];
|
|
260
|
-
};
|
|
261
|
-
/** `id`'s display items, each with its `[at, end)` display offsets. */
|
|
262
|
-
const spans = (id) => {
|
|
263
|
-
let end = 0;
|
|
264
|
-
return contentItems(id).map((item) => {
|
|
265
|
-
const at = end;
|
|
266
|
-
end += item.kind === 'text' ? item.text.length : 1;
|
|
267
|
-
return { item, at, end };
|
|
268
|
-
});
|
|
269
|
-
};
|
|
270
|
-
/** The live inline atom `atom` in `id`'s display, or undefined. */
|
|
271
|
-
const atomOf = (id, atom) => live(id) ? spans(id).find(({ item }) => item.kind === 'inline' && item.id === atom) : undefined;
|
|
272
|
-
/** Text items overlapping `[at, end)` of `id` (inline atoms carry no marks). */
|
|
273
|
-
const textIn = (id, at, end) => spans(id).flatMap((s) => (s.item.kind === 'text' && s.at < end && s.end > at ? [s.item] : []));
|
|
274
|
-
/** Push the steps writing `items` into `id` from `at` (text runs and atoms in order); the end offset. */
|
|
275
|
-
const writeContent = (writes, id, at, items = []) => {
|
|
276
|
-
for (const item of items) {
|
|
277
|
-
if (item.kind === 'inline')
|
|
278
|
-
writes.push({ op: 'insertInline', id, offset: at++, atom: item });
|
|
279
|
-
else if (item.text !== '') {
|
|
280
|
-
writes.push({ op: 'insertText', id, offset: at, text: item.text, marks: item.marks });
|
|
281
|
-
at += item.text.length;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return at;
|
|
285
|
-
};
|
|
286
|
-
/** `writes`, then every container the `leaving` blocks leave with no child removed. */
|
|
287
|
-
const emptied = (leaving, writes, kept) => emptyingAll(leaving.map((id) => positionOf(id).parent), leaving, writes, kept);
|
|
288
|
-
return {
|
|
289
|
-
REFUSED,
|
|
290
|
-
plan,
|
|
291
|
-
moveTo,
|
|
292
|
-
move,
|
|
293
|
-
leaving,
|
|
294
|
-
attr,
|
|
295
|
-
leavingIsland,
|
|
296
|
-
settledKind,
|
|
297
|
-
settle,
|
|
298
|
-
landing,
|
|
299
|
-
emptyingAll,
|
|
300
|
-
dissolving,
|
|
301
|
-
emptying,
|
|
302
|
-
emptied,
|
|
303
|
-
deleting,
|
|
304
|
-
remove,
|
|
305
|
-
merge,
|
|
306
|
-
clamp,
|
|
307
|
-
atomOf,
|
|
308
|
-
textIn,
|
|
309
|
-
writeContent
|
|
310
|
-
};
|
|
311
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { BlockId } from '../../placement/model.js';
|
|
2
|
-
import type { ContentRun, RunViewDebug } from '../runs.js';
|
|
3
|
-
import type { IndexPlacement } from './placement.js';
|
|
4
|
-
import { type Deps } from './shared.js';
|
|
5
|
-
import type { IndexState } from './state.js';
|
|
6
|
-
/** The run cache of one doc's index. */
|
|
7
|
-
export declare const indexCache: (ix: IndexState & IndexPlacement) => {
|
|
8
|
-
intern: <T>(v: T) => T;
|
|
9
|
-
computeFresh: (b: BlockId) => {
|
|
10
|
-
fresh: ContentRun[];
|
|
11
|
-
deps: Deps;
|
|
12
|
-
};
|
|
13
|
-
computeRuns: (b: BlockId) => void;
|
|
14
|
-
debug: RunViewDebug;
|
|
15
|
-
};
|
|
16
|
-
export type IndexCache = ReturnType<typeof indexCache>;
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The run cache: each block's visible runs as a frozen snapshot, recomputed
|
|
3
|
-
* when a fold invalidates it, sharing unchanged runs and interned payloads.
|
|
4
|
-
*/
|
|
5
|
-
import { cloneJsonSafe } from '../../../utils/json.js';
|
|
6
|
-
import { canonKey, deepFreeze, displayOf, readSegs } from '../model.js';
|
|
7
|
-
import { runEquals } from './shared.js';
|
|
8
|
-
/** The run cache of one doc's index. */
|
|
9
|
-
export const indexCache = (ix) => {
|
|
10
|
-
const { blocks, textConsumers, listConsumers, cache, dirty, foldStats, frames, ownShim } = ix;
|
|
11
|
-
const rangeStats = { items: 0, markers: 0 };
|
|
12
|
-
// ── run cache ────────────────────────────────────────────────────
|
|
13
|
-
const internMap = new Map();
|
|
14
|
-
const debug = {
|
|
15
|
-
recomputes: 0,
|
|
16
|
-
recomputed: new Set(),
|
|
17
|
-
get itemsWalked() {
|
|
18
|
-
return rangeStats.items;
|
|
19
|
-
},
|
|
20
|
-
get markersWalked() {
|
|
21
|
-
return rangeStats.markers;
|
|
22
|
-
},
|
|
23
|
-
get frames() {
|
|
24
|
-
return frames.size;
|
|
25
|
-
},
|
|
26
|
-
get folds() {
|
|
27
|
-
return foldStats.folds;
|
|
28
|
-
},
|
|
29
|
-
get foldedPairs() {
|
|
30
|
-
return foldStats.pairs;
|
|
31
|
-
},
|
|
32
|
-
get foldedStructs() {
|
|
33
|
-
return foldStats.structs;
|
|
34
|
-
},
|
|
35
|
-
get placements() {
|
|
36
|
-
return foldStats.placed;
|
|
37
|
-
},
|
|
38
|
-
reset() {
|
|
39
|
-
debug.recomputes = 0;
|
|
40
|
-
debug.recomputed.clear();
|
|
41
|
-
foldStats.folds = 0;
|
|
42
|
-
foldStats.pairs = 0;
|
|
43
|
-
foldStats.structs = 0;
|
|
44
|
-
foldStats.placed = 0;
|
|
45
|
-
rangeStats.items = 0;
|
|
46
|
-
rangeStats.markers = 0;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
// ── interning / freezing ─────────────────────────────────────────
|
|
50
|
-
// `cloneJsonSafe` is total even against hostile replicated payloads,
|
|
51
|
-
// and interning the normalized form keys it by its own canonical shape.
|
|
52
|
-
// The shared instance is built FROM that key (sorted keys): it must not
|
|
53
|
-
// remember the key order its first reader happened to fold, or
|
|
54
|
-
// `toJSON` would depend on when the view was read.
|
|
55
|
-
const intern = (v) => {
|
|
56
|
-
const key = canonKey(cloneJsonSafe(v));
|
|
57
|
-
let f = internMap.get(key);
|
|
58
|
-
if (f === undefined)
|
|
59
|
-
internMap.set(key, (f = deepFreeze(JSON.parse(key))));
|
|
60
|
-
return f;
|
|
61
|
-
};
|
|
62
|
-
// ── dep bookkeeping ──────────────────────────────────────────────
|
|
63
|
-
const drop = (index, keys, b) => {
|
|
64
|
-
for (const k of keys) {
|
|
65
|
-
const set = index.get(k);
|
|
66
|
-
set?.delete(b);
|
|
67
|
-
if (set?.size === 0)
|
|
68
|
-
index.delete(k);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const add = (index, keys, b) => {
|
|
72
|
-
for (const k of keys) {
|
|
73
|
-
let set = index.get(k);
|
|
74
|
-
if (set === undefined)
|
|
75
|
-
index.set(k, (set = new Set()));
|
|
76
|
-
set.add(b);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
const applyDeps = (b, deps) => {
|
|
80
|
-
const old = cache.get(b)?.deps;
|
|
81
|
-
if (old) {
|
|
82
|
-
drop(textConsumers, old.texts, b);
|
|
83
|
-
drop(listConsumers, old.lists, b);
|
|
84
|
-
}
|
|
85
|
-
add(textConsumers, deps.texts, b);
|
|
86
|
-
add(listConsumers, deps.lists, b);
|
|
87
|
-
};
|
|
88
|
-
// ── snapshot construction ────────────────────────────────────────
|
|
89
|
-
const freezeFresh = (r) => Object.freeze(r);
|
|
90
|
-
/** The normalized fresh runs of `b` (interned payloads, equal marks merged) and their deps. */
|
|
91
|
-
const computeFresh = (b) => {
|
|
92
|
-
const deps = { texts: new Set(), lists: new Set([b]) };
|
|
93
|
-
const segs = displayOf(b, blocks, ownShim, (x, home) => {
|
|
94
|
-
deps.lists.add(x);
|
|
95
|
-
if (home !== undefined)
|
|
96
|
-
deps.texts.add(home);
|
|
97
|
-
// Every claim on a walked list is read, followed or not: a claim
|
|
98
|
-
// skipped for a dead target or a higher claimer becomes effective
|
|
99
|
-
// when that target revives or its winning claim goes away.
|
|
100
|
-
for (const c of blocks.get(x)?.claims ?? [])
|
|
101
|
-
deps.lists.add(c.m);
|
|
102
|
-
}) ?? [];
|
|
103
|
-
const fresh = [];
|
|
104
|
-
for (const item of readSegs(segs, rangeStats)) {
|
|
105
|
-
if (item.kind === 'text') {
|
|
106
|
-
const last = fresh[fresh.length - 1];
|
|
107
|
-
const marks = item.marks === undefined ? undefined : intern(item.marks);
|
|
108
|
-
if (last && last.kind === 'text' && last.marks === marks) {
|
|
109
|
-
last.text += item.text;
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
fresh.push({
|
|
113
|
-
kind: 'text',
|
|
114
|
-
text: item.text,
|
|
115
|
-
...(marks === undefined ? {} : { marks })
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
fresh.push({
|
|
121
|
-
kind: 'inline',
|
|
122
|
-
id: item.id,
|
|
123
|
-
type: item.type,
|
|
124
|
-
...(item.data === undefined ? {} : { data: intern(item.data) })
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return { fresh, deps };
|
|
129
|
-
};
|
|
130
|
-
/**
|
|
131
|
-
* Structural sharing: reuse unchanged run objects from `old` — maximal
|
|
132
|
-
* equal prefix + equal suffix; `old` itself when every run is identical.
|
|
133
|
-
*/
|
|
134
|
-
const reconcile = (old, fresh) => {
|
|
135
|
-
if (!old)
|
|
136
|
-
return Object.freeze(fresh.map(freezeFresh));
|
|
137
|
-
let s = 0;
|
|
138
|
-
const n = Math.min(old.length, fresh.length);
|
|
139
|
-
while (s < n && runEquals(old[s], fresh[s]))
|
|
140
|
-
s++;
|
|
141
|
-
if (s === old.length && s === fresh.length)
|
|
142
|
-
return old;
|
|
143
|
-
let eo = old.length - 1;
|
|
144
|
-
let ef = fresh.length - 1;
|
|
145
|
-
while (eo >= s && ef >= s && runEquals(old[eo], fresh[ef])) {
|
|
146
|
-
eo--;
|
|
147
|
-
ef--;
|
|
148
|
-
}
|
|
149
|
-
const out = new Array(fresh.length);
|
|
150
|
-
for (let i = 0; i < s; i++)
|
|
151
|
-
out[i] = old[i];
|
|
152
|
-
for (let i = s; i <= ef; i++)
|
|
153
|
-
out[i] = freezeFresh(fresh[i]);
|
|
154
|
-
for (let i = eo + 1; i < old.length; i++)
|
|
155
|
-
out[fresh.length - (old.length - i)] = old[i];
|
|
156
|
-
return Object.freeze(out);
|
|
157
|
-
};
|
|
158
|
-
// ── recompute ────────────────────────────────────────────────────
|
|
159
|
-
const computeRuns = (b) => {
|
|
160
|
-
const old = cache.get(b);
|
|
161
|
-
const { fresh, deps } = computeFresh(b);
|
|
162
|
-
const runs = reconcile(old?.runs, fresh);
|
|
163
|
-
applyDeps(b, deps);
|
|
164
|
-
cache.set(b, { runs, deps });
|
|
165
|
-
dirty.delete(b);
|
|
166
|
-
debug.recomputes++;
|
|
167
|
-
debug.recomputed.add(b);
|
|
168
|
-
};
|
|
169
|
-
return {
|
|
170
|
-
intern,
|
|
171
|
-
computeFresh,
|
|
172
|
-
computeRuns,
|
|
173
|
-
debug
|
|
174
|
-
};
|
|
175
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The claim graph (maintained): each block's owner along the max-stamp
|
|
3
|
-
* claims (`top`), the blocks each owner displays, and what a structural
|
|
4
|
-
* change of a block invalidates.
|
|
5
|
-
*/
|
|
6
|
-
import type { BlockId } from '../../placement/model.js';
|
|
7
|
-
import { type Claim, type Owner } from '../model.js';
|
|
8
|
-
import type { FoldCtx, IndexState } from './state.js';
|
|
9
|
-
/** The claim graph of one doc's index, over its state. */
|
|
10
|
-
export declare const indexClaims: (ix: IndexState) => {
|
|
11
|
-
ensureOwners: () => void;
|
|
12
|
-
ownerOf: (b: BlockId) => Owner;
|
|
13
|
-
top: (m: BlockId) => BlockId | undefined;
|
|
14
|
-
displays: (owner: BlockId) => readonly BlockId[];
|
|
15
|
-
noteClaims: (id: BlockId, before: readonly Claim[], after: readonly Claim[]) => void;
|
|
16
|
-
invalidateBlock: (id: BlockId, ctx: FoldCtx, claimsBefore?: Claim[]) => void;
|
|
17
|
-
};
|
|
18
|
-
export type IndexClaims = ReturnType<typeof indexClaims>;
|