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/doc/funnel.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* frame's one attribution pass.
|
|
6
|
-
*/
|
|
7
|
-
import { blockNodeOf, buildInline, materializeSpec, ownText, view as modelView, writePlacement, writeSplit } from '../placement/model.js';
|
|
8
|
-
import { ENTRY_FACET } from '../text/runs.js';
|
|
9
|
-
import { AT, DEL_PREFIX, ID, LAST_CHANGED_ATTR, TYPE } from '../schema.js';
|
|
10
|
-
import { writeLeaves } from '../data.js';
|
|
1
|
+
import { blockNodeOf, buildInline, insertSpecsAt, moveBlocksTo, ownText, view as modelView, writeSplit } from '../placement/model.js';
|
|
2
|
+
import { ENTRY_FACET, SEQ_FACET } from '../text/runs.js';
|
|
3
|
+
import { DEL_PREFIX, ID, LAST_CHANGED_ATTR, TYPE } from '../schema.js';
|
|
4
|
+
import { writeData } from '../data.js';
|
|
11
5
|
import { NOOP } from './plan.js';
|
|
12
6
|
import { claimInto, deleteRange, formatRangeIn, insertIntoText } from '../text/model.js';
|
|
13
7
|
/**
|
|
@@ -55,8 +49,8 @@ export const writeFunnel = (c) => {
|
|
|
55
49
|
// Each apply opens a frame that tracks the index's folds. When it
|
|
56
50
|
// closes, ONE pass over the facets the fold saw stamps attribution: a
|
|
57
51
|
// block whose registry entry was created gets its `createdBy` record; a block whose content, claims, type or data
|
|
58
|
-
// changed gets a contributor stamp; moves
|
|
59
|
-
// nothing. Two intents the effects cannot name are recorded by their
|
|
52
|
+
// changed gets a contributor stamp; moves (a record or a slot in a
|
|
53
|
+
// block's sequence) and delete marks stamp nothing. Two intents the effects cannot name are recorded by their
|
|
60
54
|
// steps: a split-born block inherits its source's contributors, and a
|
|
61
55
|
// merge survivor unions the absorbed block's. With no configured
|
|
62
56
|
// `actor` the funnel writes no attribution at all.
|
|
@@ -74,9 +68,12 @@ export const writeFunnel = (c) => {
|
|
|
74
68
|
else
|
|
75
69
|
for (const facet of facets)
|
|
76
70
|
if (!facet.startsWith(DEL_PREFIX) &&
|
|
77
|
-
![ENTRY_FACET,
|
|
71
|
+
![ENTRY_FACET, SEQ_FACET, ID, LAST_CHANGED_ATTR].includes(facet))
|
|
78
72
|
changed.add(id);
|
|
79
73
|
}
|
|
74
|
+
// A merge changed its survivor (its claim item, written quietly, is no edit of the text holding it).
|
|
75
|
+
for (const [into] of f.unions)
|
|
76
|
+
changed.add(into);
|
|
80
77
|
for (const id of created) {
|
|
81
78
|
const node = blockNodeOf(doc, id);
|
|
82
79
|
if (node !== null)
|
|
@@ -93,38 +90,46 @@ export const writeFunnel = (c) => {
|
|
|
93
90
|
/**
|
|
94
91
|
* A streamless block gets its own text before the first write that
|
|
95
92
|
* needs a stream (`ownText`: a writer derived from its dead
|
|
96
|
-
* incarnation); its attribution record follows the re-minted nonce.
|
|
93
|
+
* incarnation); its attribution record follows the re-minted nonce. A
|
|
94
|
+
* claim item needs the block's own stream (`claim`); text, any piece of
|
|
95
|
+
* its display.
|
|
97
96
|
*/
|
|
98
|
-
const needStream = (id) => {
|
|
99
|
-
|
|
97
|
+
const needStream = (id, claim = false) => {
|
|
98
|
+
const { own } = modelView(doc);
|
|
99
|
+
if (claim ? own.streamOf(id) !== undefined : (own.display(id) ?? []).length > 0)
|
|
100
100
|
return;
|
|
101
101
|
const { from, to } = ownText(doc, id);
|
|
102
102
|
BA.retarget(doc, id, from, to);
|
|
103
103
|
};
|
|
104
104
|
/** One planned step, written (the plan decided it; writers never refuse). */
|
|
105
105
|
const writeStep = (w, f) => {
|
|
106
|
-
//
|
|
106
|
+
// Delete marks need no view: they write one node.
|
|
107
107
|
if (w.op === 'deleteBlock')
|
|
108
108
|
return w.marks.forEach((id) => blockNodeOf(doc, id).setAttr(DEL_PREFIX + doc.clientID, true));
|
|
109
109
|
if (w.op === 'splitBlock' || w.op === 'insertText' || w.op === 'insertInline')
|
|
110
110
|
needStream(w.id);
|
|
111
|
-
|
|
111
|
+
// A claim item goes at the end of the stream of the block merged into.
|
|
112
|
+
if (w.op === 'mergeBlocks')
|
|
113
|
+
needStream(w.into, true);
|
|
114
|
+
const v = modelView(doc);
|
|
115
|
+
const { blocks, own } = v;
|
|
112
116
|
const node = (id) => blockNodeOf(doc, id);
|
|
113
117
|
switch (w.op) {
|
|
114
118
|
case 'insertBlocks':
|
|
115
|
-
return
|
|
119
|
+
return insertSpecsAt(doc, v, w.parent, w.index, w.specs, w.split === undefined ? null : (f.bounds.get(w.split) ?? null));
|
|
116
120
|
case 'moveBlocks':
|
|
117
|
-
return w.ids.
|
|
121
|
+
return moveBlocksTo(doc, v, w.ids, w.parent, w.index, w.side ?? 0, w.part ?? 0, w.at ?? null);
|
|
118
122
|
case 'splitBlock':
|
|
119
123
|
f.inherit.set(w.newId, w.id);
|
|
120
|
-
|
|
124
|
+
f.bounds.set(w.newId, writeSplit(doc, w.id, w.offset, w.newId, w.tail, w.parent, w.index));
|
|
125
|
+
return;
|
|
121
126
|
case 'mergeBlocks':
|
|
122
127
|
f.unions.push([w.into, w.from]);
|
|
123
|
-
return claimInto(
|
|
128
|
+
return f.quiet(() => claimInto(own, w.from, w.into));
|
|
124
129
|
case 'setBlockType':
|
|
125
130
|
return void node(w.id).setAttr(TYPE, w.type);
|
|
126
131
|
case 'patchData':
|
|
127
|
-
return
|
|
132
|
+
return writeData(dataNode(w.id, w.inlineId), w.writes);
|
|
128
133
|
case 'insertText':
|
|
129
134
|
return insertIntoText(doc, blocks, own, w.id, w.offset, w.text, w.marks);
|
|
130
135
|
case 'insertInline':
|
|
@@ -141,8 +146,10 @@ export const writeFunnel = (c) => {
|
|
|
141
146
|
* and fold the result from what that transaction did. A refusal writes
|
|
142
147
|
* nothing. A plan is valid only at the version it was prepared against,
|
|
143
148
|
* in the same synchronous turn: applying it anywhere else throws.
|
|
149
|
+
* `quiet`: it stamps no attribution (a suggestion's writer: its content
|
|
150
|
+
* is not the block's until accepted).
|
|
144
151
|
*/
|
|
145
|
-
const apply = (p) => {
|
|
152
|
+
const apply = (p, quiet) => {
|
|
146
153
|
if (!('writes' in p))
|
|
147
154
|
return write(() => p);
|
|
148
155
|
if (p.version !== runsView.version()) {
|
|
@@ -150,7 +157,7 @@ export const writeFunnel = (c) => {
|
|
|
150
157
|
}
|
|
151
158
|
return write(() => doc.transact(() => {
|
|
152
159
|
const frame = runsView.track();
|
|
153
|
-
const f = { inherit: new Map(), unions: [] };
|
|
160
|
+
const f = { inherit: new Map(), bounds: new Map(), unions: [], quiet: frame.quiet };
|
|
154
161
|
try {
|
|
155
162
|
for (const w of p.writes)
|
|
156
163
|
writeStep(w, f);
|
|
@@ -161,7 +168,8 @@ export const writeFunnel = (c) => {
|
|
|
161
168
|
throw error;
|
|
162
169
|
}
|
|
163
170
|
const folded = frame.end();
|
|
164
|
-
|
|
171
|
+
if (!quiet)
|
|
172
|
+
close(f, folded);
|
|
165
173
|
return folded.wrote ? { status: 'applied', ids: p.ids } : NOOP;
|
|
166
174
|
}));
|
|
167
175
|
};
|
package/dist/crdt/doc/gate.d.ts
CHANGED
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
*
|
|
18
18
|
* ```
|
|
19
19
|
* doc.get('blocks') registry — flat map, blockId → node('block')
|
|
20
|
-
* └ <blockId>
|
|
21
|
-
* (n: incarnation nonce)
|
|
20
|
+
* └ <blockId> n/type/d/…/del./wd./l + content
|
|
21
|
+
* (n: incarnation nonce; the id is the key)
|
|
22
22
|
* doc.get('meta') version record root
|
|
23
23
|
* ├ v : number SCHEMA_VERSION (LWW attr — concurrent init converges)
|
|
24
24
|
* └ schema : 'edytor-doc' SCHEMA_NAME
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
import type { EngineDoc } from '../engine-api.js';
|
|
28
|
-
export declare const SCHEMA_VERSION:
|
|
28
|
+
export declare const SCHEMA_VERSION: 11;
|
|
29
29
|
export declare const SCHEMA_NAME: "edytor-doc";
|
|
30
30
|
export declare const META_KEY: "meta";
|
|
31
31
|
/**
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { EngineApi, EngineDoc, EngineNode } from '../engine-api.js';
|
|
2
|
-
import type { TextDeletes } from '../text/deletes.js';
|
|
3
2
|
import { type IdSetOf, type StoreStruct } from '../structs.js';
|
|
4
3
|
import { type WriteFunnel } from './funnel.js';
|
|
5
4
|
import type { DocBase } from './reads.js';
|
|
@@ -11,6 +10,12 @@ export declare class StackItem {
|
|
|
11
10
|
deletes: IdSetOf;
|
|
12
11
|
/** Per-view metadata (`session/history.ts`: the selections around the step). */
|
|
13
12
|
readonly meta: Map<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* The client it was written under when that is not the replica's own (a
|
|
15
|
+
* suggestion's writer: its transaction's `writer`): it replays under it,
|
|
16
|
+
* and its records release with the history's.
|
|
17
|
+
*/
|
|
18
|
+
writer?: number;
|
|
14
19
|
constructor(inserts: IdSetOf, deletes: IdSetOf);
|
|
15
20
|
}
|
|
16
21
|
/** A step taken into a stack: added (a new step) or updated (the open one took a transaction). */
|
|
@@ -33,15 +38,12 @@ export type HistoryOptions = {
|
|
|
33
38
|
}) => boolean;
|
|
34
39
|
};
|
|
35
40
|
/** What a history is made over: the document and the rules of its replay. */
|
|
36
|
-
type Rules = {
|
|
41
|
+
export type Rules = {
|
|
37
42
|
Y: EngineApi;
|
|
38
43
|
doc: EngineDoc;
|
|
39
44
|
scope: EngineNode[];
|
|
40
|
-
/** The
|
|
41
|
-
|
|
42
|
-
restoreFilter(item: unknown): boolean;
|
|
43
|
-
onApply(tr: unknown, step: StackItem): void;
|
|
44
|
-
};
|
|
45
|
+
/** The records list a replay's text holds go to (fork patch YP20). */
|
|
46
|
+
records: EngineNode;
|
|
45
47
|
/** Whether an item a replay would delete is withdrawn instead (`hist.undo.withdraw`). */
|
|
46
48
|
withdraw(item: StoreStruct, step: StackItem): boolean;
|
|
47
49
|
/** Repair the attrs a replay left undefined. */
|
|
@@ -85,16 +87,36 @@ export declare class UndoManager {
|
|
|
85
87
|
redo(): StackItem | null;
|
|
86
88
|
/** Drop every step (undo and redo) and release what they kept for their replay. */
|
|
87
89
|
clear(): void;
|
|
90
|
+
/**
|
|
91
|
+
* The session ends: its page left for good (`hist.release.end`). Every
|
|
92
|
+
* step is dropped and released as a dropped step is (`#release`: what
|
|
93
|
+
* they kept is collected, the text this writer's records hold is
|
|
94
|
+
* deleted for good), in one transaction that, when a step marked a block
|
|
95
|
+
* deleted or withdrawn, also appends one release record (the
|
|
96
|
+
* {@link RELEASE_ROOT} list): its id names this writer and the clock the
|
|
97
|
+
* history ended at, so the room's purge removes what those marks hide at
|
|
98
|
+
* its next tick, past the horizon or not. Then the manager stops
|
|
99
|
+
* capturing (it is destroyed; the document attaches a fresh one if asked).
|
|
100
|
+
* A history that captured nothing writes nothing. Only for a page that
|
|
101
|
+
* cannot come back: a page the back/forward cache keeps, or one that
|
|
102
|
+
* only lost its connection, keeps its history.
|
|
103
|
+
*/
|
|
104
|
+
end(): void;
|
|
88
105
|
destroy: () => void;
|
|
89
106
|
}
|
|
90
107
|
/** What a facade's history is made over: its doc, the write funnel and the bound engine layers. */
|
|
91
108
|
export type HistoryContext = DocBase & Pick<WriteFunnel, 'write' | 'disposed'> & {
|
|
92
|
-
D: TextDeletes;
|
|
93
109
|
/** Stamp the version record before the history attaches. */
|
|
94
110
|
init: (doc: EngineDoc) => void;
|
|
95
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* The document's history rules over `doc` (the one set every history of an
|
|
114
|
+
* edytor document runs): its scope (the block registry, the text hold
|
|
115
|
+
* records, the document's data), the records a replay's text holds go to,
|
|
116
|
+
* the withdraw rule (`hist.undo.withdraw`) and the attr repair below.
|
|
117
|
+
*/
|
|
118
|
+
export declare const historyRules: (Y: EngineApi, doc: EngineDoc) => Rules;
|
|
96
119
|
/** The history factory of one facade. */
|
|
97
120
|
export declare const docHistory: (c: HistoryContext) => {
|
|
98
121
|
createUndoManager: (options?: HistoryOptions) => UndoManager;
|
|
99
122
|
};
|
|
100
|
-
export {};
|
package/dist/crdt/doc/history.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The document's history: the one undo manager (fork patch YP18 removed the
|
|
3
3
|
* engine's). It captures the tracked local transactions that write in its
|
|
4
|
-
* scope (the block registry, the text
|
|
4
|
+
* scope (the block registry, the text hold records, the document's data)
|
|
5
5
|
* into steps, and an undo or a redo replays a step in one transaction over
|
|
6
|
-
* the engine's struct primitives (`redoItem`, `keepItem`). Its rules: text
|
|
7
|
-
*
|
|
6
|
+
* the engine's struct primitives (`redoItem`, `keepItem`). Its rules: a text
|
|
7
|
+
* delete is a hold record (fork patch YP20), so undoing it deletes the record
|
|
8
|
+
* and the text shows again unless another writer's record holds it
|
|
9
|
+
* (`hist.undo.restore`); undoing typing holds the typed text; an undone
|
|
8
10
|
* creation is withdrawn (`hist.undo.withdraw`), an overwritten attr is
|
|
9
|
-
* repaired, the stack keeps `limit` steps,
|
|
10
|
-
* steps below it (`hist.purge.horizon`)
|
|
11
|
+
* repaired, the stack keeps `limit` steps, the purge horizon drops the
|
|
12
|
+
* steps below it (`hist.purge.horizon`), and a session's end releases them
|
|
13
|
+
* all (`hist.release.end`).
|
|
11
14
|
*/
|
|
12
15
|
import { getUnixTime } from 'lib0-v14/time';
|
|
13
|
-
import { ownTextIds } from '../text/model.js';
|
|
16
|
+
import { openSteps, ownTextIds } from '../text/model.js';
|
|
14
17
|
import { readHorizon } from '../purge.js';
|
|
15
|
-
import { BLOCK_NODE, DATA_LEAF_PREFIX, DOC_DATA_ROOT, HORIZON_ROOT, INLINE_NODE, LAST_CHANGED_ATTR, TYPE } from '../schema.js';
|
|
18
|
+
import { BLOCK_NODE, CONTENT_NODE, DATA_LEAF_PREFIX, DEL_PREFIX, DOC_DATA_ROOT, HORIZON_ROOT, INLINE_NODE, LAST_CHANGED_ATTR, RELEASE_ROOT, SUGGEST_ROOT, TEXT_DELETES_ROOT, TYPE, WITHDRAW_PREFIX } from '../schema.js';
|
|
16
19
|
import { changedUnder, collectable, collectNow, collects, dropSearchMarkers, engineOps, walkIdSet } from '../structs.js';
|
|
17
20
|
import { isInitialized } from './gate.js';
|
|
18
21
|
import { DocumentDestroyedError } from './funnel.js';
|
|
@@ -27,6 +30,12 @@ export class StackItem {
|
|
|
27
30
|
deletes;
|
|
28
31
|
/** Per-view metadata (`session/history.ts`: the selections around the step). */
|
|
29
32
|
meta = new Map();
|
|
33
|
+
/**
|
|
34
|
+
* The client it was written under when that is not the replica's own (a
|
|
35
|
+
* suggestion's writer: its transaction's `writer`): it replays under it,
|
|
36
|
+
* and its records release with the history's.
|
|
37
|
+
*/
|
|
38
|
+
writer;
|
|
30
39
|
constructor(inserts, deletes) {
|
|
31
40
|
this.inserts = inserts;
|
|
32
41
|
this.deletes = deletes;
|
|
@@ -37,6 +46,13 @@ const REPAIRED = {
|
|
|
37
46
|
[BLOCK_NODE]: [TYPE, LAST_CHANGED_ATTR],
|
|
38
47
|
[INLINE_NODE]: [TYPE]
|
|
39
48
|
};
|
|
49
|
+
/** A unit of a block's text: a character or an inline atom (what a hold record holds). */
|
|
50
|
+
const isUnit = (it) => {
|
|
51
|
+
const content = it.content;
|
|
52
|
+
return (it.parentSub === null &&
|
|
53
|
+
it.parent?.name === CONTENT_NODE &&
|
|
54
|
+
(typeof content?.str === 'string' || content?.type?.name === INLINE_NODE));
|
|
55
|
+
};
|
|
40
56
|
/**
|
|
41
57
|
* A document's undo manager: the undo and redo stacks of the tracked local
|
|
42
58
|
* transactions in its scope. `undo()`/`redo()` replay the top step and
|
|
@@ -64,6 +80,10 @@ export class UndoManager {
|
|
|
64
80
|
/** What dropped steps released (an item is released once every step that deleted it is gone). */
|
|
65
81
|
#released;
|
|
66
82
|
#horizon;
|
|
83
|
+
/** A captured step wrote a block's delete or withdraw mark: the session's end records its release. */
|
|
84
|
+
#marked = false;
|
|
85
|
+
/** The session ended (`end()`): nothing more is released. */
|
|
86
|
+
#ended = false;
|
|
67
87
|
constructor(rules, options = {}) {
|
|
68
88
|
this.#rules = rules;
|
|
69
89
|
this.#ops = engineOps(rules.Y);
|
|
@@ -78,7 +98,19 @@ export class UndoManager {
|
|
|
78
98
|
this.trackedOrigins.add(this);
|
|
79
99
|
this.#ops.on(rules.doc, 'afterTransaction', this.#afterTransaction);
|
|
80
100
|
this.#ops.on(rules.doc, 'destroy', this.destroy);
|
|
101
|
+
const open = openSteps.get(rules.doc) ?? new Set();
|
|
102
|
+
openSteps.set(rules.doc, open.add(this.#open));
|
|
81
103
|
}
|
|
104
|
+
/** What the step still capturing inserted (`null`: none is open): a text delete there removes. */
|
|
105
|
+
#open = () => {
|
|
106
|
+
const top = this.undoStack.at(-1);
|
|
107
|
+
const open = top !== undefined &&
|
|
108
|
+
!this.undoing &&
|
|
109
|
+
!this.redoing &&
|
|
110
|
+
this.lastChange > 0 &&
|
|
111
|
+
getUnixTime() - this.lastChange < this.captureTimeout;
|
|
112
|
+
return open ? top.inserts : null;
|
|
113
|
+
};
|
|
82
114
|
/** Be told of every step taken into a stack (added, or the open one updated). Returns the unsubscribe. */
|
|
83
115
|
observe(f) {
|
|
84
116
|
this.#observers.add(f);
|
|
@@ -118,8 +150,39 @@ export class UndoManager {
|
|
|
118
150
|
clear() {
|
|
119
151
|
this.#release([...this.undoStack.splice(0), ...this.redoStack.splice(0)]);
|
|
120
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* The session ends: its page left for good (`hist.release.end`). Every
|
|
155
|
+
* step is dropped and released as a dropped step is (`#release`: what
|
|
156
|
+
* they kept is collected, the text this writer's records hold is
|
|
157
|
+
* deleted for good), in one transaction that, when a step marked a block
|
|
158
|
+
* deleted or withdrawn, also appends one release record (the
|
|
159
|
+
* {@link RELEASE_ROOT} list): its id names this writer and the clock the
|
|
160
|
+
* history ended at, so the room's purge removes what those marks hide at
|
|
161
|
+
* its next tick, past the horizon or not. Then the manager stops
|
|
162
|
+
* capturing (it is destroyed; the document attaches a fresh one if asked).
|
|
163
|
+
* A history that captured nothing writes nothing. Only for a page that
|
|
164
|
+
* cannot come back: a page the back/forward cache keeps, or one that
|
|
165
|
+
* only lost its connection, keeps its history.
|
|
166
|
+
*/
|
|
167
|
+
end() {
|
|
168
|
+
if (this.#ended)
|
|
169
|
+
return;
|
|
170
|
+
this.#ended = true;
|
|
171
|
+
const dropped = [...this.undoStack.splice(0), ...this.redoStack.splice(0)];
|
|
172
|
+
const marked = this.#marked;
|
|
173
|
+
if (dropped.length > 0 || marked)
|
|
174
|
+
this.doc.transact(() => {
|
|
175
|
+
this.#release(dropped);
|
|
176
|
+
if (marked) {
|
|
177
|
+
const records = this.doc.get(RELEASE_ROOT);
|
|
178
|
+
records.insert(records.length, [true]);
|
|
179
|
+
}
|
|
180
|
+
}, HISTORY_TRIM);
|
|
181
|
+
this.destroy();
|
|
182
|
+
}
|
|
121
183
|
destroy = () => {
|
|
122
184
|
this.trackedOrigins.delete(this);
|
|
185
|
+
openSteps.get(this.doc)?.delete(this.#open);
|
|
123
186
|
this.#ops.off(this.doc, 'afterTransaction', this.#afterTransaction);
|
|
124
187
|
this.#ops.off(this.doc, 'destroy', this.destroy);
|
|
125
188
|
this.#observers.clear();
|
|
@@ -164,12 +227,22 @@ export class UndoManager {
|
|
|
164
227
|
stack.push(new StackItem(tr.insertSet, tr.deleteSet));
|
|
165
228
|
if (!undoing && !redoing)
|
|
166
229
|
this.lastChange = now;
|
|
230
|
+
// A block's delete or withdraw mark: what it hides waits for this
|
|
231
|
+
// history (its end releases it to the purge, `end()`).
|
|
232
|
+
if (!this.#marked)
|
|
233
|
+
for (const keys of tr.changed.values())
|
|
234
|
+
for (const key of keys)
|
|
235
|
+
if (typeof key === 'string' &&
|
|
236
|
+
(key.startsWith(DEL_PREFIX) || key.startsWith(WITHDRAW_PREFIX)))
|
|
237
|
+
this.#marked = true;
|
|
167
238
|
// What the step deleted is kept from collection while a step may bring it back.
|
|
168
239
|
ops.iterate(tr, tr.deleteSet, (item) => {
|
|
169
240
|
if (ops.isItem(item) && this.#inScope(item))
|
|
170
241
|
ops.keep(item);
|
|
171
242
|
});
|
|
172
243
|
const step = stack.at(-1);
|
|
244
|
+
if (tr.writer)
|
|
245
|
+
step.writer = tr.writer;
|
|
173
246
|
// A streamless block's own text is shared by every replica that
|
|
174
247
|
// typed into it first: no step captures it, so undoing the first
|
|
175
248
|
// typing removes the typing and keeps the text (and nonce).
|
|
@@ -190,59 +263,80 @@ export class UndoManager {
|
|
|
190
263
|
}
|
|
191
264
|
/**
|
|
192
265
|
* Replay the top step of `stack` in one transaction (origin: this
|
|
193
|
-
* manager): what it inserted is deleted (
|
|
194
|
-
*
|
|
266
|
+
* manager): what it inserted is deleted (text: held by a record of this
|
|
267
|
+
* writer; a block: withdrawn), what it deleted comes back (a text delete
|
|
268
|
+
* was a record: deleting it shows the text), the overwritten attrs are
|
|
195
269
|
* repaired. A step that changes nothing is skipped for the one below.
|
|
196
270
|
*/
|
|
197
271
|
#pop(stack) {
|
|
198
272
|
const ops = this.#ops;
|
|
199
|
-
const {
|
|
273
|
+
const { records, withdraw, repair } = this.#rules;
|
|
200
274
|
const deletedByHistory = (id) => [this.undoStack, this.redoStack].some((s) => s.some((step) => step.deletes.has(id.client, id.clock)));
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
275
|
+
// A step written under another client replays under it, and so does its replay's step.
|
|
276
|
+
const local = this.doc.clientID;
|
|
277
|
+
const writer = stack.at(-1)?.writer;
|
|
278
|
+
this.doc.clientID = writer ?? local;
|
|
279
|
+
try {
|
|
280
|
+
this.doc.transact((transaction) => {
|
|
281
|
+
const tr = transaction;
|
|
282
|
+
tr.writer = writer;
|
|
283
|
+
while (stack.length > 0 && this.currStackItem === null) {
|
|
284
|
+
const step = stack.pop();
|
|
285
|
+
const restore = new Set();
|
|
286
|
+
const remove = [];
|
|
287
|
+
const held = [];
|
|
288
|
+
let changed = false;
|
|
289
|
+
ops.iterate(tr, step.inserts, (struct) => {
|
|
290
|
+
if (!ops.isItem(struct))
|
|
291
|
+
return;
|
|
292
|
+
const item = ops.latest(tr, struct);
|
|
293
|
+
// An atom the step inserted is held whole: its attrs stay with it.
|
|
294
|
+
const atom = item.parent?._item;
|
|
295
|
+
if (atom?.id &&
|
|
296
|
+
isUnit(atom) &&
|
|
297
|
+
step.inserts.has(atom.id.client, atom.id.clock))
|
|
298
|
+
return;
|
|
299
|
+
if (!item.deleted && this.#inScope(item))
|
|
300
|
+
remove.push(item);
|
|
301
|
+
});
|
|
302
|
+
ops.iterate(tr, step.deletes, (struct) => {
|
|
303
|
+
if (!ops.isItem(struct) || !this.#inScope(struct))
|
|
304
|
+
return;
|
|
305
|
+
// Created and deleted within the step: nothing to bring back.
|
|
306
|
+
if (step.inserts.has(struct.id.client, struct.id.clock))
|
|
307
|
+
return;
|
|
308
|
+
// Text a record holds stays hidden: the record decides (removed with its node).
|
|
309
|
+
if ((struct.holds ?? 0) > 0)
|
|
310
|
+
return;
|
|
225
311
|
restore.add(struct);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
312
|
+
});
|
|
313
|
+
for (const item of restore)
|
|
314
|
+
changed =
|
|
315
|
+
ops.redo(tr, item, restore, step.inserts, deletedByHistory) !== null || changed;
|
|
316
|
+
// Children before parents: more is known when an item is judged.
|
|
317
|
+
for (let i = remove.length - 1; i >= 0; i--) {
|
|
318
|
+
const item = remove[i];
|
|
319
|
+
// Text the step wrote is held, never removed: a redo shows it again where it was.
|
|
320
|
+
if (isUnit(item))
|
|
321
|
+
held.push(item.id.client, item.id.clock, item.length);
|
|
322
|
+
else if (!withdraw(item, step))
|
|
323
|
+
ops.remove(tr, item);
|
|
324
|
+
changed = true;
|
|
325
|
+
}
|
|
326
|
+
records.hold(held);
|
|
327
|
+
this.currStackItem = changed ? step : null;
|
|
328
|
+
if (changed)
|
|
329
|
+
repair(step);
|
|
239
330
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
});
|
|
245
|
-
}
|
|
331
|
+
tr.changed.forEach((keys, node) => {
|
|
332
|
+
if (keys.has(null))
|
|
333
|
+
dropSearchMarkers(node);
|
|
334
|
+
});
|
|
335
|
+
}, this);
|
|
336
|
+
}
|
|
337
|
+
finally {
|
|
338
|
+
this.doc.clientID = local;
|
|
339
|
+
}
|
|
246
340
|
const step = this.currStackItem;
|
|
247
341
|
this.currStackItem = null;
|
|
248
342
|
return step;
|
|
@@ -250,8 +344,8 @@ export class UndoManager {
|
|
|
250
344
|
/**
|
|
251
345
|
* Release what dropped steps kept for their replay: the engine collects
|
|
252
346
|
* that content now, as it would have at the delete without a history
|
|
253
|
-
* (the doc's `gcFilter` still decides
|
|
254
|
-
*
|
|
347
|
+
* (the doc's `gcFilter` still decides), and the text their hold records
|
|
348
|
+
* hold is deleted for good. An item is released only
|
|
255
349
|
* once every step that deleted part of it was dropped (the engine merges
|
|
256
350
|
* deleted items across steps): never split, so the store keeps its
|
|
257
351
|
* merged items. Only the items themselves are released: a kept
|
|
@@ -269,6 +363,8 @@ export class UndoManager {
|
|
|
269
363
|
return (released.clients.get(st.id.client)?.getIds() ?? []).some((r) => r.clock <= st.id.clock && end <= r.clock + r.len);
|
|
270
364
|
};
|
|
271
365
|
const gc = collects(doc);
|
|
366
|
+
const ops = this.#ops;
|
|
367
|
+
const { records } = this.#rules;
|
|
272
368
|
doc.transact((tr) => {
|
|
273
369
|
for (const step of dropped)
|
|
274
370
|
walkIdSet(Y, doc, step.deletes, (st) => {
|
|
@@ -279,6 +375,34 @@ export class UndoManager {
|
|
|
279
375
|
if (gc && it.deleted && collectable(doc, it))
|
|
280
376
|
collectNow(it, tr);
|
|
281
377
|
});
|
|
378
|
+
// A dropped step's text deletes can never be undone here: what this
|
|
379
|
+
// writer's live records hold is deleted for good, as the purge does,
|
|
380
|
+
// and the engine collects it (`hist.delete-marks.release`). A record
|
|
381
|
+
// a captured remote frame wrote (the room's validation) is its
|
|
382
|
+
// writer's to take back.
|
|
383
|
+
const own = ops.idSet();
|
|
384
|
+
for (const step of dropped)
|
|
385
|
+
walkIdSet(Y, doc, step.inserts, (st, from, to) => {
|
|
386
|
+
if (st.parent === records &&
|
|
387
|
+
!st.deleted &&
|
|
388
|
+
(st.id.client === doc.clientID || st.id.client === step.writer))
|
|
389
|
+
own.add(st.id.client, from, to - from);
|
|
390
|
+
});
|
|
391
|
+
const held = ops.idSet();
|
|
392
|
+
ops.iterate(tr, own, (st) => {
|
|
393
|
+
const ids = st.content?.ids;
|
|
394
|
+
if (st.deleted || ids === undefined)
|
|
395
|
+
return;
|
|
396
|
+
for (let i = 0; i < ids.length; i += 3)
|
|
397
|
+
held.add(ids[i], ids[i + 1], ids[i + 2]);
|
|
398
|
+
ops.remove(tr, st);
|
|
399
|
+
});
|
|
400
|
+
ops.iterate(tr, held, (it) => {
|
|
401
|
+
if (it.removed !== false)
|
|
402
|
+
return;
|
|
403
|
+
ops.remove(tr, it);
|
|
404
|
+
dropSearchMarkers(it.parent);
|
|
405
|
+
});
|
|
282
406
|
}, HISTORY_TRIM);
|
|
283
407
|
}
|
|
284
408
|
/**
|
|
@@ -310,9 +434,13 @@ export class UndoManager {
|
|
|
310
434
|
this.#release(dropped);
|
|
311
435
|
}
|
|
312
436
|
}
|
|
313
|
-
/**
|
|
314
|
-
|
|
315
|
-
|
|
437
|
+
/**
|
|
438
|
+
* The document's history rules over `doc` (the one set every history of an
|
|
439
|
+
* edytor document runs): its scope (the block registry, the text hold
|
|
440
|
+
* records, the document's data), the records a replay's text holds go to,
|
|
441
|
+
* the withdraw rule (`hist.undo.withdraw`) and the attr repair below.
|
|
442
|
+
*/
|
|
443
|
+
export const historyRules = (Y, doc) => {
|
|
316
444
|
/**
|
|
317
445
|
* Undo repair, inside the replay's transaction. A replayed step
|
|
318
446
|
* deletes its own attr write and re-creates the value that write
|
|
@@ -350,9 +478,25 @@ export const docHistory = (c) => {
|
|
|
350
478
|
node.setAttr(key, value);
|
|
351
479
|
}
|
|
352
480
|
};
|
|
481
|
+
// The text hold records are in scope (an undo deletes the undoer's
|
|
482
|
+
// own, fork patch YP20). An undone creation withdraws the block instead
|
|
483
|
+
// of deleting it (`hist.undo.withdraw`).
|
|
484
|
+
const records = doc.get(TEXT_DELETES_ROOT);
|
|
485
|
+
return {
|
|
486
|
+
Y,
|
|
487
|
+
doc,
|
|
488
|
+
scope: [registryOf(doc), records, doc.get(DOC_DATA_ROOT), doc.get(SUGGEST_ROOT)],
|
|
489
|
+
records,
|
|
490
|
+
withdraw: withdrawOnUndo(doc),
|
|
491
|
+
repair: repairAttrs
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
/** The history factory of one facade. */
|
|
495
|
+
export const docHistory = (c) => {
|
|
496
|
+
const { Y, doc, init, write, disposed } = c;
|
|
353
497
|
/**
|
|
354
498
|
* The document's undo manager, SCOPED TO THE BLOCK REGISTRY (with the
|
|
355
|
-
* text
|
|
499
|
+
* text hold records and the document's data), attached only after the
|
|
356
500
|
* doc carries the schema version record:
|
|
357
501
|
*
|
|
358
502
|
* - the `meta` root (version stamp, schema name) is outside the scope,
|
|
@@ -373,20 +517,7 @@ export const docHistory = (c) => {
|
|
|
373
517
|
if (!isInitialized(doc))
|
|
374
518
|
init(doc);
|
|
375
519
|
});
|
|
376
|
-
|
|
377
|
-
// removes the undoer's own), and the history restores text only as the
|
|
378
|
-
// marks allow. An undone creation withdraws the block instead of
|
|
379
|
-
// deleting it (`hist.undo.withdraw`).
|
|
380
|
-
const marks = D.history(doc, () => um);
|
|
381
|
-
const um = new UndoManager({
|
|
382
|
-
Y,
|
|
383
|
-
doc,
|
|
384
|
-
scope: [registryOf(doc), D.scope(doc), doc.get(DOC_DATA_ROOT)],
|
|
385
|
-
marks,
|
|
386
|
-
withdraw: withdrawOnUndo(doc),
|
|
387
|
-
repair: repairAttrs
|
|
388
|
-
}, options);
|
|
389
|
-
return um;
|
|
520
|
+
return new UndoManager(historyRules(Y, doc), options);
|
|
390
521
|
};
|
|
391
522
|
return { createUndoManager };
|
|
392
523
|
};
|