@lexical/yjs 0.44.1-nightly.20260519.0 → 0.45.1-dev.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/{RenderSnapshot.d.ts → dist/RenderSnapshot.d.ts} +0 -7
- package/dist/simpleDiffWithCursor.d.ts +12 -0
- package/package.json +31 -16
- package/src/Bindings.ts +135 -0
- package/src/CollabDecoratorNode.ts +112 -0
- package/src/CollabElementNode.ts +696 -0
- package/src/CollabLineBreakNode.ts +70 -0
- package/src/CollabTextNode.ts +179 -0
- package/src/CollabV2Mapping.ts +117 -0
- package/src/RenderSnapshot.ts +97 -0
- package/src/SyncCursors.ts +823 -0
- package/src/SyncEditorStates.ts +469 -0
- package/src/SyncV2.ts +871 -0
- package/src/Utils.ts +648 -0
- package/src/index.ts +151 -0
- package/src/simpleDiffWithCursor.ts +49 -0
- package/src/types.ts +27 -0
- /package/{Bindings.d.ts → dist/Bindings.d.ts} +0 -0
- /package/{CollabDecoratorNode.d.ts → dist/CollabDecoratorNode.d.ts} +0 -0
- /package/{CollabElementNode.d.ts → dist/CollabElementNode.d.ts} +0 -0
- /package/{CollabLineBreakNode.d.ts → dist/CollabLineBreakNode.d.ts} +0 -0
- /package/{CollabTextNode.d.ts → dist/CollabTextNode.d.ts} +0 -0
- /package/{CollabV2Mapping.d.ts → dist/CollabV2Mapping.d.ts} +0 -0
- /package/{LexicalYjs.dev.js → dist/LexicalYjs.dev.js} +0 -0
- /package/{LexicalYjs.dev.mjs → dist/LexicalYjs.dev.mjs} +0 -0
- /package/{LexicalYjs.js → dist/LexicalYjs.js} +0 -0
- /package/{LexicalYjs.js.flow → dist/LexicalYjs.js.flow} +0 -0
- /package/{LexicalYjs.mjs → dist/LexicalYjs.mjs} +0 -0
- /package/{LexicalYjs.node.mjs → dist/LexicalYjs.node.mjs} +0 -0
- /package/{LexicalYjs.prod.js → dist/LexicalYjs.prod.js} +0 -0
- /package/{LexicalYjs.prod.mjs → dist/LexicalYjs.prod.mjs} +0 -0
- /package/{SyncCursors.d.ts → dist/SyncCursors.d.ts} +0 -0
- /package/{SyncEditorStates.d.ts → dist/SyncEditorStates.d.ts} +0 -0
- /package/{SyncV2.d.ts → dist/SyncV2.d.ts} +0 -0
- /package/{Utils.d.ts → dist/Utils.d.ts} +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{types.d.ts → dist/types.d.ts} +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {Binding} from '.';
|
|
10
|
+
import type {CollabElementNode} from './CollabElementNode';
|
|
11
|
+
import type {LineBreakNode, NodeKey} from 'lexical';
|
|
12
|
+
import type {Map as YMap} from 'yjs';
|
|
13
|
+
|
|
14
|
+
import {$getNodeByKey, $isLineBreakNode} from 'lexical';
|
|
15
|
+
|
|
16
|
+
export class CollabLineBreakNode {
|
|
17
|
+
_map: YMap<unknown>;
|
|
18
|
+
_key: NodeKey;
|
|
19
|
+
_parent: CollabElementNode;
|
|
20
|
+
_type: 'linebreak';
|
|
21
|
+
|
|
22
|
+
constructor(map: YMap<unknown>, parent: CollabElementNode) {
|
|
23
|
+
this._key = '';
|
|
24
|
+
this._map = map;
|
|
25
|
+
this._parent = parent;
|
|
26
|
+
this._type = 'linebreak';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getNode(): null | LineBreakNode {
|
|
30
|
+
const node = $getNodeByKey(this._key);
|
|
31
|
+
return $isLineBreakNode(node) ? node : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getKey(): NodeKey {
|
|
35
|
+
return this._key;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getSharedType(): YMap<unknown> {
|
|
39
|
+
return this._map;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getType(): string {
|
|
43
|
+
return this._type;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getSize(): number {
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
getOffset(): number {
|
|
51
|
+
const collabElementNode = this._parent;
|
|
52
|
+
return collabElementNode.getChildOffset(this);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
destroy(binding: Binding): void {
|
|
56
|
+
const collabNodeMap = binding.collabNodeMap;
|
|
57
|
+
if (collabNodeMap.get(this._key) === this) {
|
|
58
|
+
collabNodeMap.delete(this._key);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function $createCollabLineBreakNode(
|
|
64
|
+
map: YMap<unknown>,
|
|
65
|
+
parent: CollabElementNode,
|
|
66
|
+
): CollabLineBreakNode {
|
|
67
|
+
const collabNode = new CollabLineBreakNode(map, parent);
|
|
68
|
+
map._collabNode = collabNode;
|
|
69
|
+
return collabNode;
|
|
70
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {Binding} from '.';
|
|
10
|
+
import type {CollabElementNode} from './CollabElementNode';
|
|
11
|
+
import type {NodeKey, NodeMap, TextNode} from 'lexical';
|
|
12
|
+
import type {Map as YMap} from 'yjs';
|
|
13
|
+
|
|
14
|
+
import invariant from '@lexical/internal/invariant';
|
|
15
|
+
import {
|
|
16
|
+
$getNodeByKey,
|
|
17
|
+
$getSelection,
|
|
18
|
+
$isRangeSelection,
|
|
19
|
+
$isTextNode,
|
|
20
|
+
} from 'lexical';
|
|
21
|
+
|
|
22
|
+
import simpleDiffWithCursor from './simpleDiffWithCursor';
|
|
23
|
+
import {$syncPropertiesFromYjs, syncPropertiesFromLexical} from './Utils';
|
|
24
|
+
|
|
25
|
+
function $diffTextContentAndApplyDelta(
|
|
26
|
+
collabNode: CollabTextNode,
|
|
27
|
+
key: NodeKey,
|
|
28
|
+
prevText: string,
|
|
29
|
+
nextText: string,
|
|
30
|
+
): void {
|
|
31
|
+
const selection = $getSelection();
|
|
32
|
+
let cursorOffset = nextText.length;
|
|
33
|
+
|
|
34
|
+
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
35
|
+
const anchor = selection.anchor;
|
|
36
|
+
|
|
37
|
+
if (anchor.key === key) {
|
|
38
|
+
cursorOffset = anchor.offset;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const diff = simpleDiffWithCursor(prevText, nextText, cursorOffset);
|
|
43
|
+
collabNode.spliceText(diff.index, diff.remove, diff.insert);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class CollabTextNode {
|
|
47
|
+
_map: YMap<unknown>;
|
|
48
|
+
_key: NodeKey;
|
|
49
|
+
_parent: CollabElementNode;
|
|
50
|
+
_text: string;
|
|
51
|
+
_type: string;
|
|
52
|
+
_normalized: boolean;
|
|
53
|
+
|
|
54
|
+
constructor(
|
|
55
|
+
map: YMap<unknown>,
|
|
56
|
+
text: string,
|
|
57
|
+
parent: CollabElementNode,
|
|
58
|
+
type: string,
|
|
59
|
+
) {
|
|
60
|
+
this._key = '';
|
|
61
|
+
this._map = map;
|
|
62
|
+
this._parent = parent;
|
|
63
|
+
this._text = text;
|
|
64
|
+
this._type = type;
|
|
65
|
+
this._normalized = false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getPrevNode(nodeMap: null | NodeMap): null | TextNode {
|
|
69
|
+
if (nodeMap === null) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const node = nodeMap.get(this._key);
|
|
74
|
+
return $isTextNode(node) ? node : null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getNode(): null | TextNode {
|
|
78
|
+
const node = $getNodeByKey(this._key);
|
|
79
|
+
return $isTextNode(node) ? node : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getSharedType(): YMap<unknown> {
|
|
83
|
+
return this._map;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getType(): string {
|
|
87
|
+
return this._type;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getKey(): NodeKey {
|
|
91
|
+
return this._key;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getSize(): number {
|
|
95
|
+
return this._text.length + (this._normalized ? 0 : 1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getOffset(): number {
|
|
99
|
+
const collabElementNode = this._parent;
|
|
100
|
+
return collabElementNode.getChildOffset(this);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
spliceText(index: number, delCount: number, newText: string): void {
|
|
104
|
+
const collabElementNode = this._parent;
|
|
105
|
+
const xmlText = collabElementNode._xmlText;
|
|
106
|
+
const offset = this.getOffset() + 1 + index;
|
|
107
|
+
|
|
108
|
+
if (delCount !== 0) {
|
|
109
|
+
xmlText.delete(offset, delCount);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (newText !== '') {
|
|
113
|
+
xmlText.insert(offset, newText);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
syncPropertiesAndTextFromLexical(
|
|
118
|
+
binding: Binding,
|
|
119
|
+
nextLexicalNode: TextNode,
|
|
120
|
+
prevNodeMap: null | NodeMap,
|
|
121
|
+
): void {
|
|
122
|
+
const prevLexicalNode = this.getPrevNode(prevNodeMap);
|
|
123
|
+
const nextText = nextLexicalNode.__text;
|
|
124
|
+
|
|
125
|
+
syncPropertiesFromLexical(
|
|
126
|
+
binding,
|
|
127
|
+
this._map,
|
|
128
|
+
prevLexicalNode,
|
|
129
|
+
nextLexicalNode,
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (prevLexicalNode !== null) {
|
|
133
|
+
const prevText = prevLexicalNode.__text;
|
|
134
|
+
|
|
135
|
+
if (prevText !== nextText) {
|
|
136
|
+
const key = nextLexicalNode.__key;
|
|
137
|
+
$diffTextContentAndApplyDelta(this, key, prevText, nextText);
|
|
138
|
+
this._text = nextText;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
syncPropertiesAndTextFromYjs(
|
|
144
|
+
binding: Binding,
|
|
145
|
+
keysChanged: null | Set<string>,
|
|
146
|
+
): void {
|
|
147
|
+
const lexicalNode = this.getNode();
|
|
148
|
+
invariant(
|
|
149
|
+
lexicalNode !== null,
|
|
150
|
+
'syncPropertiesAndTextFromYjs: could not find decorator node',
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
$syncPropertiesFromYjs(binding, this._map, lexicalNode, keysChanged);
|
|
154
|
+
|
|
155
|
+
const collabText = this._text;
|
|
156
|
+
|
|
157
|
+
if (lexicalNode.__text !== collabText) {
|
|
158
|
+
lexicalNode.setTextContent(collabText);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
destroy(binding: Binding): void {
|
|
163
|
+
const collabNodeMap = binding.collabNodeMap;
|
|
164
|
+
if (collabNodeMap.get(this._key) === this) {
|
|
165
|
+
collabNodeMap.delete(this._key);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function $createCollabTextNode(
|
|
171
|
+
map: YMap<unknown>,
|
|
172
|
+
text: string,
|
|
173
|
+
parent: CollabElementNode,
|
|
174
|
+
type: string,
|
|
175
|
+
): CollabTextNode {
|
|
176
|
+
const collabNode = new CollabTextNode(map, text, parent, type);
|
|
177
|
+
map._collabNode = collabNode;
|
|
178
|
+
return collabNode;
|
|
179
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import invariant from '@lexical/internal/invariant';
|
|
10
|
+
import {$isTextNode, type LexicalNode, NodeKey, type TextNode} from 'lexical';
|
|
11
|
+
import {XmlElement, XmlText} from 'yjs';
|
|
12
|
+
|
|
13
|
+
type SharedType = XmlElement | XmlText;
|
|
14
|
+
|
|
15
|
+
// Stores mappings between Yjs shared types and the Lexical nodes they were last associated with.
|
|
16
|
+
export class CollabV2Mapping {
|
|
17
|
+
private _nodeMap: Map<NodeKey, LexicalNode> = new Map();
|
|
18
|
+
|
|
19
|
+
private _sharedTypeToNodeKeys: Map<SharedType, NodeKey[]> = new Map();
|
|
20
|
+
private _nodeKeyToSharedType: Map<NodeKey, SharedType> = new Map();
|
|
21
|
+
|
|
22
|
+
set(sharedType: SharedType, node: LexicalNode | TextNode[]): void {
|
|
23
|
+
const isArray = node instanceof Array;
|
|
24
|
+
|
|
25
|
+
// Clear all existing associations for this key.
|
|
26
|
+
this.delete(sharedType);
|
|
27
|
+
|
|
28
|
+
// If nodes were associated with other shared types, remove those associations.
|
|
29
|
+
const nodes = isArray ? node : [node];
|
|
30
|
+
for (const n of nodes) {
|
|
31
|
+
const key = n.getKey();
|
|
32
|
+
if (this._nodeKeyToSharedType.has(key)) {
|
|
33
|
+
const otherSharedType = this._nodeKeyToSharedType.get(key)!;
|
|
34
|
+
const keyIndex = this._sharedTypeToNodeKeys
|
|
35
|
+
.get(otherSharedType)!
|
|
36
|
+
.indexOf(key);
|
|
37
|
+
if (keyIndex !== -1) {
|
|
38
|
+
this._sharedTypeToNodeKeys.get(otherSharedType)!.splice(keyIndex, 1);
|
|
39
|
+
}
|
|
40
|
+
this._nodeKeyToSharedType.delete(key);
|
|
41
|
+
this._nodeMap.delete(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (sharedType instanceof XmlText) {
|
|
46
|
+
invariant(isArray, 'Text nodes must be mapped as an array');
|
|
47
|
+
if (node.length === 0) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
this._sharedTypeToNodeKeys.set(
|
|
51
|
+
sharedType,
|
|
52
|
+
node.map(n => n.getKey()),
|
|
53
|
+
);
|
|
54
|
+
for (const n of node) {
|
|
55
|
+
this._nodeMap.set(n.getKey(), n);
|
|
56
|
+
this._nodeKeyToSharedType.set(n.getKey(), sharedType);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
invariant(!isArray, 'Element nodes must be mapped as a single node');
|
|
60
|
+
invariant(!$isTextNode(node), 'Text nodes must be mapped to XmlText');
|
|
61
|
+
this._sharedTypeToNodeKeys.set(sharedType, [node.getKey()]);
|
|
62
|
+
this._nodeMap.set(node.getKey(), node);
|
|
63
|
+
this._nodeKeyToSharedType.set(node.getKey(), sharedType);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get(sharedType: XmlElement): LexicalNode | undefined;
|
|
68
|
+
get(sharedType: XmlText): TextNode[] | undefined;
|
|
69
|
+
get(sharedType: SharedType): LexicalNode | Array<TextNode> | undefined;
|
|
70
|
+
get(sharedType: SharedType): LexicalNode | Array<TextNode> | undefined {
|
|
71
|
+
const nodes = this._sharedTypeToNodeKeys.get(sharedType);
|
|
72
|
+
if (nodes === undefined) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
if (sharedType instanceof XmlText) {
|
|
76
|
+
const arr = Array.from(
|
|
77
|
+
nodes.map(nodeKey => this._nodeMap.get(nodeKey)!),
|
|
78
|
+
) as Array<TextNode>;
|
|
79
|
+
return arr.length > 0 ? arr : undefined;
|
|
80
|
+
}
|
|
81
|
+
return this._nodeMap.get(nodes[0])!;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getSharedType(node: LexicalNode): SharedType | undefined {
|
|
85
|
+
return this._nodeKeyToSharedType.get(node.getKey());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
delete(sharedType: SharedType): void {
|
|
89
|
+
const nodeKeys = this._sharedTypeToNodeKeys.get(sharedType);
|
|
90
|
+
if (nodeKeys === undefined) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const nodeKey of nodeKeys) {
|
|
94
|
+
this._nodeMap.delete(nodeKey);
|
|
95
|
+
this._nodeKeyToSharedType.delete(nodeKey);
|
|
96
|
+
}
|
|
97
|
+
this._sharedTypeToNodeKeys.delete(sharedType);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
deleteNode(nodeKey: NodeKey): void {
|
|
101
|
+
const sharedType = this._nodeKeyToSharedType.get(nodeKey);
|
|
102
|
+
if (sharedType) {
|
|
103
|
+
this.delete(sharedType);
|
|
104
|
+
}
|
|
105
|
+
this._nodeMap.delete(nodeKey);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
has(sharedType: SharedType): boolean {
|
|
109
|
+
return this._sharedTypeToNodeKeys.has(sharedType);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
clear(): void {
|
|
113
|
+
this._nodeMap.clear();
|
|
114
|
+
this._sharedTypeToNodeKeys.clear();
|
|
115
|
+
this._nodeKeyToSharedType.clear();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import invariant from '@lexical/internal/invariant';
|
|
9
|
+
import {$getRoot, $getState, createState, LexicalNode} from 'lexical';
|
|
10
|
+
import {
|
|
11
|
+
emptySnapshot,
|
|
12
|
+
ID,
|
|
13
|
+
iterateDeletedStructs,
|
|
14
|
+
PermanentUserData,
|
|
15
|
+
Snapshot,
|
|
16
|
+
snapshot as createSnapshot,
|
|
17
|
+
} from 'yjs';
|
|
18
|
+
|
|
19
|
+
import {BindingV2} from './Bindings';
|
|
20
|
+
import {$createOrUpdateNodeFromYElement} from './SyncV2';
|
|
21
|
+
|
|
22
|
+
const STATE_KEY = 'ychange';
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
export type YChange<UserT = any> = {
|
|
26
|
+
id: ID;
|
|
27
|
+
type: 'removed' | 'added';
|
|
28
|
+
user: UserT | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const ychangeState = createState<typeof STATE_KEY, YChange | null>(STATE_KEY, {
|
|
32
|
+
isEqual: (a, b) => a === b,
|
|
33
|
+
parse: value => (value as YChange) ?? null,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export function $getYChangeState<UserT = unknown>(
|
|
37
|
+
node: LexicalNode,
|
|
38
|
+
): YChange<UserT> | null {
|
|
39
|
+
return $getState(node, ychangeState);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Not exposing $setState because it should only be created by SyncV2.ts.
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Replaces the editor content with a view that compares the state between two given snapshots.
|
|
46
|
+
* Any added or removed nodes between the two snapshots will have {@link YChange} attached to them.
|
|
47
|
+
*
|
|
48
|
+
* @param binding Yjs binding
|
|
49
|
+
* @param snapshot Ending snapshot state (default: current state of the Yjs document)
|
|
50
|
+
* @param prevSnapshot Starting snapshot state (default: empty snapshot)
|
|
51
|
+
*/
|
|
52
|
+
export const renderSnapshot__EXPERIMENTAL = (
|
|
53
|
+
binding: BindingV2,
|
|
54
|
+
snapshot: Snapshot = createSnapshot(binding.doc),
|
|
55
|
+
prevSnapshot: Snapshot = emptySnapshot,
|
|
56
|
+
) => {
|
|
57
|
+
// The document that contains the full history of this document.
|
|
58
|
+
const {doc} = binding;
|
|
59
|
+
invariant(!doc.gc, 'GC must be disabled to render snapshot');
|
|
60
|
+
|
|
61
|
+
doc.transact(transaction => {
|
|
62
|
+
// Before rendering, we are going to sanitize ops and split deleted ops
|
|
63
|
+
// if they were deleted by separate users.
|
|
64
|
+
const pud = new PermanentUserData(doc);
|
|
65
|
+
if (pud) {
|
|
66
|
+
pud.dss.forEach(ds => {
|
|
67
|
+
iterateDeletedStructs(transaction, ds, _item => {});
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const computeYChange = (type: 'removed' | 'added', id: ID): YChange => {
|
|
72
|
+
const user =
|
|
73
|
+
type === 'added'
|
|
74
|
+
? pud.getUserByClientId(id.client)
|
|
75
|
+
: pud.getUserByDeletedId(id);
|
|
76
|
+
return {
|
|
77
|
+
id,
|
|
78
|
+
type,
|
|
79
|
+
user: user ?? null,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
binding.mapping.clear();
|
|
84
|
+
binding.editor.update(() => {
|
|
85
|
+
$getRoot().clear();
|
|
86
|
+
$createOrUpdateNodeFromYElement(
|
|
87
|
+
binding.root,
|
|
88
|
+
binding,
|
|
89
|
+
null,
|
|
90
|
+
true,
|
|
91
|
+
snapshot,
|
|
92
|
+
prevSnapshot,
|
|
93
|
+
computeYChange,
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
}, binding);
|
|
97
|
+
};
|