edytor 3.0.0 → 5.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 +23 -12
- package/dist/cloudflare/DocumentRoom.js +10 -6
- 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 +139 -129
- package/dist/cloudflare/room/context.js +11 -4
- 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.d.ts +6 -3
- package/dist/cloudflare/room/purge.js +45 -19
- 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/room/updates.d.ts +3 -4
- package/dist/cloudflare/room/updates.js +6 -8
- 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 +34 -12
- package/dist/crdt/attribution/attribution.js +59 -21
- package/dist/crdt/attribution/block.d.ts +3 -3
- package/dist/crdt/attribution/block.js +7 -8
- package/dist/crdt/attribution/index.d.ts +1 -1
- package/dist/crdt/attribution/index.js +1 -1
- package/dist/crdt/data.d.ts +62 -31
- package/dist/crdt/data.js +211 -103
- package/dist/crdt/doc/capability.js +9 -13
- package/dist/crdt/doc/delete.d.ts +1 -1
- package/dist/crdt/doc/delete.js +13 -30
- package/dist/crdt/doc/funnel.d.ts +1 -1
- package/dist/crdt/doc/funnel.js +34 -26
- package/dist/crdt/doc/gate.d.ts +3 -3
- package/dist/crdt/doc/history.d.ts +31 -9
- package/dist/crdt/doc/history.js +204 -73
- package/dist/crdt/doc/layout.js +16 -27
- package/dist/crdt/doc/meta.js +17 -23
- package/dist/crdt/doc/moves.d.ts +1 -1
- package/dist/crdt/doc/moves.js +52 -32
- package/dist/crdt/doc/reads.d.ts +2 -2
- package/dist/crdt/doc/reads.js +20 -30
- package/dist/crdt/doc/seed.js +2 -2
- package/dist/crdt/doc/split-merge.js +12 -15
- package/dist/crdt/doc/steps.d.ts +25 -13
- package/dist/crdt/doc/steps.js +55 -125
- package/dist/crdt/doc/suggest.d.ts +167 -0
- package/dist/crdt/doc/suggest.js +579 -0
- package/dist/crdt/doc/types.d.ts +21 -9
- package/dist/crdt/document.d.ts +26 -6
- package/dist/crdt/document.js +40 -15
- package/dist/crdt/edytor-doc.d.ts +410 -390
- package/dist/crdt/edytor-doc.js +143 -95
- package/dist/crdt/engine-api.d.ts +39 -1
- package/dist/crdt/engine.d.ts +2 -2
- package/dist/crdt/engine.js +4 -5
- package/dist/crdt/flow.d.ts +2 -2
- package/dist/crdt/flow.js +29 -37
- package/dist/crdt/index.d.ts +134 -127
- package/dist/crdt/index.js +4 -1
- package/dist/crdt/operations.d.ts +12 -3
- package/dist/crdt/parts.d.ts +43 -0
- package/dist/crdt/parts.js +44 -0
- package/dist/crdt/placement/display.d.ts +6 -31
- package/dist/crdt/placement/display.js +41 -146
- package/dist/crdt/placement/keys.d.ts +63 -0
- package/dist/crdt/placement/keys.js +95 -0
- package/dist/crdt/placement/model.d.ts +119 -118
- package/dist/crdt/placement/model.js +242 -142
- package/dist/crdt/placement/resolve.d.ts +123 -35
- package/dist/crdt/placement/resolve.js +188 -153
- package/dist/crdt/placement/slots.d.ts +134 -0
- package/dist/crdt/placement/slots.js +152 -0
- 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/comments.d.ts +16 -1
- package/dist/crdt/protocols/comments.js +9 -1
- package/dist/crdt/providers/index.d.ts +6 -4
- package/dist/crdt/providers/index.js +3 -1
- package/dist/crdt/providers/room.d.ts +16 -2
- package/dist/crdt/providers/room.js +64 -17
- package/dist/crdt/providers/websocket.d.ts +6 -4
- package/dist/crdt/providers/websocket.js +14 -7
- package/dist/crdt/purge.d.ts +24 -9
- package/dist/crdt/purge.js +266 -126
- package/dist/crdt/restore.d.ts +4 -3
- package/dist/crdt/restore.js +27 -34
- package/dist/crdt/schema.d.ts +30 -21
- package/dist/crdt/schema.js +39 -23
- package/dist/crdt/storage.d.ts +1 -1
- package/dist/crdt/storage.js +1 -1
- package/dist/crdt/structs.d.ts +17 -15
- package/dist/crdt/structs.js +10 -22
- package/dist/crdt/text/index/cache.js +4 -0
- package/dist/crdt/text/index/claims.js +13 -23
- package/dist/crdt/text/index/fold.d.ts +2 -3
- package/dist/crdt/text/index/fold.js +194 -44
- package/dist/crdt/text/index/layout.d.ts +2 -4
- package/dist/crdt/text/index/layout.js +14 -30
- package/dist/crdt/text/index/placement.d.ts +26 -18
- package/dist/crdt/text/index/placement.js +420 -157
- package/dist/crdt/text/index/records.d.ts +3 -3
- package/dist/crdt/text/index/records.js +24 -31
- package/dist/crdt/text/index/shared.d.ts +11 -3
- package/dist/crdt/text/index/shared.js +29 -17
- package/dist/crdt/text/index/state.d.ts +21 -17
- package/dist/crdt/text/index/state.js +15 -17
- package/dist/crdt/text/index/streams.d.ts +10 -5
- package/dist/crdt/text/index/streams.js +78 -27
- package/dist/crdt/text/items.d.ts +10 -3
- package/dist/crdt/text/items.js +7 -0
- package/dist/crdt/text/model.d.ts +87 -62
- package/dist/crdt/text/model.js +91 -169
- package/dist/crdt/text/ranges.d.ts +1 -1
- package/dist/crdt/text/ranges.js +3 -3
- package/dist/crdt/text/rows.d.ts +13 -7
- package/dist/crdt/text/rows.js +24 -7
- package/dist/crdt/text/runs.d.ts +51 -42
- package/dist/crdt/text/runs.js +24 -7
- package/dist/crdt/text/streams.d.ts +26 -13
- package/dist/crdt/text/streams.js +41 -14
- package/dist/crdt/vendor/yjs/UPSTREAM.md +651 -21
- package/dist/crdt/vendor/yjs/dts/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/dts/index.d.ts +1 -1
- package/dist/crdt/vendor/yjs/dts/structs/AbstractStruct.d.ts +7 -0
- package/dist/crdt/vendor/yjs/dts/structs/Item.d.ts +197 -10
- package/dist/crdt/vendor/yjs/dts/utils/Doc.d.ts +6 -3
- package/dist/crdt/vendor/yjs/dts/utils/StructStore.d.ts +5 -18
- package/dist/crdt/vendor/yjs/dts/utils/Transaction.d.ts +15 -1
- package/dist/crdt/vendor/yjs/dts/utils/UpdateDecoder.d.ts +14 -6
- package/dist/crdt/vendor/yjs/dts/utils/UpdateEncoder.d.ts +33 -6
- package/dist/crdt/vendor/yjs/dts/utils/ids.d.ts +1 -65
- package/dist/crdt/vendor/yjs/dts/utils/marks.d.ts +6 -1
- package/dist/crdt/vendor/yjs/dts/ynode.d.ts +54 -7
- package/dist/crdt/vendor/yjs/global.d.ts +2 -0
- package/dist/crdt/vendor/yjs/src/index.js +1 -1
- package/dist/crdt/vendor/yjs/src/structs/AbstractStruct.js +10 -0
- package/dist/crdt/vendor/yjs/src/structs/Item.js +649 -51
- package/dist/crdt/vendor/yjs/src/utils/BlockSet.js +18 -9
- package/dist/crdt/vendor/yjs/src/utils/Doc.js +5 -3
- package/dist/crdt/vendor/yjs/src/utils/StructStore.js +5 -0
- package/dist/crdt/vendor/yjs/src/utils/Transaction.js +36 -13
- package/dist/crdt/vendor/yjs/src/utils/UpdateDecoder.js +88 -18
- package/dist/crdt/vendor/yjs/src/utils/UpdateEncoder.js +119 -32
- package/dist/crdt/vendor/yjs/src/utils/encoding.js +13 -1
- package/dist/crdt/vendor/yjs/src/utils/ids.js +7 -7
- package/dist/crdt/vendor/yjs/src/utils/marks.js +193 -8
- package/dist/crdt/vendor/yjs/src/utils/redo.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/transaction-helpers.js +1 -1
- package/dist/crdt/vendor/yjs/src/utils/updates.js +3 -3
- package/dist/crdt/vendor/yjs/src/ynode.js +199 -12
- package/dist/edytor.svelte.d.ts +1 -1
- package/dist/edytor.svelte.js +103 -75
- package/dist/events/beforeInputCommands.d.ts +2 -2
- package/dist/events/beforeInputCommands.js +2 -2
- 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/events/onPageHide.d.ts +3 -0
- package/dist/events/onPageHide.js +16 -0
- package/dist/export/forms.d.ts +50 -50
- 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/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 +28 -3
- 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 +11 -37
- 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 +130 -83
- package/dist/surface/pin.svelte.d.ts +9 -9
- package/dist/surface/pin.svelte.js +15 -22
- package/dist/surface/projector.svelte.d.ts +15 -3
- package/dist/surface/projector.svelte.js +67 -22
- package/dist/text/text.svelte.js +14 -19
- package/dist/themes/notion.css +29 -0
- package/package.json +4 -2
- package/dist/crdt/placement/rank.d.ts +0 -87
- package/dist/crdt/placement/rank.js +0 -332
- package/dist/crdt/placement/source.d.ts +0 -43
- package/dist/crdt/placement/source.js +0 -66
- package/dist/crdt/text/delete-index.d.ts +0 -129
- package/dist/crdt/text/delete-index.js +0 -365
- package/dist/crdt/text/deletes.d.ts +0 -76
- package/dist/crdt/text/deletes.js +0 -362
- package/dist/crdt/text/index/anchored.d.ts +0 -17
- package/dist/crdt/text/index/anchored.js +0 -197
package/dist/crdt/edytor-doc.js
CHANGED
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
*/
|
|
49
49
|
import { setMarkEdges } from './text/marks.js';
|
|
50
50
|
import { keepingReplaced } from './incarnations.js';
|
|
51
|
+
import { clientsOf } from './structs.js';
|
|
51
52
|
import { SCHEMA } from './schema.js';
|
|
52
53
|
import { bindModel, blockText, crdtId, listBlockIds, project, resolveBlock } from './placement/model.js';
|
|
53
54
|
import { bindText } from './text/model.js';
|
|
54
|
-
import { bindDeletes } from './text/deletes.js';
|
|
55
55
|
import { bindPurge, readHorizon } from './purge.js';
|
|
56
56
|
import { restoreDocument } from './restore.js';
|
|
57
57
|
import { bindRuns, indexOf } from './text/runs.js';
|
|
@@ -84,15 +84,29 @@ export { DEFAULT_HISTORY_LIMIT } from './doc/history.js';
|
|
|
84
84
|
* The per-doc facade factory over the engine `Y` (the editor's documents
|
|
85
85
|
* use it directly; {@link bindDocModel} is the public binding around it).
|
|
86
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* Give `doc` edytor's rule — a registry value a concurrent creation replaced
|
|
89
|
+
* keeps its subtree (fork patch YP14, `keepingReplaced`) — or refuse it
|
|
90
|
+
* loudly when it has integrated updates without it: the engine decides what
|
|
91
|
+
* a replaced value keeps when it integrates, so a document that took updates
|
|
92
|
+
* under the engine's default could lose a subtree its peers keep, forever.
|
|
93
|
+
* Create it with `createDoc()` (or `createDocument`/`loadDocument`), never
|
|
94
|
+
* `new Y.Doc()`.
|
|
95
|
+
*/
|
|
96
|
+
export const adoptRules = (doc) => {
|
|
97
|
+
const d = doc;
|
|
98
|
+
if (d.keepReplaced == null && clientsOf(doc).size > 0)
|
|
99
|
+
throw new TypeError("edytor: this document took updates before it carried edytor's rules (create it with createDoc(), createDocument or loadDocument, not new Y.Doc())");
|
|
100
|
+
return keepingReplaced(d);
|
|
101
|
+
};
|
|
87
102
|
export const docFacade = (Y, layers) => {
|
|
88
|
-
const D = bindDeletes(Y);
|
|
89
103
|
// Compact per-block attribution writes (`attribution/block.ts`).
|
|
90
104
|
// One bound instance per engine binding; its suppression memory is
|
|
91
105
|
// per-doc (WeakMap-keyed), so facades on the same doc share it.
|
|
92
106
|
const BA = bindBlockAttribution(Y);
|
|
93
107
|
const { init, seed } = bindSeed(Y, BA);
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
const create = (doc, config = {}) => {
|
|
109
|
+
adoptRules(doc);
|
|
96
110
|
setMarkEdges(doc, config.markEdge ?? (() => undefined));
|
|
97
111
|
const roleOf = config.roleOf ?? (() => undefined);
|
|
98
112
|
const defaultType = config.defaultType ?? 'paragraph';
|
|
@@ -109,21 +123,42 @@ export const docFacade = (Y, layers) => {
|
|
|
109
123
|
// A layout displays only its items — its
|
|
110
124
|
// `defaultChild` kind — and only two or more (`layout.*`). A table
|
|
111
125
|
// displays only its rows and a row only its cells (`table.*`).
|
|
126
|
+
let kinds = new Map();
|
|
127
|
+
let declared;
|
|
112
128
|
const roles = {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
129
|
+
of: (type) => {
|
|
130
|
+
let k = kinds.get(type);
|
|
131
|
+
if (k === undefined) {
|
|
132
|
+
const role = type === null ? undefined : roleOf(type);
|
|
133
|
+
const own = type === null ? undefined : defaultChildOf(type);
|
|
134
|
+
const renders = type === null || rendersContentOf(type);
|
|
135
|
+
kinds.set(type, (k = {
|
|
136
|
+
childless: role?.void === true,
|
|
137
|
+
island: role?.island === true,
|
|
138
|
+
container: !renders && role?.void !== true && role?.island !== true,
|
|
139
|
+
rendersContent: renders,
|
|
140
|
+
child: own ?? defaultType,
|
|
141
|
+
line: role?.island === true && role.lines === true ? own : undefined,
|
|
142
|
+
layout: role?.layout === true ? own : undefined,
|
|
143
|
+
table: role?.table === true ? own : undefined
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
return k;
|
|
121
147
|
},
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
declared: () => {
|
|
149
|
+
if (declared === undefined) {
|
|
150
|
+
const all = [...(config.kinds?.() ?? [])].map(roles.of);
|
|
151
|
+
const rows = new Map(all.flatMap((k) => (k.table ? [[k.table, roles.of(k.table).child]] : [])));
|
|
152
|
+
declared = {
|
|
153
|
+
lines: new Set(all.flatMap((k) => k.line ?? [])),
|
|
154
|
+
layouts: [...(config.kinds?.() ?? [])].filter((t) => roles.of(t).layout !== undefined),
|
|
155
|
+
items: new Set(all.flatMap((k) => k.layout ?? [])),
|
|
156
|
+
rows,
|
|
157
|
+
cells: new Set(rows.values())
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return declared;
|
|
161
|
+
}
|
|
127
162
|
};
|
|
128
163
|
if (config.roleOf)
|
|
129
164
|
runsView.roles(roles);
|
|
@@ -141,56 +176,55 @@ export const docFacade = (Y, layers) => {
|
|
|
141
176
|
funnel.dispose();
|
|
142
177
|
events.close();
|
|
143
178
|
};
|
|
144
|
-
const { createUndoManager } = docHistory({ ...base, ...funnel,
|
|
179
|
+
const { createUndoManager } = docHistory({ ...base, ...funnel, init });
|
|
145
180
|
const { anchorAt, followUndo, resolveAnchor } = docAnchors(reads);
|
|
146
181
|
const steps = { ...shape, ...planSteps(shape) };
|
|
147
182
|
const { plan } = steps;
|
|
148
|
-
// The prepared op families (`OpsContext`: the reads, capability and step
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const {
|
|
152
|
-
const {
|
|
153
|
-
const { dataSteps, replaceData,
|
|
154
|
-
const
|
|
183
|
+
// The prepared op families (`OpsContext`: the reads, capability and step
|
|
184
|
+
// writers): every op they answer is a document write, but for the reads
|
|
185
|
+
// and step writers taken out here.
|
|
186
|
+
const { landingOf, ...moves } = moveOps(steps);
|
|
187
|
+
const { besideAt, ...layouts } = layoutOps(steps);
|
|
188
|
+
const { dataSteps, replaceData, ...metas } = metaOps(steps);
|
|
189
|
+
const deletes = deleteOps(steps);
|
|
155
190
|
/** What range deletion and flow placement compose: the step writers and the ops. */
|
|
156
|
-
const ops = { ...steps,
|
|
191
|
+
const ops = { ...steps, ...moves, ...deletes, dataSteps, replaceData };
|
|
157
192
|
/** Every document op, prepared — `apply(prepare.op(…))` is the op. */
|
|
158
193
|
const prepare = {
|
|
159
|
-
|
|
160
|
-
|
|
194
|
+
...moves,
|
|
195
|
+
...layouts,
|
|
196
|
+
...splitMergeOps(steps),
|
|
197
|
+
...deletes,
|
|
198
|
+
...metas,
|
|
199
|
+
...contentOps(steps),
|
|
161
200
|
/**
|
|
162
201
|
* Move `id` to the last position under `parent` — under a container
|
|
163
202
|
* it is no item of, under its last item (`nestParent`: Tab after a list).
|
|
164
203
|
*/
|
|
165
204
|
nestBlock: (id, parent) => {
|
|
166
205
|
const at = nestParent([ref(id)], ref(parent));
|
|
167
|
-
return moveBlocks([id], { parent: at, index: childrenIds(at).length });
|
|
206
|
+
return moves.moveBlocks([id], { parent: at, index: childrenIds(at).length });
|
|
168
207
|
},
|
|
169
|
-
unNestBlock,
|
|
170
|
-
unNestBlocks,
|
|
171
|
-
liftOut,
|
|
172
|
-
placeBeside,
|
|
173
|
-
wrapInLayout,
|
|
174
|
-
splitBlock,
|
|
175
|
-
mergeBlocks,
|
|
176
|
-
mergeBackward,
|
|
177
|
-
mergeForward,
|
|
178
|
-
deleteBlock,
|
|
179
|
-
setBlock,
|
|
180
|
-
setBlockType,
|
|
181
|
-
patchData,
|
|
182
|
-
duplicateBlock,
|
|
183
|
-
insertText,
|
|
184
|
-
deleteText,
|
|
185
|
-
formatRange,
|
|
186
|
-
clearMarks,
|
|
187
|
-
insertInline,
|
|
188
|
-
removeInline,
|
|
189
208
|
/** Delete a block selection — one plan; only the members leave (`deleteBlocks` above). */
|
|
190
|
-
deleteBlocks: (ids) => deleteBlocks(ids),
|
|
209
|
+
deleteBlocks: (ids) => deletes.deleteBlocks(ids),
|
|
191
210
|
...rangeDeleteOps(ops),
|
|
192
211
|
...flowOps(ops)
|
|
193
212
|
};
|
|
213
|
+
/**
|
|
214
|
+
* What a part built over this document reads beyond the facade (the
|
|
215
|
+
* shared suggestions, `doc/suggest.ts`): the reads, a facade over
|
|
216
|
+
* another doc (`create`, with this `config`);
|
|
217
|
+
* `v` counts its changes no index fact shows (in `version`), and
|
|
218
|
+
* `base`, when set, is the export `toJSON()` gives unless asked for
|
|
219
|
+
* the suggestions.
|
|
220
|
+
*/
|
|
221
|
+
const part = {
|
|
222
|
+
...reads,
|
|
223
|
+
config,
|
|
224
|
+
create: create,
|
|
225
|
+
v: 0,
|
|
226
|
+
base: undefined
|
|
227
|
+
};
|
|
194
228
|
/**
|
|
195
229
|
* Plans prepared at one version, as one plan (refused if any part is;
|
|
196
230
|
* stamped with the oldest part's version, so `apply` refuses a stale
|
|
@@ -212,7 +246,11 @@ export const docFacade = (Y, layers) => {
|
|
|
212
246
|
* omitted when empty): the one serializer, {@link blockJSON}, over the
|
|
213
247
|
* root's visible children.
|
|
214
248
|
*/
|
|
215
|
-
const toJSON = () => {
|
|
249
|
+
const toJSON = (options) => {
|
|
250
|
+
// A part's export first (the shared suggestions': without the pending ones, `sugg.json.base`).
|
|
251
|
+
const base = !options?.suggestions && part.base?.();
|
|
252
|
+
if (base)
|
|
253
|
+
return base;
|
|
216
254
|
const data = docData();
|
|
217
255
|
const children = childrenIds(null).map(blockJSON);
|
|
218
256
|
return Object.keys(data).length > 0
|
|
@@ -222,32 +260,17 @@ export const docFacade = (Y, layers) => {
|
|
|
222
260
|
// ── facade object ─────────────────────────────────────────────────
|
|
223
261
|
/** A read taking an id first: the id normalizes at ingress. */
|
|
224
262
|
const byRef = (f) => (id, ...rest) => f(ref(id), ...rest);
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
/** Replicated `meta.v` schema version (the module-level read, bound). */
|
|
232
|
-
schemaVersion: () => schemaVersion(doc),
|
|
233
|
-
/**
|
|
234
|
-
* The schema gate for this doc — throws {@link SchemaMismatchError}
|
|
235
|
-
* on unversioned-content or unsupported-version state. Consumers
|
|
236
|
-
* (`Edytor.sync()`, packed consumers) call it before trusting
|
|
237
|
-
* a synced doc.
|
|
238
|
-
*/
|
|
239
|
-
checkSchema: () => checkSchema(doc),
|
|
240
|
-
assertSchema: () => assertSchema(doc),
|
|
241
|
-
dispose,
|
|
242
|
-
createUndoManager,
|
|
263
|
+
/**
|
|
264
|
+
* The public operations (`DocumentOperations`), each name once: what
|
|
265
|
+
* an `EdytorDocument` forwards. The facade has them and the internal
|
|
266
|
+
* members below.
|
|
267
|
+
*/
|
|
268
|
+
const api = {
|
|
243
269
|
// reads — every id argument normalizes at ingress
|
|
244
270
|
project: () => project(doc),
|
|
245
271
|
toJSON,
|
|
246
272
|
blockJSON: byRef(blockJSON),
|
|
247
273
|
childrenIds: byRef(childrenIds),
|
|
248
|
-
/** Visible children of `parent` with their ranks, in `(rank, id)` order. */
|
|
249
|
-
childSlots: (parent) => view().kids.get(parent) ?? [],
|
|
250
|
-
slotOf: byRef(slotOf),
|
|
251
274
|
positionOf: byRef(positionOf),
|
|
252
275
|
pathOf: byRef(pathOf),
|
|
253
276
|
parentOf: byRef((id) => positionOf(id)?.parent ?? null),
|
|
@@ -265,8 +288,6 @@ export const docFacade = (Y, layers) => {
|
|
|
265
288
|
* O(1) per call; live-replicated (remote values read the same).
|
|
266
289
|
*/
|
|
267
290
|
blockAttribution: byRef((id) => blockAttributionOf(doc, id)),
|
|
268
|
-
crdtId: byRef((id) => crdtId(doc, id)),
|
|
269
|
-
resolveBlock: byRef((id) => resolveBlock(doc, id)),
|
|
270
291
|
displayLength: byRef(displayLength),
|
|
271
292
|
contentItems: byRef(contentItems),
|
|
272
293
|
hasBlock: byRef(hasBlock),
|
|
@@ -278,10 +299,6 @@ export const docFacade = (Y, layers) => {
|
|
|
278
299
|
// caret anchors — backing-text-bound selection endpoints
|
|
279
300
|
anchorAt: byRef(anchorAt),
|
|
280
301
|
resolveAnchor,
|
|
281
|
-
followUndo,
|
|
282
|
-
// roles
|
|
283
|
-
/** Re-read the roles after `roleOf` answers differently (roles adopted later). */
|
|
284
|
-
rolesChanged: () => runsView.roles(roles),
|
|
285
302
|
isVoid: byRef(isVoid),
|
|
286
303
|
isIsland: byRef(isIsland),
|
|
287
304
|
isLines: byRef(isLines),
|
|
@@ -317,10 +334,8 @@ export const docFacade = (Y, layers) => {
|
|
|
317
334
|
nestParent: (ids, parent) => nestParent(ids.map(ref), ref(parent)),
|
|
318
335
|
/** Where a block of `kind` at `id`'s place lands, and the containers it leaves (`liftOut`). */
|
|
319
336
|
landingOf: (id, kind, after) => landingOf(ref(id), ref(kind), after),
|
|
320
|
-
defaultChild: byRef(defaultChild),
|
|
321
337
|
// maintained runs (bound to this doc)
|
|
322
338
|
runs: byRef(runsView.runs),
|
|
323
|
-
contentJSON: byRef(runsView.contentJSON),
|
|
324
339
|
// events
|
|
325
340
|
onChange,
|
|
326
341
|
// every op — each returns an {@link OpResult}; `apply(prepare.op(…))`
|
|
@@ -333,10 +348,6 @@ export const docFacade = (Y, layers) => {
|
|
|
333
348
|
// transactions (composed ops already run in one; expose for callers
|
|
334
349
|
// that batch several ops into one undo step / one event)
|
|
335
350
|
transact: (fn, origin) => write(() => doc.transact(fn, origin)),
|
|
336
|
-
// escape hatch for the history and debugging — the bound engine layers.
|
|
337
|
-
model: layers.model,
|
|
338
|
-
text: layers.text,
|
|
339
|
-
runsView,
|
|
340
351
|
// ── model-state version ─────────────────────────────
|
|
341
352
|
/**
|
|
342
353
|
* The index version — bumps on every fold that changed derived
|
|
@@ -345,11 +356,57 @@ export const docFacade = (Y, layers) => {
|
|
|
345
356
|
* post-write state — this is the read-your-writes contract.
|
|
346
357
|
*/
|
|
347
358
|
get version() {
|
|
348
|
-
|
|
359
|
+
// A part's change may show in no index fact (a suggestion's decision).
|
|
360
|
+
return runsView.version() + part.v;
|
|
349
361
|
}
|
|
350
362
|
};
|
|
351
|
-
|
|
363
|
+
const internal = {
|
|
364
|
+
// lifecycle
|
|
365
|
+
init: (opts) => write(() => init(doc, opts)),
|
|
366
|
+
/** Apply the deterministic seed of `value` and the document's `data` — the document's seed decision. */
|
|
367
|
+
seed: (value, data) => write(() => seed(doc, value, defaultType, data)),
|
|
368
|
+
isInitialized: () => isInitialized(doc),
|
|
369
|
+
/** Replicated `meta.v` schema version (the module-level read, bound). */
|
|
370
|
+
schemaVersion: () => schemaVersion(doc),
|
|
371
|
+
/**
|
|
372
|
+
* The schema gate for this doc — throws {@link SchemaMismatchError}
|
|
373
|
+
* on unversioned-content or unsupported-version state. Consumers
|
|
374
|
+
* (`Edytor.sync()`, packed consumers) call it before trusting
|
|
375
|
+
* a synced doc.
|
|
376
|
+
*/
|
|
377
|
+
checkSchema: () => checkSchema(doc),
|
|
378
|
+
assertSchema: () => assertSchema(doc),
|
|
379
|
+
dispose,
|
|
380
|
+
createUndoManager,
|
|
381
|
+
/** @internal What a part built over the document reads and writes (`doc/suggest.ts`). */
|
|
382
|
+
part,
|
|
383
|
+
/** Visible children of `parent` with their ranks, in `(rank, id)` order. */
|
|
384
|
+
childSlots: (parent) => view().kids.get(parent) ?? [],
|
|
385
|
+
slotOf: byRef(slotOf),
|
|
386
|
+
crdtId: byRef((id) => crdtId(doc, id)),
|
|
387
|
+
resolveBlock: byRef((id) => resolveBlock(doc, id)),
|
|
388
|
+
followUndo,
|
|
389
|
+
// roles
|
|
390
|
+
/** Re-read the roles after `roleOf` answers differently (roles adopted later). */
|
|
391
|
+
rolesChanged: (place = true) => {
|
|
392
|
+
kinds = new Map();
|
|
393
|
+
declared = undefined;
|
|
394
|
+
if (place)
|
|
395
|
+
runsView.roles(roles);
|
|
396
|
+
},
|
|
397
|
+
defaultChild: byRef(defaultChild),
|
|
398
|
+
contentJSON: byRef(runsView.contentJSON),
|
|
399
|
+
// escape hatch for the history and debugging — the bound engine layers.
|
|
400
|
+
model: layers.model,
|
|
401
|
+
text: layers.text,
|
|
402
|
+
runsView,
|
|
403
|
+
/** The public members' names (`EdytorDocument` forwards them). */
|
|
404
|
+
api: api
|
|
405
|
+
};
|
|
406
|
+
// The public members joined as they are (the `version` getter stays one).
|
|
407
|
+
return Object.defineProperties(internal, Object.getOwnPropertyDescriptors(api));
|
|
352
408
|
};
|
|
409
|
+
return create;
|
|
353
410
|
};
|
|
354
411
|
/**
|
|
355
412
|
* Bind the assembled model to a concrete engine surface. `Y` must be the
|
|
@@ -361,7 +418,6 @@ export const bindDocModel = (Y) => {
|
|
|
361
418
|
const M = bindModel(Y);
|
|
362
419
|
const T = bindText(Y);
|
|
363
420
|
const R = bindRuns(Y);
|
|
364
|
-
const D = bindDeletes(Y);
|
|
365
421
|
const { init, seed } = bindSeed(Y, bindBlockAttribution(Y));
|
|
366
422
|
const create = docFacade(Y, { model: M, text: T });
|
|
367
423
|
return {
|
|
@@ -390,14 +446,6 @@ export const bindDocModel = (Y) => {
|
|
|
390
446
|
* not have integrated anything yet.
|
|
391
447
|
*/
|
|
392
448
|
newDoc: (opts) => keepingReplaced(new Y.Doc(opts)),
|
|
393
|
-
/**
|
|
394
|
-
* Keep, on `doc`, the text a replica may have to copy again: its
|
|
395
|
-
* `gcFilter` then spares a deleted copy's content, as every facade's
|
|
396
|
-
* history does. A doc that only relays updates (the room) calls it
|
|
397
|
-
* before applying anything, so what it collects and encodes matches
|
|
398
|
-
* what an editing replica keeps.
|
|
399
|
-
*/
|
|
400
|
-
keepCopies: (doc) => void D.scope(doc),
|
|
401
449
|
/** The bound engine layers (same instances the facades use). */
|
|
402
450
|
model: M,
|
|
403
451
|
text: T,
|
|
@@ -49,6 +49,10 @@ export type EngineItemRef = {
|
|
|
49
49
|
*/
|
|
50
50
|
export interface EngineTransaction {
|
|
51
51
|
changed?: Map<unknown, Set<string | null>>;
|
|
52
|
+
/** The visibility changes hold records made, in order: `client, clock, ±len` per item (`> 0` shown). */
|
|
53
|
+
holds?: number[];
|
|
54
|
+
/** The units whose mover changed (the fork's move records), in order: `client, clock, len` per item. */
|
|
55
|
+
moves?: number[];
|
|
52
56
|
}
|
|
53
57
|
/**
|
|
54
58
|
* Structural minimum of the vendored `Y.Node` (v14) used by the placement
|
|
@@ -70,7 +74,34 @@ export interface EngineNode {
|
|
|
70
74
|
forEachAttr(f: (value: unknown, key: string, node: EngineNode) => void): void;
|
|
71
75
|
attrKeys(): IterableIterator<string>;
|
|
72
76
|
insert(index: number, content: unknown): void;
|
|
73
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Delete `length` live units at `index`. With `into` (the document's
|
|
79
|
+
* records list), they are held by one record appended there instead (a
|
|
80
|
+
* delete its writer can take back), except those `fresh` names, which are
|
|
81
|
+
* removed. With `propose` (a suggestion's delete), each
|
|
82
|
+
* unit is sorted: `1` held as above (its author's own), `2` proposed (an
|
|
83
|
+
* id list appended to `propose.into`) or `3` stepped over; proposed and
|
|
84
|
+
* stepped-over units read gone until the engine's `unhide`.
|
|
85
|
+
*/
|
|
86
|
+
delete(index: number, length?: number, into?: EngineNode | null, fresh?: unknown, propose?: {
|
|
87
|
+
into: EngineNode;
|
|
88
|
+
sort: (client: number, clock: number) => number;
|
|
89
|
+
} | null): void;
|
|
90
|
+
/** Append one hold record of `ids` (`client, clock, len` triples) to this records list: they read as deleted while it lives. */
|
|
91
|
+
hold(ids: number[]): void;
|
|
92
|
+
/**
|
|
93
|
+
* Move the units `ids` names (`client, clock, len` triples, in order) to
|
|
94
|
+
* live index `index` of this sequence: one move record there (the fork's
|
|
95
|
+
* `ContentMove`); `side` is its class among the items of its origin (`0`
|
|
96
|
+
* after, `1` before), `at` the chain it stands at for the sort of two
|
|
97
|
+
* records at one origin — the units from the root sequence down to the
|
|
98
|
+
* unit it stands at, outermost first (`null`: the first unit it names) —
|
|
99
|
+
* and `part` its order among the records standing there.
|
|
100
|
+
*/
|
|
101
|
+
move(index: number, ids: number[], side?: number, part?: number, at?: ReadonlyArray<{
|
|
102
|
+
client: number;
|
|
103
|
+
clock: number;
|
|
104
|
+
}> | null): void;
|
|
74
105
|
}
|
|
75
106
|
/** Structural minimum of the vendored `Y.Doc`. */
|
|
76
107
|
export interface EngineDoc {
|
|
@@ -98,6 +129,13 @@ export interface EngineDoc {
|
|
|
98
129
|
*/
|
|
99
130
|
on(name: 'commit', f: (transaction: never) => void): void;
|
|
100
131
|
off(name: 'commit', f: (transaction: never) => void): void;
|
|
132
|
+
/**
|
|
133
|
+
* Doc-level `beforeCommit` channel (the fork's) — fires once per
|
|
134
|
+
* outermost transaction, after its function returned and while the
|
|
135
|
+
* transaction is still open: a listener's writes join it.
|
|
136
|
+
*/
|
|
137
|
+
on(name: 'beforeCommit', f: (transaction: never) => void): void;
|
|
138
|
+
off(name: 'beforeCommit', f: (transaction: never) => void): void;
|
|
101
139
|
/** Doc destruction event — doc-scoped state must release on destroy. */
|
|
102
140
|
on(name: 'destroy', f: () => void): void;
|
|
103
141
|
off(name: 'destroy', f: () => void): void;
|
package/dist/crdt/engine.d.ts
CHANGED
package/dist/crdt/engine.js
CHANGED
|
@@ -37,16 +37,15 @@ export const Y = {
|
|
|
37
37
|
createIdSet: V.createIdSet,
|
|
38
38
|
diffIdSet: V.diffIdSet,
|
|
39
39
|
insertIntoIdSet: V.insertIntoIdSet,
|
|
40
|
-
//
|
|
40
|
+
// the document's undo manager (`doc/history.ts`, fork patch YP18)
|
|
41
41
|
iterateStructsByIdSet: V.iterateStructsByIdSet,
|
|
42
42
|
getItemCleanStart: V.getItemCleanStart,
|
|
43
43
|
redoItem: V.redoItem,
|
|
44
|
-
// the document's undo manager (`doc/history.ts`, fork patch YP18)
|
|
45
44
|
keepItem: V.keepItem,
|
|
46
45
|
followRedone: V.followRedone,
|
|
47
46
|
isParentOf: V.isParentOf,
|
|
48
47
|
mergeIdSets: V.mergeIdSets,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
createID: V.createID,
|
|
49
|
+
// a suggestion's proposed deletion reads gone until its plan ends (fork patch YP32)
|
|
50
|
+
unhide: V.unhide
|
|
52
51
|
};
|
package/dist/crdt/flow.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* - a `whole` flow (a block-selection copy) goes after the block, replacing
|
|
22
22
|
* it when empty (`flow.whole`); over selected blocks the lines take their
|
|
23
23
|
* slot (`flow.slot`), and at a slot they are placed with nothing replaced
|
|
24
|
-
* (`flow.place`: an accepted suggestion),
|
|
24
|
+
* (`flow.place`: an accepted suggestion), as plain inserts (`insertBlocks`: their order
|
|
25
25
|
* against a peer's split beside them is not claimed); a void
|
|
26
26
|
* takes one run (`flow.void`);
|
|
27
27
|
* - a table's cell is never split: it takes the lines a flow shows, nested
|
|
@@ -82,7 +82,7 @@ export type FlowContext = OpsContext & Pick<MoveOps, 'insertBlocks'> & Pick<Dele
|
|
|
82
82
|
* paragraph (`doc.empty.virtual`) place the caret by it.
|
|
83
83
|
*/
|
|
84
84
|
export declare const placedEnd: (roleOf: (kind: string) => {
|
|
85
|
-
|
|
85
|
+
childless: boolean;
|
|
86
86
|
rendersContent: boolean;
|
|
87
87
|
}) => (s: BlockSpec) => DocPosition | undefined;
|
|
88
88
|
/** `insertFlow`, prepared. */
|
package/dist/crdt/flow.js
CHANGED
|
@@ -14,7 +14,7 @@ const lengthOf = (l) => (l.content ?? []).reduce((n, i) => n + (i.kind === 'text
|
|
|
14
14
|
export const placedEnd = (roleOf) => {
|
|
15
15
|
const endOf = (s) => {
|
|
16
16
|
const role = roleOf(s.type);
|
|
17
|
-
if (role.rendersContent && !role.
|
|
17
|
+
if (role.rendersContent && !role.childless)
|
|
18
18
|
return { block: s.id, offset: lengthOf(s) };
|
|
19
19
|
for (const kid of [...(s.children ?? [])].reverse()) {
|
|
20
20
|
const at = endOf(kid);
|
|
@@ -27,15 +27,9 @@ export const placedEnd = (roleOf) => {
|
|
|
27
27
|
};
|
|
28
28
|
/** `insertFlow`, prepared. */
|
|
29
29
|
export const flowOps = (c) => {
|
|
30
|
-
|
|
31
|
-
const roleOf = (kind) => ({
|
|
32
|
-
void: c.roles.childless(kind),
|
|
33
|
-
island: c.roles.island(kind),
|
|
34
|
-
rendersContent: c.roles.rendersContent(kind),
|
|
35
|
-
layout: c.roles.layout(kind)
|
|
36
|
-
});
|
|
30
|
+
const roleOf = c.roles.of;
|
|
37
31
|
/** What a kind is in a table: the table, its row or its cell. */
|
|
38
|
-
const tableKind = (kind) =>
|
|
32
|
+
const tableKind = (kind) => roleOf(kind).table !== undefined
|
|
39
33
|
? 'table'
|
|
40
34
|
: c.isRowKind(kind)
|
|
41
35
|
? 'row'
|
|
@@ -94,7 +88,9 @@ export const flowOps = (c) => {
|
|
|
94
88
|
const placed = specs(dest.parent);
|
|
95
89
|
const p = c.insertBlocks(dest, placed);
|
|
96
90
|
const at = endOf(placed.at(-1)) ?? { block: last().id, offset: lengthOf(last()) };
|
|
97
|
-
|
|
91
|
+
// The lines are placed while the replaced blocks still show (each step reads the
|
|
92
|
+
// display as it stands), then those go.
|
|
93
|
+
return 'writes' in p ? { ...c.plan(p.ids, [...p.writes, ...pre]), at } : p;
|
|
98
94
|
};
|
|
99
95
|
if ('replace' in target || 'slot' in target) {
|
|
100
96
|
let slot;
|
|
@@ -148,7 +144,7 @@ export const flowOps = (c) => {
|
|
|
148
144
|
const apart = (l, under) => {
|
|
149
145
|
const kind = l.type && fit(under, l.type);
|
|
150
146
|
const role = kind ? roleOf(kind) : undefined;
|
|
151
|
-
return !!role && (!role.rendersContent || role.
|
|
147
|
+
return !!role && (!role.rendersContent || role.childless || role.island);
|
|
152
148
|
};
|
|
153
149
|
// At the end of a container's header whose body shows, what follows the caret
|
|
154
150
|
// leads its children and the body stays, as Enter opens a first child
|
|
@@ -188,55 +184,53 @@ export const flowOps = (c) => {
|
|
|
188
184
|
return c.REFUSED;
|
|
189
185
|
const writes = [];
|
|
190
186
|
/** A joined line's children lead the block it joins; `moved` follow them. */
|
|
191
|
-
const kids = (to, l, moved = []
|
|
187
|
+
const kids = (to, l, moved = []) => {
|
|
192
188
|
const k = l?.children?.length ?? 0;
|
|
193
189
|
if (k)
|
|
194
|
-
writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children
|
|
190
|
+
writes.push({ op: 'insertBlocks', parent: to, index: 0, specs: l.children });
|
|
195
191
|
if (moved.length)
|
|
196
|
-
writes.push({
|
|
197
|
-
op: 'moveBlocks',
|
|
198
|
-
ids: moved,
|
|
199
|
-
parent: to,
|
|
200
|
-
index: k,
|
|
201
|
-
ranks: ranks.slice(k)
|
|
202
|
-
});
|
|
192
|
+
writes.push({ op: 'moveBlocks', ids: moved, parent: to, index: k });
|
|
203
193
|
};
|
|
204
194
|
/**
|
|
205
195
|
* `l` joins `B` at `at`: its content, its kind when `B` shows no text (a header
|
|
206
196
|
* keeps its own, as Enter does), its children first.
|
|
207
197
|
*/
|
|
208
|
-
const join = (l, at
|
|
198
|
+
const join = (l, at) => {
|
|
209
199
|
const end = c.writeContent(writes, B, at, l.content);
|
|
210
200
|
const kind = l.type && fit(parent, l.type);
|
|
211
201
|
if (len === 0 && !header && kind && kind === l.type)
|
|
212
202
|
writes.push(...c.attr(B, TYPE, kind), ...(c.dataSteps(B, c.replaceData(l.data ?? {})) ?? []));
|
|
213
203
|
if (!shut)
|
|
214
|
-
kids(B, l
|
|
204
|
+
kids(B, l);
|
|
215
205
|
return end;
|
|
216
206
|
};
|
|
217
|
-
/** The placed lines at `at` under `home
|
|
218
|
-
const place = (at,
|
|
207
|
+
/** The placed lines at `at` under `home` (`split`: the tail of the split they come with). */
|
|
208
|
+
const place = (at, split) => {
|
|
219
209
|
if (placed.length)
|
|
220
|
-
writes.push({
|
|
210
|
+
writes.push({
|
|
211
|
+
op: 'insertBlocks',
|
|
212
|
+
parent: home,
|
|
213
|
+
index: at,
|
|
214
|
+
specs: placed,
|
|
215
|
+
...(split !== undefined && { split })
|
|
216
|
+
});
|
|
221
217
|
};
|
|
222
218
|
// Under a header, the first line's children, the placed lines and the rest, in order.
|
|
223
219
|
const k = joinsHead ? (first.children?.length ?? 0) : 0;
|
|
224
|
-
const
|
|
225
|
-
const slot = inside ? 0 : index + 1;
|
|
220
|
+
const slot = inside ? k : index + 1;
|
|
226
221
|
// Where the placed lines end the caret: after their last shown line (`flow.apart`).
|
|
227
222
|
const end = placed.length ? endOf(placed.at(-1)) : undefined;
|
|
228
223
|
// Nothing before the position and the first line stands apart: the lines go before `B`,
|
|
229
224
|
// which keeps its text and takes a joining last line, or goes when empty and not needed.
|
|
230
225
|
if (!joinsHead && o === 0 && !inside) {
|
|
231
|
-
place(index
|
|
226
|
+
place(index);
|
|
232
227
|
if (joinsTail) {
|
|
233
228
|
const at = join(last(), 0);
|
|
234
229
|
// A closed header's taken line's nested lines follow it, the caret ending them.
|
|
235
230
|
const after = shut ? specs(parent, last().children ?? []) : [];
|
|
236
231
|
if (after.length) {
|
|
237
|
-
const ranks = c.ranksFor(parent, index + 1, after.length, [], true);
|
|
238
232
|
const slot = index + placed.length + 1;
|
|
239
|
-
writes.push({ op: 'insertBlocks', parent, index: slot, specs: after
|
|
233
|
+
writes.push({ op: 'insertBlocks', parent, index: slot, specs: after });
|
|
240
234
|
}
|
|
241
235
|
const caret = after.length ? endOf(after.at(-1)) : undefined;
|
|
242
236
|
const touched = [...ids, B, ...after.map((s) => s.id)];
|
|
@@ -246,13 +240,13 @@ export const flowOps = (c) => {
|
|
|
246
240
|
return { ...c.plan(ids, [...writes, c.remove(B, [])]), at: end };
|
|
247
241
|
return { ...c.plan([...ids, B], writes), at: end ?? { block: B, offset: 0 } };
|
|
248
242
|
}
|
|
249
|
-
const head = joinsHead ? join(first, o
|
|
243
|
+
const head = joinsHead ? join(first, o) : o;
|
|
250
244
|
if (lines.length === 1 && joinsHead)
|
|
251
245
|
return { ...c.plan([B], writes), at: { block: B, offset: head } };
|
|
252
246
|
const moved = inside ? [] : c.childrenIds(B).filter((id) => !view.hidden?.(id));
|
|
253
247
|
// Nothing after the position to keep, and the caret has a line: no split.
|
|
254
248
|
if (!joinsTail && o === len && moved.length === 0 && end) {
|
|
255
|
-
place(slot
|
|
249
|
+
place(slot);
|
|
256
250
|
return { ...c.plan([B, ...ids], writes), at: end };
|
|
257
251
|
}
|
|
258
252
|
// The rest of `B` stays in a shown line: the last line's block when it joins, else
|
|
@@ -266,9 +260,7 @@ export const flowOps = (c) => {
|
|
|
266
260
|
: bare || inside
|
|
267
261
|
? { type: c.defaultChild(home), data: {} }
|
|
268
262
|
: { type: c.kindToCopy(B), data: c.blockDataOf(B) };
|
|
269
|
-
//
|
|
270
|
-
const ranks = inside ? lead.slice(k) : c.pieceRanks(B, o, placed.length + 1);
|
|
271
|
-
const rank = ranks.pop();
|
|
263
|
+
// After `B` (under a header: after the first line's children); the placed lines go before it.
|
|
272
264
|
const length = len - o;
|
|
273
265
|
writes.push({
|
|
274
266
|
op: 'splitBlock',
|
|
@@ -278,10 +270,10 @@ export const flowOps = (c) => {
|
|
|
278
270
|
newId: T,
|
|
279
271
|
tail,
|
|
280
272
|
parent: home,
|
|
281
|
-
|
|
273
|
+
index: slot
|
|
282
274
|
});
|
|
283
275
|
kids(T, joinsTail ? last() : undefined, moved);
|
|
284
|
-
place(slot,
|
|
276
|
+
place(slot, T);
|
|
285
277
|
const at = joinsTail
|
|
286
278
|
? { block: T, offset: c.writeContent(writes, T, 0, last().content) }
|
|
287
279
|
: (end ?? { block: T, offset: 0 });
|