@lexical/yjs 0.1.6 → 0.1.10

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.
@@ -0,0 +1,2487 @@
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
+ 'use strict';
8
+
9
+ var yjs = require('yjs');
10
+ var lexical = require('lexical');
11
+
12
+ /**
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ *
18
+ *
19
+ */
20
+ class CollabLineBreakNode {
21
+ constructor(map, parent) {
22
+ this._key = '';
23
+ this._map = map;
24
+ this._parent = parent;
25
+ this._type = 'linebreak';
26
+ }
27
+
28
+ getNode() {
29
+ const node = lexical.$getNodeByKey(this._key);
30
+ return lexical.$isLineBreakNode(node) ? node : null;
31
+ }
32
+
33
+ getKey() {
34
+ return this._key;
35
+ }
36
+
37
+ getSharedType() {
38
+ return this._map;
39
+ }
40
+
41
+ getType() {
42
+ return this._type;
43
+ }
44
+
45
+ getSize() {
46
+ return 1;
47
+ }
48
+
49
+ getOffset() {
50
+ const collabElementNode = this._parent;
51
+ return collabElementNode.getChildOffset(this);
52
+ }
53
+
54
+ destroy(binding) {
55
+ const collabNodeMap = binding.collabNodeMap;
56
+ collabNodeMap.delete(this._key);
57
+ }
58
+
59
+ }
60
+ function $createCollabLineBreakNode(map, parent) {
61
+ const collabNode = new CollabLineBreakNode(map, parent); // $FlowFixMe: internal field
62
+
63
+ map._collabNode = collabNode;
64
+ return collabNode;
65
+ }
66
+
67
+ /**
68
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
69
+ *
70
+ * This source code is licensed under the MIT license found in the
71
+ * LICENSE file in the root directory of this source tree.
72
+ *
73
+ *
74
+ */
75
+
76
+ function simpleDiffWithCursor(a, b, cursor) {
77
+ const aLength = a.length;
78
+ const bLength = b.length;
79
+ let left = 0; // number of same characters counting from left
80
+
81
+ let right = 0; // number of same characters counting from right
82
+ // Iterate left to the right until we find a changed character
83
+ // First iteration considers the current cursor position
84
+
85
+ while (left < aLength && left < bLength && a[left] === b[left] && left < cursor) {
86
+ left++;
87
+ } // Iterate right to the left until we find a changed character
88
+
89
+
90
+ while (right + left < aLength && right + left < bLength && a[aLength - right - 1] === b[bLength - right - 1]) {
91
+ right++;
92
+ } // Try to iterate left further to the right without caring about the current cursor position
93
+
94
+
95
+ while (right + left < aLength && right + left < bLength && a[left] === b[left]) {
96
+ left++;
97
+ }
98
+
99
+ return {
100
+ index: left,
101
+ insert: b.slice(left, bLength - right),
102
+ remove: aLength - left - right
103
+ };
104
+ }
105
+
106
+ function diffTextContentAndApplyDelta(collabNode, key, prevText, nextText) {
107
+ const selection = lexical.$getSelection();
108
+ let cursorOffset = nextText.length;
109
+
110
+ if (lexical.$isRangeSelection(selection) && selection.isCollapsed()) {
111
+ const anchor = selection.anchor;
112
+
113
+ if (anchor.key === key) {
114
+ cursorOffset = anchor.offset;
115
+ }
116
+ }
117
+
118
+ const diff = simpleDiffWithCursor(prevText, nextText, cursorOffset);
119
+ collabNode.spliceText(diff.index, diff.remove, diff.insert);
120
+ }
121
+
122
+ class CollabTextNode {
123
+ constructor(map, text, parent, type) {
124
+ this._key = '';
125
+ this._map = map;
126
+ this._parent = parent;
127
+ this._text = text;
128
+ this._type = type;
129
+ this._normalized = false;
130
+ }
131
+
132
+ getPrevNode(nodeMap) {
133
+ if (nodeMap === null) {
134
+ return null;
135
+ }
136
+
137
+ const node = nodeMap.get(this._key);
138
+ return lexical.$isTextNode(node) ? node : null;
139
+ }
140
+
141
+ getNode() {
142
+ const node = lexical.$getNodeByKey(this._key);
143
+ return lexical.$isTextNode(node) ? node : null;
144
+ }
145
+
146
+ getSharedType() {
147
+ return this._map;
148
+ }
149
+
150
+ getType() {
151
+ return this._type;
152
+ }
153
+
154
+ getKey() {
155
+ return this._key;
156
+ }
157
+
158
+ getSize() {
159
+ return this._text.length + (this._normalized ? 0 : 1);
160
+ }
161
+
162
+ getOffset() {
163
+ const collabElementNode = this._parent;
164
+ return collabElementNode.getChildOffset(this);
165
+ }
166
+
167
+ spliceText(index, delCount, newText) {
168
+ const collabElementNode = this._parent;
169
+ const xmlText = collabElementNode._xmlText;
170
+ const offset = this.getOffset() + 1 + index;
171
+
172
+ if (delCount !== 0) {
173
+ xmlText.delete(offset, delCount);
174
+ }
175
+
176
+ if (newText !== '') {
177
+ xmlText.insert(offset, newText);
178
+ }
179
+ }
180
+
181
+ syncPropertiesAndTextFromLexical(binding, nextLexicalNode, prevNodeMap) {
182
+ const prevLexicalNode = this.getPrevNode(prevNodeMap);
183
+ const nextText = nextLexicalNode.__text;
184
+ syncPropertiesFromLexical(binding, this._map, prevLexicalNode, nextLexicalNode);
185
+
186
+ if (prevLexicalNode !== null) {
187
+ const prevText = prevLexicalNode.__text;
188
+
189
+ if (prevText !== nextText) {
190
+ const key = nextLexicalNode.__key;
191
+ diffTextContentAndApplyDelta(this, key, prevText, nextText);
192
+ this._text = nextText;
193
+ }
194
+ }
195
+ }
196
+
197
+ syncPropertiesAndTextFromYjs(binding, keysChanged) {
198
+ const lexicalNode = this.getNode();
199
+
200
+ if (lexicalNode === null) {
201
+ throw new Error('Should never happen');
202
+ }
203
+
204
+ syncPropertiesFromYjs(binding, this._map, lexicalNode, keysChanged);
205
+ const collabText = this._text;
206
+
207
+ if (lexicalNode.__text !== collabText) {
208
+ const writable = lexicalNode.getWritable();
209
+ writable.__text = collabText;
210
+ }
211
+ }
212
+
213
+ destroy(binding) {
214
+ const collabNodeMap = binding.collabNodeMap;
215
+ collabNodeMap.delete(this._key);
216
+ }
217
+
218
+ }
219
+ function $createCollabTextNode(map, text, parent, type) {
220
+ const collabNode = new CollabTextNode(map, text, parent, type); // $FlowFixMe: internal field
221
+
222
+ map._collabNode = collabNode;
223
+ return collabNode;
224
+ }
225
+
226
+ /**
227
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
228
+ *
229
+ * This source code is licensed under the MIT license found in the
230
+ * LICENSE file in the root directory of this source tree.
231
+ *
232
+ *
233
+ */
234
+ const excludedProperties = new Set(['__key', '__children', '__parent', '__cachedText', '__text', '__state']);
235
+ function $getNodeByKeyOrThrow(key) {
236
+ const node = lexical.$getNodeByKey(key);
237
+
238
+ if (node === null) {
239
+ throw new Error('Should never happen');
240
+ }
241
+
242
+ return node;
243
+ }
244
+ function $createCollabNodeFromLexicalNode(binding, lexicalNode, parent) {
245
+ const nodeType = lexicalNode.__type;
246
+ let collabNode;
247
+
248
+ if (lexical.$isElementNode(lexicalNode)) {
249
+ const xmlText = new yjs.XmlText();
250
+ collabNode = $createCollabElementNode(xmlText, parent, nodeType);
251
+ collabNode.syncPropertiesFromLexical(binding, lexicalNode, null);
252
+ collabNode.syncChildrenFromLexical(binding, lexicalNode, null, null, null);
253
+ } else if (lexical.$isTextNode(lexicalNode)) {
254
+ // TODO create a token text node for immutable, segmented or inert nodes.
255
+ const map = new yjs.Map();
256
+ collabNode = $createCollabTextNode(map, lexicalNode.__text, parent, nodeType);
257
+ collabNode.syncPropertiesAndTextFromLexical(binding, lexicalNode, null);
258
+ } else if (lexical.$isLineBreakNode(lexicalNode)) {
259
+ const map = new yjs.Map();
260
+ map.set('__type', 'linebreak');
261
+ collabNode = $createCollabLineBreakNode(map, parent);
262
+ } else if (lexical.$isDecoratorNode(lexicalNode)) {
263
+ const xmlElem = new yjs.XmlElement();
264
+ collabNode = $createCollabDecoratorNode(xmlElem, parent, nodeType);
265
+ collabNode.syncPropertiesFromLexical(binding, lexicalNode, null);
266
+ } else {
267
+ throw new Error('Should never happen');
268
+ }
269
+
270
+ collabNode._key = lexicalNode.__key;
271
+ return collabNode;
272
+ }
273
+
274
+ function getNodeTypeFromSharedType(sharedType) {
275
+ const type = sharedType instanceof yjs.Map ? sharedType.get('__type') : sharedType.getAttribute('__type');
276
+
277
+ if (type == null) {
278
+ throw new Error('Should never happen');
279
+ }
280
+
281
+ return type;
282
+ }
283
+
284
+ function getOrInitCollabNodeFromSharedType(binding, sharedType, parent) {
285
+ // $FlowFixMe: internal field
286
+ const collabNode = sharedType._collabNode;
287
+
288
+ if (collabNode === undefined) {
289
+ const registeredNodes = binding.editor._nodes;
290
+ const type = getNodeTypeFromSharedType(sharedType);
291
+ const nodeInfo = registeredNodes.get(type);
292
+
293
+ if (nodeInfo === undefined) {
294
+ throw new Error('Should never happen');
295
+ }
296
+
297
+ const sharedParent = sharedType.parent;
298
+ const targetParent = parent === undefined && sharedParent !== null ? getOrInitCollabNodeFromSharedType(binding, sharedParent) : parent || null;
299
+
300
+ if (!(targetParent instanceof CollabElementNode)) {
301
+ throw new Error('Should never happen');
302
+ }
303
+
304
+ if (sharedType instanceof yjs.XmlText) {
305
+ return $createCollabElementNode(sharedType, targetParent, type);
306
+ } else if (sharedType instanceof yjs.Map) {
307
+ if (targetParent === null) {
308
+ throw new Error('Should never happen');
309
+ }
310
+
311
+ if (type === 'linebreak') {
312
+ return $createCollabLineBreakNode(sharedType, targetParent);
313
+ }
314
+
315
+ return $createCollabTextNode(sharedType, '', targetParent, type);
316
+ } else if (sharedType instanceof yjs.XmlElement) {
317
+ return $createCollabDecoratorNode(sharedType, targetParent, type);
318
+ }
319
+ }
320
+
321
+ return collabNode;
322
+ }
323
+ function createLexicalNodeFromCollabNode(binding, collabNode, parentKey) {
324
+ const type = collabNode.getType();
325
+ const registeredNodes = binding.editor._nodes;
326
+ const nodeInfo = registeredNodes.get(type);
327
+
328
+ if (nodeInfo === undefined) {
329
+ throw new Error('createLexicalNode failed');
330
+ } // $FlowFixMe: needs refining
331
+
332
+
333
+ const lexicalNode = new nodeInfo.klass();
334
+ lexicalNode.__parent = parentKey;
335
+ collabNode._key = lexicalNode.__key;
336
+
337
+ if (collabNode instanceof CollabElementNode) {
338
+ const xmlText = collabNode._xmlText;
339
+ collabNode.syncPropertiesFromYjs(binding, null);
340
+ collabNode.applyChildrenYjsDelta(binding, xmlText.toDelta());
341
+ collabNode.syncChildrenFromYjs(binding);
342
+ } else if (collabNode instanceof CollabTextNode) {
343
+ collabNode.syncPropertiesAndTextFromYjs(binding, null);
344
+ } else if (collabNode instanceof CollabDecoratorNode) {
345
+ collabNode.syncPropertiesFromYjs(binding, null);
346
+ }
347
+
348
+ binding.collabNodeMap.set(lexicalNode.__key, collabNode);
349
+ return lexicalNode;
350
+ }
351
+ function syncPropertiesFromYjs(binding, sharedType, lexicalNode, keysChanged) {
352
+ const properties = keysChanged === null ? sharedType instanceof yjs.Map ? Array.from(sharedType.keys()) : Object.keys(sharedType.getAttributes()) : Array.from(keysChanged);
353
+ let writableNode;
354
+
355
+ for (let i = 0; i < properties.length; i++) {
356
+ const property = properties[i];
357
+
358
+ if (excludedProperties.has(property)) {
359
+ continue;
360
+ } // $FlowFixMe: intentional
361
+
362
+
363
+ const prevValue = lexicalNode[property];
364
+ const nextValue = sharedType instanceof yjs.Map ? sharedType.get(property) : sharedType.getAttribute(property);
365
+
366
+ if (prevValue !== nextValue) {
367
+ if (writableNode === undefined) {
368
+ writableNode = lexicalNode.getWritable();
369
+ } // $FlowFixMe
370
+
371
+
372
+ writableNode[property] = nextValue;
373
+ }
374
+ }
375
+ }
376
+ function syncPropertiesFromLexical(binding, sharedType, prevLexicalNode, nextLexicalNode) {
377
+ const type = nextLexicalNode.__type;
378
+ const nodeProperties = binding.nodeProperties;
379
+ let properties = nodeProperties.get(type);
380
+
381
+ if (properties === undefined) {
382
+ properties = Object.keys(nextLexicalNode).filter(property => {
383
+ return !excludedProperties.has(property);
384
+ });
385
+ nodeProperties.set(type, properties);
386
+ }
387
+
388
+ for (let i = 0; i < properties.length; i++) {
389
+ const property = properties[i];
390
+ const prevValue = // $FlowFixMe: intentional override
391
+ prevLexicalNode === null ? undefined : prevLexicalNode[property]; // $FlowFixMe: intentional override
392
+
393
+ const nextValue = nextLexicalNode[property];
394
+
395
+ if (prevValue !== nextValue) {
396
+ if (sharedType instanceof yjs.Map) {
397
+ sharedType.set(property, nextValue);
398
+ } else {
399
+ sharedType.setAttribute(property, nextValue);
400
+ }
401
+ }
402
+ }
403
+ }
404
+ function spliceString(str, index, delCount, newText) {
405
+ return str.slice(0, index) + newText + str.slice(index + delCount);
406
+ }
407
+ function getPositionFromElementAndOffset(node, offset, boundaryIsEdge) {
408
+ let index = 0;
409
+ let i = 0;
410
+ const children = node._children;
411
+ const childrenLength = children.length;
412
+
413
+ for (; i < childrenLength; i++) {
414
+ const child = children[i];
415
+ const childOffset = index;
416
+ const size = child.getSize();
417
+ index += size;
418
+ const exceedsBoundary = boundaryIsEdge ? index >= offset : index > offset;
419
+
420
+ if (exceedsBoundary && child instanceof CollabTextNode) {
421
+ let textOffset = offset - childOffset - 1;
422
+
423
+ if (textOffset < 0) {
424
+ textOffset = 0;
425
+ }
426
+
427
+ const diffLength = size - textOffset;
428
+ return {
429
+ length: diffLength,
430
+ node: child,
431
+ nodeIndex: i,
432
+ offset: textOffset
433
+ };
434
+ }
435
+
436
+ if (index > offset) {
437
+ return {
438
+ length: 0,
439
+ node: child,
440
+ nodeIndex: i,
441
+ offset: childOffset
442
+ };
443
+ } else if (i === childrenLength - 1) {
444
+ return {
445
+ length: 0,
446
+ node: null,
447
+ nodeIndex: i + 1,
448
+ offset: childOffset + 1
449
+ };
450
+ }
451
+ }
452
+
453
+ return {
454
+ length: 0,
455
+ node: null,
456
+ nodeIndex: 0,
457
+ offset: 0
458
+ };
459
+ }
460
+ function doesSelectionNeedRecovering(selection) {
461
+ const anchor = selection.anchor;
462
+ const focus = selection.focus;
463
+ let recoveryNeeded = false;
464
+
465
+ try {
466
+ const anchorNode = anchor.getNode();
467
+ const focusNode = focus.getNode();
468
+
469
+ if ( // We might have removed a node that no longer exists
470
+ !anchorNode.isAttached() || !focusNode.isAttached() || // If we've split a node, then the offset might not be right
471
+ lexical.$isTextNode(anchorNode) && anchor.offset > anchorNode.getTextContentSize() || lexical.$isTextNode(focusNode) && focus.offset > focusNode.getTextContentSize()) {
472
+ recoveryNeeded = true;
473
+ }
474
+ } catch (e) {
475
+ // Sometimes checking nor a node via getNode might trigger
476
+ // an error, so we need recovery then too.
477
+ recoveryNeeded = true;
478
+ }
479
+
480
+ return recoveryNeeded;
481
+ }
482
+ function syncWithTransaction(binding, fn) {
483
+ binding.doc.transact(fn, binding);
484
+ }
485
+
486
+ /**
487
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
488
+ *
489
+ * This source code is licensed under the MIT license found in the
490
+ * LICENSE file in the root directory of this source tree.
491
+ *
492
+ *
493
+ */
494
+ let isMutationFromCollab = false;
495
+ function mutationFromCollab(fn) {
496
+ const prevIsMutationFromCollab = isMutationFromCollab;
497
+
498
+ try {
499
+ isMutationFromCollab = true;
500
+ fn();
501
+ } finally {
502
+ isMutationFromCollab = prevIsMutationFromCollab;
503
+ }
504
+ }
505
+ function observeDecoratorMap(binding, collabNode, decoratorMap, yjsMap) {
506
+ const unobserve = decoratorMap.observe(changedKey => {
507
+ if (isMutationFromCollab) {
508
+ return;
509
+ }
510
+
511
+ syncWithTransaction(binding, () => syncLexicalDecoratorMapKeyToYjs(binding, collabNode, decoratorMap._map, yjsMap, changedKey));
512
+ });
513
+
514
+ collabNode._unobservers.add(unobserve);
515
+ }
516
+
517
+ function observeDecoratorArray(binding, collabNode, decoratorArray, yjsArray) {
518
+ const unobserve = decoratorArray.observe((changedIndex, delCount) => {
519
+ if (isMutationFromCollab) {
520
+ return;
521
+ }
522
+
523
+ syncWithTransaction(binding, () => {
524
+ if (delCount > 0) {
525
+ yjsArray.delete(changedIndex, delCount);
526
+ }
527
+
528
+ syncLexicalDecoratorArrayValueToYjs(binding, collabNode, decoratorArray._array, yjsArray, changedIndex);
529
+ });
530
+ });
531
+
532
+ collabNode._unobservers.add(unobserve);
533
+ }
534
+
535
+ function syncLexicalDecoratorMapKeyToYjs(binding, collabNode, internalMap, yjsMap, key) {
536
+ const lexicalValue = internalMap.get(key);
537
+ let yjsValue = yjsMap.get(key);
538
+
539
+ if (lexicalValue !== yjsValue) {
540
+ if (lexical.isDecoratorMap(lexicalValue)) {
541
+ if (yjsValue === undefined) {
542
+ yjsValue = new yjs.Map(); // $FlowFixMe: internal field
543
+
544
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
545
+
546
+ yjsValue._collabNode = collabNode;
547
+ yjsValue.set('type', 'map');
548
+ yjsMap.set(key, yjsValue);
549
+ observeDecoratorMap(binding, collabNode, lexicalValue, yjsValue);
550
+ }
551
+
552
+ syncLexicalDecoratorMapToYjs(binding, collabNode, lexicalValue, yjsValue);
553
+ } else if (lexical.isDecoratorEditor(lexicalValue)) {
554
+ let doc;
555
+
556
+ if (yjsValue === undefined) {
557
+ yjsValue = new yjs.Map(); // $FlowFixMe: internal field
558
+
559
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
560
+
561
+ yjsValue._collabNode = collabNode; // Create a subdocument
562
+
563
+ doc = new yjs.Doc();
564
+ yjsValue.set('doc', doc);
565
+ yjsValue.set('type', 'editor');
566
+ yjsMap.set(key, yjsValue);
567
+ }
568
+
569
+ doc = doc || yjsValue.get('doc');
570
+ const yjsId = yjsValue.get('id');
571
+ const lexicalId = lexicalValue.id;
572
+
573
+ if (yjsId !== lexicalId) {
574
+ const yjsDocMap = binding.docMap;
575
+ yjsDocMap.delete(yjsId);
576
+ yjsValue.set('id', lexicalId);
577
+ yjsDocMap.set(lexicalId, doc);
578
+ }
579
+ } else if (lexical.isDecoratorArray(lexicalValue)) {
580
+ if (yjsValue === undefined) {
581
+ yjsValue = new yjs.Array(); // $FlowFixMe: internal field
582
+
583
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
584
+
585
+ yjsValue._collabNode = collabNode;
586
+ yjsMap.set(key, yjsValue);
587
+ observeDecoratorArray(binding, collabNode, lexicalValue, yjsValue);
588
+ }
589
+
590
+ syncLexicalDecoratorArrayToYjs(binding, collabNode, lexicalValue, yjsValue);
591
+ } else {
592
+ if (lexical.isDecoratorArray(yjsValue) || lexical.isDecoratorMap(yjsValue)) {
593
+ yjsValue.destroy();
594
+ }
595
+
596
+ yjsMap.set(key, lexicalValue);
597
+ }
598
+ }
599
+ }
600
+
601
+ function syncLexicalDecoratorArrayValueToYjs(binding, collabNode, internalArray, yjsArray, index) {
602
+ const lexicalValue = internalArray[index];
603
+ let yjsValue = yjsArray.get(index);
604
+
605
+ if (lexicalValue !== yjsValue) {
606
+ if (lexical.isDecoratorMap(lexicalValue)) {
607
+ if (yjsValue === undefined) {
608
+ yjsValue = new yjs.Map(); // $FlowFixMe: internal field
609
+
610
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
611
+
612
+ yjsValue._collabNode = collabNode;
613
+ yjsValue.set('type', 'map');
614
+ yjsArray.insert(index, [yjsValue]);
615
+ observeDecoratorMap(binding, collabNode, lexicalValue, yjsValue);
616
+ }
617
+
618
+ syncLexicalDecoratorMapToYjs(binding, collabNode, lexicalValue, yjsValue);
619
+ } else if (lexical.isDecoratorEditor(lexicalValue)) {
620
+ let doc;
621
+
622
+ if (yjsValue === undefined) {
623
+ yjsValue = new yjs.Map(); // $FlowFixMe: internal field
624
+
625
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
626
+
627
+ yjsValue._collabNode = collabNode; // Create a subdocument
628
+
629
+ doc = new yjs.Doc();
630
+ yjsValue.set('doc', doc);
631
+ yjsValue.set('type', 'editor');
632
+ yjsArray.insert(index, [yjsValue]);
633
+ }
634
+
635
+ doc = doc || yjsValue.get('doc');
636
+ const yjsId = yjsValue.get('id');
637
+ const lexicalId = lexicalValue.id;
638
+
639
+ if (yjsId !== lexicalId) {
640
+ const yjsDocMap = binding.docMap;
641
+ yjsDocMap.delete(yjsId);
642
+ yjsValue.set('id', lexicalId);
643
+ yjsDocMap.set(lexicalId, doc);
644
+ }
645
+ } else if (lexical.isDecoratorArray(lexicalValue)) {
646
+ if (yjsValue === undefined) {
647
+ yjsValue = new yjs.Array(); // $FlowFixMe: internal field
648
+
649
+ yjsValue._lexicalValue = lexicalValue; // $FlowFixMe: internal field
650
+
651
+ yjsValue._collabNode = collabNode;
652
+ yjsArray.insert(index, [yjsValue]);
653
+ observeDecoratorArray(binding, collabNode, lexicalValue, yjsValue);
654
+ }
655
+
656
+ syncLexicalDecoratorArrayToYjs(binding, collabNode, lexicalValue, yjsValue);
657
+ } else {
658
+ yjsArray.insert(index, [lexicalValue]);
659
+ }
660
+ }
661
+ }
662
+
663
+ function syncLexicalDecoratorMapToYjs(binding, collabNode, decoratorMap, yjsMap) {
664
+ const internalMap = decoratorMap._map;
665
+ const keys = Array.from(internalMap.keys());
666
+
667
+ for (let i = 0; i < keys.length; i++) {
668
+ const key = keys[i];
669
+ syncLexicalDecoratorMapKeyToYjs(binding, collabNode, internalMap, yjsMap, key);
670
+ }
671
+ }
672
+
673
+ function syncLexicalDecoratorArrayToYjs(binding, collabNode, decoratorArray, yjsArray) {
674
+ const internalArray = decoratorArray._array;
675
+
676
+ for (let i = 0; i < internalArray.length; i++) {
677
+ syncLexicalDecoratorArrayValueToYjs(binding, collabNode, internalArray, yjsArray, i);
678
+ }
679
+ }
680
+
681
+ function syncYjsDecoratorMapKeyToLexical(binding, collabNode, yjsMap, decoratorMap, key) {
682
+ const lexicalValue = decoratorMap.get(key);
683
+ const yjsValue = yjsMap.get(key);
684
+
685
+ if (lexicalValue !== yjsValue) {
686
+ // $FlowFixMe: internal field
687
+ let nextValue = yjsValue._lexicalValue;
688
+
689
+ if (yjsValue instanceof yjs.Map) {
690
+ const type = yjsValue.get('type');
691
+
692
+ if (type === 'editor') {
693
+ if (nextValue === undefined) {
694
+ const yjsDocMap = binding.docMap;
695
+ const id = yjsValue.get('id');
696
+ const doc = yjsValue.get('doc');
697
+ nextValue = lexical.createDecoratorEditor(id);
698
+ yjsValue._lexicalValue = nextValue;
699
+ yjsValue._collabNode = collabNode;
700
+ yjsDocMap.set(id, doc);
701
+ mutationFromCollab(() => decoratorMap.set(key, nextValue));
702
+ }
703
+ } else if (type === 'map') {
704
+ if (nextValue === undefined) {
705
+ nextValue = lexical.createDecoratorMap(binding.editor);
706
+ observeDecoratorMap(binding, collabNode, nextValue, yjsValue);
707
+ yjsValue._lexicalValue = nextValue;
708
+ yjsValue._collabNode = collabNode;
709
+ mutationFromCollab(() => decoratorMap.set(key, nextValue));
710
+ }
711
+
712
+ syncYjsDecoratorMapToLexical(binding, collabNode, yjsValue, nextValue, null);
713
+ } else {
714
+ {
715
+ throw Error(`Should never happen`);
716
+ }
717
+ }
718
+ } else if (yjsValue instanceof yjs.Array) {
719
+ if (nextValue === undefined) {
720
+ nextValue = lexical.createDecoratorArray(binding.editor);
721
+ observeDecoratorArray(binding, collabNode, nextValue, yjsValue);
722
+ yjsValue._lexicalValue = nextValue;
723
+ yjsValue._collabNode = collabNode;
724
+ mutationFromCollab(() => decoratorMap.set(key, nextValue));
725
+ }
726
+
727
+ syncYjsDecoratorArrayToLexical(binding, collabNode, yjsValue, nextValue);
728
+ } else {
729
+ mutationFromCollab(() => decoratorMap.set(key, yjsValue));
730
+ }
731
+ }
732
+ }
733
+
734
+ function syncYjsDecoratorArrayValueToLexical(binding, collabNode, yjsArray, decoratorArray, index) {
735
+ const lexicalValue = decoratorArray._array[index];
736
+ const yjsValue = yjsArray.get(index);
737
+
738
+ if (lexicalValue !== yjsValue) {
739
+ // $FlowFixMe: internal field
740
+ let nextValue = yjsValue._lexicalValue;
741
+
742
+ if (yjsValue instanceof yjs.Map) {
743
+ const type = yjsValue.get('type');
744
+
745
+ if (type === 'editor') {
746
+ if (nextValue === undefined) {
747
+ const yjsDocMap = binding.docMap;
748
+ const id = yjsValue.get('id');
749
+ const doc = yjsValue.get('doc');
750
+ nextValue = lexical.createDecoratorEditor(id);
751
+ yjsValue._lexicalValue = nextValue;
752
+ yjsValue._collabNode = collabNode;
753
+ yjsDocMap.set(id, doc);
754
+ mutationFromCollab(() => decoratorArray.splice(index, 0, nextValue));
755
+ }
756
+ } else if (type === 'map') {
757
+ if (nextValue === undefined) {
758
+ nextValue = lexical.createDecoratorMap(binding.editor);
759
+ observeDecoratorMap(binding, collabNode, nextValue, yjsValue);
760
+ yjsValue._lexicalValue = nextValue;
761
+ yjsValue._collabNode = collabNode;
762
+ mutationFromCollab(() => decoratorArray.splice(index, 0, nextValue));
763
+ }
764
+
765
+ syncYjsDecoratorMapToLexical(binding, collabNode, yjsValue, nextValue, null);
766
+ } else {
767
+ {
768
+ throw Error(`Should never happen`);
769
+ }
770
+ }
771
+ } else if (yjsValue instanceof yjs.Array) {
772
+ if (nextValue === undefined) {
773
+ nextValue = lexical.createDecoratorArray(binding.editor);
774
+ observeDecoratorArray(binding, collabNode, nextValue, yjsValue);
775
+ yjsValue._lexicalValue = nextValue;
776
+ yjsValue._collabNode = collabNode;
777
+ mutationFromCollab(() => decoratorArray.splice(index, 0, nextValue));
778
+ }
779
+
780
+ syncYjsDecoratorArrayToLexical(binding, collabNode, yjsValue, nextValue);
781
+ } else {
782
+ mutationFromCollab(() => decoratorArray.splice(index, 0, yjsValue));
783
+ }
784
+ }
785
+ }
786
+ function syncYjsDecoratorArrayToLexical(binding, collabNode, yjsArray, decoratorArray) {
787
+ const length = Math.max(yjsArray.length, decoratorArray.getLength());
788
+
789
+ for (let i = 0; i < length; i++) {
790
+ syncYjsDecoratorArrayValueToLexical(binding, collabNode, yjsArray, decoratorArray, i);
791
+ }
792
+ }
793
+ function syncYjsDecoratorMapToLexical(binding, collabNode, yjsMap, decoratorMap, keysChanged) {
794
+ const keys = keysChanged === null ? Array.from(yjsMap.keys()) : Array.from(keysChanged);
795
+
796
+ for (let i = 0; i < keys.length; i++) {
797
+ const key = keys[i];
798
+ syncYjsDecoratorMapKeyToLexical(binding, collabNode, yjsMap, decoratorMap, key);
799
+ }
800
+ }
801
+
802
+ /**
803
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
804
+ *
805
+ * This source code is licensed under the MIT license found in the
806
+ * LICENSE file in the root directory of this source tree.
807
+ *
808
+ *
809
+ */
810
+ class CollabDecoratorNode {
811
+ constructor(xmlElem, parent, type) {
812
+ this._key = '';
813
+ this._xmlElem = xmlElem;
814
+ this._parent = parent;
815
+ this._type = type;
816
+ this._unobservers = new Set();
817
+ }
818
+
819
+ getPrevNode(nodeMap) {
820
+ if (nodeMap === null) {
821
+ return null;
822
+ }
823
+
824
+ const node = nodeMap.get(this._key);
825
+ return lexical.$isDecoratorNode(node) ? node : null;
826
+ }
827
+
828
+ getNode() {
829
+ const node = lexical.$getNodeByKey(this._key);
830
+ return lexical.$isDecoratorNode(node) ? node : null;
831
+ }
832
+
833
+ getSharedType() {
834
+ return this._xmlElem;
835
+ }
836
+
837
+ getType() {
838
+ return this._type;
839
+ }
840
+
841
+ getKey() {
842
+ return this._key;
843
+ }
844
+
845
+ getSize() {
846
+ return 1;
847
+ }
848
+
849
+ getOffset() {
850
+ const collabElementNode = this._parent;
851
+ return collabElementNode.getChildOffset(this);
852
+ }
853
+
854
+ syncPropertiesFromLexical(binding, nextLexicalNode, prevNodeMap) {
855
+ const prevLexicalNode = this.getPrevNode(prevNodeMap);
856
+ const xmlElem = this._xmlElem;
857
+ const prevDecoratorMap = prevLexicalNode === null ? null : prevLexicalNode.__state;
858
+ const nextDecoratorMap = nextLexicalNode.__state;
859
+ syncPropertiesFromLexical(binding, xmlElem, prevLexicalNode, nextLexicalNode); // Handle bindings
860
+
861
+ if (prevDecoratorMap !== nextDecoratorMap) {
862
+ const yjsMap = new yjs.Map();
863
+ xmlElem.insert(0, [yjsMap]); // $FlowFixMe: internal field
864
+
865
+ yjsMap._lexicalValue = nextDecoratorMap; // $FlowFixMe: internal field
866
+
867
+ yjsMap._collabNode = this;
868
+ syncLexicalDecoratorMapToYjs(binding, this, nextDecoratorMap, yjsMap);
869
+ observeDecoratorMap(binding, this, nextDecoratorMap, yjsMap);
870
+ }
871
+ }
872
+
873
+ syncPropertiesFromYjs(binding, keysChanged) {
874
+ const lexicalNode = this.getNode();
875
+
876
+ if (lexicalNode === null) {
877
+ throw new Error('Should never happen');
878
+ }
879
+
880
+ const xmlElem = this._xmlElem; // $FlowFixMe: should always be true
881
+
882
+ const yjsMap = xmlElem.firstChild;
883
+ const decoratorMap = lexicalNode.__state; // $FlowFixMe: internal field
884
+
885
+ yjsMap._lexicalValue = decoratorMap; // $FlowFixMe: internal field
886
+
887
+ yjsMap._collabNode = this;
888
+ syncPropertiesFromYjs(binding, xmlElem, lexicalNode, keysChanged);
889
+ syncYjsDecoratorMapToLexical(binding, this, yjsMap, decoratorMap, null);
890
+ observeDecoratorMap(binding, this, decoratorMap, yjsMap);
891
+ }
892
+
893
+ destroy(binding) {
894
+ const collabNodeMap = binding.collabNodeMap;
895
+ collabNodeMap.delete(this._key);
896
+
897
+ this._unobservers.forEach(unobserver => unobserver());
898
+
899
+ this._unobservers.clear();
900
+ }
901
+
902
+ }
903
+ function $createCollabDecoratorNode(xmlElem, parent, type) {
904
+ const collabNode = new CollabDecoratorNode(xmlElem, parent, type); // $FlowFixMe: internal field
905
+
906
+ xmlElem._collabNode = collabNode;
907
+ return collabNode;
908
+ }
909
+
910
+ /**
911
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
912
+ *
913
+ * This source code is licensed under the MIT license found in the
914
+ * LICENSE file in the root directory of this source tree.
915
+ *
916
+ *
917
+ */
918
+ class CollabElementNode {
919
+ constructor(xmlText, parent, type) {
920
+ this._key = '';
921
+ this._children = [];
922
+ this._xmlText = xmlText;
923
+ this._type = type;
924
+ this._parent = parent;
925
+ }
926
+
927
+ getPrevNode(nodeMap) {
928
+ if (nodeMap === null) {
929
+ return null;
930
+ }
931
+
932
+ const node = nodeMap.get(this._key);
933
+ return lexical.$isElementNode(node) ? node : null;
934
+ }
935
+
936
+ getNode() {
937
+ const node = lexical.$getNodeByKey(this._key);
938
+ return lexical.$isElementNode(node) ? node : null;
939
+ }
940
+
941
+ getSharedType() {
942
+ return this._xmlText;
943
+ }
944
+
945
+ getType() {
946
+ return this._type;
947
+ }
948
+
949
+ getKey() {
950
+ return this._key;
951
+ }
952
+
953
+ isEmpty() {
954
+ return this._children.length === 0;
955
+ }
956
+
957
+ getSize() {
958
+ return 1;
959
+ }
960
+
961
+ getOffset() {
962
+ const collabElementNode = this._parent;
963
+
964
+ if (collabElementNode === null) {
965
+ throw new Error('Should never happen');
966
+ }
967
+
968
+ return collabElementNode.getChildOffset(this);
969
+ }
970
+
971
+ syncPropertiesFromYjs(binding, keysChanged) {
972
+ const lexicalNode = this.getNode();
973
+
974
+ if (lexicalNode === null) {
975
+ this.getNode();
976
+ throw new Error('Should never happen');
977
+ }
978
+
979
+ syncPropertiesFromYjs(binding, this._xmlText, lexicalNode, keysChanged);
980
+ }
981
+
982
+ applyChildrenYjsDelta(binding, deltas) {
983
+ const children = this._children;
984
+ let currIndex = 0;
985
+
986
+ for (let i = 0; i < deltas.length; i++) {
987
+ const delta = deltas[i];
988
+ const insertDelta = delta.insert;
989
+ const deleteDelta = delta.delete;
990
+
991
+ if (delta.retain != null) {
992
+ currIndex += delta.retain;
993
+ } else if (typeof deleteDelta === 'number') {
994
+ let deletionSize = deleteDelta;
995
+
996
+ while (deletionSize > 0) {
997
+ const {
998
+ node,
999
+ nodeIndex,
1000
+ offset,
1001
+ length
1002
+ } = getPositionFromElementAndOffset(this, currIndex, false);
1003
+
1004
+ if (node instanceof CollabElementNode || node instanceof CollabLineBreakNode || node instanceof CollabDecoratorNode) {
1005
+ children.splice(nodeIndex, 1);
1006
+ deletionSize -= 1;
1007
+ } else if (node instanceof CollabTextNode) {
1008
+ const delCount = Math.min(deletionSize, length);
1009
+ const prevCollabNode = nodeIndex !== 0 ? children[nodeIndex - 1] : null;
1010
+ const nodeSize = node.getSize();
1011
+
1012
+ if (offset === 0 && delCount === 1 && nodeIndex > 0 && prevCollabNode instanceof CollabTextNode && length === nodeSize && // If the node has no keys, it's been deleted
1013
+ Array.from(node._map.keys()).length === 0) {
1014
+ // Merge the text node with previous.
1015
+ prevCollabNode._text += node._text;
1016
+ children.splice(nodeIndex, 1);
1017
+ } else if (offset === 0 && delCount === nodeSize) {
1018
+ // The entire thing needs removing
1019
+ children.splice(nodeIndex, 1);
1020
+ } else {
1021
+ node._text = spliceString(node._text, offset, delCount, '');
1022
+ }
1023
+
1024
+ deletionSize -= delCount;
1025
+ } else {
1026
+ // Can occur due to the deletion from the dangling text heuristic below.
1027
+ break;
1028
+ }
1029
+ }
1030
+ } else if (insertDelta != null) {
1031
+ if (typeof insertDelta === 'string') {
1032
+ const {
1033
+ node,
1034
+ offset
1035
+ } = getPositionFromElementAndOffset(this, currIndex, true);
1036
+
1037
+ if (node instanceof CollabTextNode) {
1038
+ node._text = spliceString(node._text, offset, 0, insertDelta);
1039
+ } else {
1040
+ // TODO: maybe we can improve this by keeping around a redundant
1041
+ // text node map, rather than removing all the text nodes, so there
1042
+ // never can be dangling text.
1043
+ // We have a conflict where there was likely a CollabTextNode and
1044
+ // an Lexical TextNode too, but they were removed in a merge. So
1045
+ // let's just ignore the text and trigger a removal for it from our
1046
+ // shared type.
1047
+ this._xmlText.delete(offset, insertDelta.length);
1048
+ }
1049
+
1050
+ currIndex += insertDelta.length;
1051
+ } else {
1052
+ const sharedType = insertDelta;
1053
+ const {
1054
+ nodeIndex
1055
+ } = getPositionFromElementAndOffset(this, currIndex, false);
1056
+ const collabNode = getOrInitCollabNodeFromSharedType(binding, sharedType, this);
1057
+ children.splice(nodeIndex, 0, collabNode);
1058
+ currIndex += 1;
1059
+ }
1060
+ } else {
1061
+ throw new Error('Unexpected delta format');
1062
+ }
1063
+ }
1064
+ }
1065
+
1066
+ syncChildrenFromYjs(binding) {
1067
+ // Now diff the children of the collab node with that of our existing Lexical node.
1068
+ const lexicalNode = this.getNode();
1069
+
1070
+ if (lexicalNode === null) {
1071
+ this.getNode();
1072
+ throw new Error('Should never happen');
1073
+ }
1074
+
1075
+ const key = lexicalNode.__key;
1076
+ const prevLexicalChildrenKeys = lexicalNode.__children;
1077
+ const nextLexicalChildrenKeys = [];
1078
+ const lexicalChildrenKeysLength = prevLexicalChildrenKeys.length;
1079
+ const collabChildren = this._children;
1080
+ const collabChildrenLength = collabChildren.length;
1081
+ const collabNodeMap = binding.collabNodeMap;
1082
+ const visitedKeys = new Set();
1083
+ let collabKeys; // Assign the new children key array that we're about to mutate
1084
+
1085
+ let writableLexicalNode;
1086
+
1087
+ if (collabChildrenLength !== lexicalChildrenKeysLength) {
1088
+ writableLexicalNode = lazilyCloneElementNode(lexicalNode, writableLexicalNode, nextLexicalChildrenKeys);
1089
+ }
1090
+
1091
+ let prevIndex = 0;
1092
+
1093
+ for (let i = 0; i < collabChildrenLength; i++) {
1094
+ const lexicalChildKey = prevLexicalChildrenKeys[prevIndex];
1095
+ const childCollabNode = collabChildren[i];
1096
+ const collabLexicalChildNode = childCollabNode.getNode();
1097
+ const collabKey = childCollabNode._key;
1098
+
1099
+ if (collabLexicalChildNode !== null && lexicalChildKey === collabKey) {
1100
+ const childNeedsUpdating = lexical.$isTextNode(collabLexicalChildNode); // Update
1101
+
1102
+ visitedKeys.add(lexicalChildKey);
1103
+
1104
+ if (childNeedsUpdating) {
1105
+ childCollabNode._key = lexicalChildKey;
1106
+
1107
+ if (childCollabNode instanceof CollabElementNode) {
1108
+ const xmlText = childCollabNode._xmlText;
1109
+ childCollabNode.syncPropertiesFromYjs(binding, null);
1110
+ childCollabNode.applyChildrenYjsDelta(binding, xmlText.toDelta());
1111
+ childCollabNode.syncChildrenFromYjs(binding);
1112
+ } else if (childCollabNode instanceof CollabTextNode) {
1113
+ childCollabNode.syncPropertiesAndTextFromYjs(binding, null);
1114
+ } else if (childCollabNode instanceof CollabDecoratorNode) {
1115
+ childCollabNode.syncPropertiesFromYjs(binding, null);
1116
+ } else if (!(childCollabNode instanceof CollabLineBreakNode)) {
1117
+ throw new Error('Should never happen');
1118
+ }
1119
+ }
1120
+
1121
+ nextLexicalChildrenKeys[i] = lexicalChildKey;
1122
+ prevIndex++;
1123
+ } else {
1124
+ if (collabKeys === undefined) {
1125
+ collabKeys = new Set();
1126
+
1127
+ for (let s = 0; s < collabChildrenLength; s++) {
1128
+ const child = collabChildren[s];
1129
+ const childKey = child._key;
1130
+
1131
+ if (childKey !== '') {
1132
+ collabKeys.add(childKey);
1133
+ }
1134
+ }
1135
+ }
1136
+
1137
+ if (collabLexicalChildNode !== null && lexicalChildKey !== undefined && !collabKeys.has(lexicalChildKey)) {
1138
+ i--;
1139
+ prevIndex++;
1140
+ continue;
1141
+ }
1142
+
1143
+ writableLexicalNode = lazilyCloneElementNode(lexicalNode, writableLexicalNode, nextLexicalChildrenKeys); // Create/Replace
1144
+
1145
+ const lexicalChildNode = createLexicalNodeFromCollabNode(binding, childCollabNode, key);
1146
+ const childKey = lexicalChildNode.__key;
1147
+ collabNodeMap.set(childKey, childCollabNode);
1148
+ nextLexicalChildrenKeys[i] = childKey;
1149
+ }
1150
+ }
1151
+
1152
+ for (let i = 0; i < lexicalChildrenKeysLength; i++) {
1153
+ const lexicalChildKey = prevLexicalChildrenKeys[i];
1154
+
1155
+ if (!visitedKeys.has(lexicalChildKey)) {
1156
+ // Remove
1157
+ const lexicalChildNode = $getNodeByKeyOrThrow(lexicalChildKey).getWritable();
1158
+ const collabNode = binding.collabNodeMap.get(lexicalChildKey);
1159
+
1160
+ if (collabNode !== undefined) {
1161
+ collabNode.destroy(binding);
1162
+ }
1163
+
1164
+ lexicalChildNode.__parent = null;
1165
+ }
1166
+ }
1167
+ }
1168
+
1169
+ syncPropertiesFromLexical(binding, nextLexicalNode, prevNodeMap) {
1170
+ syncPropertiesFromLexical(binding, this._xmlText, this.getPrevNode(prevNodeMap), nextLexicalNode);
1171
+ }
1172
+
1173
+ _syncChildFromLexical(binding, index, key, prevNodeMap, dirtyElements, dirtyLeaves) {
1174
+ const childCollabNode = this._children[index]; // Update
1175
+
1176
+ const nextChildNode = $getNodeByKeyOrThrow(key);
1177
+
1178
+ if (childCollabNode instanceof CollabElementNode && lexical.$isElementNode(nextChildNode)) {
1179
+ childCollabNode.syncPropertiesFromLexical(binding, nextChildNode, prevNodeMap);
1180
+ childCollabNode.syncChildrenFromLexical(binding, nextChildNode, prevNodeMap, dirtyElements, dirtyLeaves);
1181
+ } else if (childCollabNode instanceof CollabTextNode && lexical.$isTextNode(nextChildNode)) {
1182
+ childCollabNode.syncPropertiesAndTextFromLexical(binding, nextChildNode, prevNodeMap);
1183
+ } else if (childCollabNode instanceof CollabDecoratorNode && lexical.$isDecoratorNode(nextChildNode)) {
1184
+ childCollabNode.syncPropertiesFromLexical(binding, nextChildNode, prevNodeMap);
1185
+ }
1186
+ }
1187
+
1188
+ syncChildrenFromLexical(binding, nextLexicalNode, prevNodeMap, dirtyElements, dirtyLeaves) {
1189
+ const prevLexicalNode = this.getPrevNode(prevNodeMap);
1190
+ const prevChildren = prevLexicalNode === null ? [] : prevLexicalNode.__children;
1191
+ const nextChildren = nextLexicalNode.__children;
1192
+ const prevEndIndex = prevChildren.length - 1;
1193
+ const nextEndIndex = nextChildren.length - 1;
1194
+ const collabNodeMap = binding.collabNodeMap;
1195
+ let prevChildrenSet;
1196
+ let nextChildrenSet;
1197
+ let prevIndex = 0;
1198
+ let nextIndex = 0;
1199
+
1200
+ while (prevIndex <= prevEndIndex && nextIndex <= nextEndIndex) {
1201
+ const prevKey = prevChildren[prevIndex];
1202
+ const nextKey = nextChildren[nextIndex];
1203
+
1204
+ if (prevKey === nextKey) {
1205
+ // Nove move, create or remove
1206
+ this._syncChildFromLexical(binding, nextIndex, nextKey, prevNodeMap, dirtyElements, dirtyLeaves);
1207
+
1208
+ prevIndex++;
1209
+ nextIndex++;
1210
+ } else {
1211
+ if (prevChildrenSet === undefined) {
1212
+ prevChildrenSet = new Set(prevChildren);
1213
+ }
1214
+
1215
+ if (nextChildrenSet === undefined) {
1216
+ nextChildrenSet = new Set(nextChildren);
1217
+ }
1218
+
1219
+ const nextHasPrevKey = nextChildrenSet.has(prevKey);
1220
+ const prevHasNextKey = prevChildrenSet.has(nextKey);
1221
+
1222
+ if (!nextHasPrevKey) {
1223
+ // Remove
1224
+ this.splice(binding, nextIndex, 1);
1225
+ prevIndex++;
1226
+ } else {
1227
+ // Create or replace
1228
+ const nextChildNode = $getNodeByKeyOrThrow(nextKey);
1229
+ const collabNode = $createCollabNodeFromLexicalNode(binding, nextChildNode, this);
1230
+ collabNodeMap.set(nextKey, collabNode);
1231
+
1232
+ if (prevHasNextKey) {
1233
+ this.splice(binding, nextIndex, 1, collabNode);
1234
+ prevIndex++;
1235
+ nextIndex++;
1236
+ } else {
1237
+ this.splice(binding, nextIndex, 0, collabNode);
1238
+ nextIndex++;
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1243
+
1244
+ const appendNewChildren = prevIndex > prevEndIndex;
1245
+ const removeOldChildren = nextIndex > nextEndIndex;
1246
+
1247
+ if (appendNewChildren && !removeOldChildren) {
1248
+ for (; nextIndex <= nextEndIndex; ++nextIndex) {
1249
+ const key = nextChildren[nextIndex];
1250
+ const nextChildNode = $getNodeByKeyOrThrow(key);
1251
+ const collabNode = $createCollabNodeFromLexicalNode(binding, nextChildNode, this);
1252
+ this.append(collabNode);
1253
+ collabNodeMap.set(key, collabNode);
1254
+ }
1255
+ } else if (removeOldChildren && !appendNewChildren) {
1256
+ for (let i = this._children.length - 1; i >= nextIndex; i--) {
1257
+ this.splice(binding, i, 1);
1258
+ }
1259
+ }
1260
+ }
1261
+
1262
+ append(collabNode) {
1263
+ const xmlText = this._xmlText;
1264
+ const children = this._children;
1265
+ const lastChild = children[children.length - 1];
1266
+ const offset = lastChild !== undefined ? lastChild.getOffset() + lastChild.getSize() : 0;
1267
+
1268
+ if (collabNode instanceof CollabElementNode) {
1269
+ xmlText.insertEmbed(offset, collabNode._xmlText);
1270
+ } else if (collabNode instanceof CollabTextNode) {
1271
+ const map = collabNode._map;
1272
+
1273
+ if (map.parent === null) {
1274
+ xmlText.insertEmbed(offset, map);
1275
+ }
1276
+
1277
+ xmlText.insert(offset + 1, collabNode._text);
1278
+ } else if (collabNode instanceof CollabLineBreakNode) {
1279
+ xmlText.insertEmbed(offset, collabNode._map);
1280
+ } else if (collabNode instanceof CollabDecoratorNode) {
1281
+ xmlText.insertEmbed(offset, collabNode._xmlElem);
1282
+ }
1283
+
1284
+ this._children.push(collabNode);
1285
+ }
1286
+
1287
+ splice(binding, index, delCount, collabNode) {
1288
+ const children = this._children;
1289
+ const child = children[index];
1290
+
1291
+ if (child === undefined) {
1292
+ if (collabNode !== undefined) {
1293
+ this.append(collabNode);
1294
+ } else {
1295
+ throw new Error('Should never happen');
1296
+ }
1297
+
1298
+ return;
1299
+ }
1300
+
1301
+ const offset = child.getOffset();
1302
+
1303
+ if (offset === -1) {
1304
+ throw new Error('Should never happen');
1305
+ }
1306
+
1307
+ const xmlText = this._xmlText;
1308
+
1309
+ if (delCount !== 0) {
1310
+ // What if we delete many nodes, don't we need to get all their
1311
+ // sizes?
1312
+ xmlText.delete(offset, child.getSize());
1313
+ }
1314
+
1315
+ if (collabNode instanceof CollabElementNode) {
1316
+ xmlText.insertEmbed(offset, collabNode._xmlText);
1317
+ } else if (collabNode instanceof CollabTextNode) {
1318
+ const map = collabNode._map;
1319
+
1320
+ if (map.parent === null) {
1321
+ xmlText.insertEmbed(offset, map);
1322
+ }
1323
+
1324
+ xmlText.insert(offset + 1, collabNode._text);
1325
+ } else if (collabNode instanceof CollabLineBreakNode) {
1326
+ xmlText.insertEmbed(offset, collabNode._map);
1327
+ } else if (collabNode instanceof CollabDecoratorNode) {
1328
+ xmlText.insertEmbed(offset, collabNode._xmlElem);
1329
+ }
1330
+
1331
+ if (delCount !== 0) {
1332
+ const childrenToDelete = children.slice(index, index + delCount);
1333
+
1334
+ for (let i = 0; i < childrenToDelete.length; i++) {
1335
+ childrenToDelete[i].destroy(binding);
1336
+ }
1337
+ }
1338
+
1339
+ if (collabNode !== undefined) {
1340
+ children.splice(index, delCount, collabNode);
1341
+ } else {
1342
+ children.splice(index, delCount);
1343
+ }
1344
+ }
1345
+
1346
+ getChildOffset(collabNode) {
1347
+ let offset = 0;
1348
+ const children = this._children;
1349
+
1350
+ for (let i = 0; i < children.length; i++) {
1351
+ const child = children[i];
1352
+
1353
+ if (child === collabNode) {
1354
+ return offset;
1355
+ }
1356
+
1357
+ offset += child.getSize();
1358
+ }
1359
+
1360
+ return -1;
1361
+ }
1362
+
1363
+ destroy(binding) {
1364
+ const collabNodeMap = binding.collabNodeMap;
1365
+ const children = this._children;
1366
+
1367
+ for (let i = 0; i < children.length; i++) {
1368
+ children[i].destroy(binding);
1369
+ }
1370
+
1371
+ collabNodeMap.delete(this._key);
1372
+ }
1373
+
1374
+ }
1375
+
1376
+ function lazilyCloneElementNode(lexicalNode, writableLexicalNode, nextLexicalChildrenKeys) {
1377
+ if (writableLexicalNode === undefined) {
1378
+ const clone = lexicalNode.getWritable();
1379
+ clone.__children = nextLexicalChildrenKeys;
1380
+ return clone;
1381
+ }
1382
+
1383
+ return writableLexicalNode;
1384
+ }
1385
+
1386
+ function $createCollabElementNode(xmlText, parent, type) {
1387
+ const collabNode = new CollabElementNode(xmlText, parent, type); // $FlowFixMe: internal field
1388
+
1389
+ xmlText._collabNode = collabNode;
1390
+ return collabNode;
1391
+ }
1392
+
1393
+ /**
1394
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1395
+ *
1396
+ * This source code is licensed under the MIT license found in the
1397
+ * LICENSE file in the root directory of this source tree.
1398
+ *
1399
+ *
1400
+ */
1401
+ function createBinding(editor, provider, id, docMap) {
1402
+ const doc = docMap.get(id);
1403
+
1404
+ if (doc === undefined) {
1405
+ throw new Error('Should never happen');
1406
+ } // $FlowFixMe: this will work
1407
+
1408
+
1409
+ const rootXmlText = doc.get('root', yjs.XmlText);
1410
+ const root = $createCollabElementNode(rootXmlText, null, 'root');
1411
+ root._key = 'root'; // $FlowFixMe: our Flow bindings need fixing
1412
+
1413
+ return {
1414
+ clientID: doc.clientID,
1415
+ collabNodeMap: new Map(),
1416
+ cursors: new Map(),
1417
+ cursorsContainer: null,
1418
+ doc,
1419
+ docMap,
1420
+ editor,
1421
+ id,
1422
+ nodeProperties: new Map(),
1423
+ root
1424
+ };
1425
+ }
1426
+
1427
+ /**
1428
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1429
+ *
1430
+ * This source code is licensed under the MIT license found in the
1431
+ * LICENSE file in the root directory of this source tree.
1432
+ *
1433
+ *
1434
+ */
1435
+
1436
+ function createRelativePosition(point, binding) {
1437
+ const collabNodeMap = binding.collabNodeMap;
1438
+ const collabNode = collabNodeMap.get(point.key);
1439
+
1440
+ if (collabNode === undefined) {
1441
+ return null;
1442
+ }
1443
+
1444
+ let offset = point.offset;
1445
+ let sharedType = collabNode.getSharedType();
1446
+
1447
+ if (collabNode instanceof CollabTextNode) {
1448
+ sharedType = collabNode._parent._xmlText;
1449
+ const currentOffset = collabNode.getOffset();
1450
+
1451
+ if (currentOffset === -1) {
1452
+ return null;
1453
+ }
1454
+
1455
+ offset = currentOffset + 1 + offset;
1456
+ }
1457
+
1458
+ return yjs.createRelativePositionFromTypeIndex(sharedType, offset);
1459
+ }
1460
+
1461
+ function createAbsolutePosition(relativePosition, binding) {
1462
+ return yjs.createAbsolutePositionFromRelativePosition(relativePosition, binding.doc);
1463
+ }
1464
+
1465
+ function shouldUpdatePosition(currentPos, pos) {
1466
+ if (currentPos == null) {
1467
+ if (pos != null) {
1468
+ return true;
1469
+ }
1470
+ } else if (pos == null || !yjs.compareRelativePositions(currentPos, pos)) {
1471
+ return true;
1472
+ }
1473
+
1474
+ return false;
1475
+ }
1476
+
1477
+ function createCursor(name, color) {
1478
+ return {
1479
+ color: color,
1480
+ name: name,
1481
+ selection: null
1482
+ };
1483
+ }
1484
+
1485
+ function destroySelection(binding, selection) {
1486
+ const cursorsContainer = binding.cursorsContainer;
1487
+
1488
+ if (cursorsContainer !== null) {
1489
+ const selections = selection.selections;
1490
+ const selectionsLength = selections.length;
1491
+
1492
+ for (let i = 0; i < selectionsLength; i++) {
1493
+ cursorsContainer.removeChild(selections[i]);
1494
+ }
1495
+ }
1496
+ }
1497
+
1498
+ function destroyCursor(binding, cursor) {
1499
+ const selection = cursor.selection;
1500
+
1501
+ if (selection !== null) {
1502
+ destroySelection(binding, selection);
1503
+ }
1504
+ }
1505
+
1506
+ function getDOMTextNode(element) {
1507
+ let node = element;
1508
+
1509
+ while (node != null) {
1510
+ if (node.nodeType === 3) {
1511
+ // $FlowFixMe: this is a Text
1512
+ return node;
1513
+ }
1514
+
1515
+ node = node.firstChild;
1516
+ }
1517
+
1518
+ return null;
1519
+ }
1520
+
1521
+ function createCursorSelection(cursor, anchorKey, anchorOffset, focusKey, focusOffset) {
1522
+ const color = cursor.color;
1523
+ const caret = document.createElement('span');
1524
+ caret.style.cssText = `position:absolute;top:0;bottom:0;right:-1px;width:1px;background-color:rgb(${color});z-index:10;`;
1525
+ const name = document.createElement('span');
1526
+ name.textContent = cursor.name;
1527
+ name.style.cssText = `position:absolute;left:-2px;top:-16px;background-color:rgb(${color});color:#fff;line-height:12px;height:12px;font-size:12px;padding:2px;font-family:Arial;font-weight:bold`;
1528
+ caret.appendChild(name);
1529
+ return {
1530
+ anchor: {
1531
+ key: anchorKey,
1532
+ offset: anchorOffset
1533
+ },
1534
+ caret,
1535
+ color,
1536
+ focus: {
1537
+ key: focusKey,
1538
+ offset: focusOffset
1539
+ },
1540
+ name,
1541
+ range: document.createRange(),
1542
+ selections: []
1543
+ };
1544
+ }
1545
+
1546
+ function getDOMIndexWithinParent(node) {
1547
+ const parent = node.parentNode;
1548
+
1549
+ if (parent == null) {
1550
+ throw new Error('Should never happen');
1551
+ }
1552
+
1553
+ return [parent, Array.from(parent.childNodes).indexOf(node)];
1554
+ }
1555
+
1556
+ function updateCursor(binding, cursor, nextSelection, nodeMap) {
1557
+ const editor = binding.editor;
1558
+ const rootElement = editor.getRootElement();
1559
+ const cursorsContainer = binding.cursorsContainer;
1560
+
1561
+ if (cursorsContainer === null || rootElement === null) {
1562
+ return;
1563
+ }
1564
+
1565
+ const prevSelection = cursor.selection;
1566
+
1567
+ if (nextSelection === null) {
1568
+ if (prevSelection === null) {
1569
+ return;
1570
+ } else {
1571
+ cursor.selection = null;
1572
+ destroySelection(binding, prevSelection);
1573
+ return;
1574
+ }
1575
+ } else {
1576
+ cursor.selection = nextSelection;
1577
+ }
1578
+
1579
+ const range = nextSelection.range;
1580
+ const caret = nextSelection.caret;
1581
+ const color = nextSelection.color;
1582
+ const selections = nextSelection.selections;
1583
+ const anchor = nextSelection.anchor;
1584
+ const focus = nextSelection.focus;
1585
+ const anchorKey = anchor.key;
1586
+ const focusKey = focus.key;
1587
+ const anchorNode = nodeMap.get(anchorKey);
1588
+ const focusNode = nodeMap.get(focusKey);
1589
+ let anchorDOM = editor.getElementByKey(anchorKey);
1590
+ let focusDOM = editor.getElementByKey(focusKey);
1591
+ let anchorOffset = anchor.offset;
1592
+ let focusOffset = focus.offset;
1593
+
1594
+ if (lexical.$isTextNode(anchorNode)) {
1595
+ anchorDOM = getDOMTextNode(anchorDOM);
1596
+ }
1597
+
1598
+ if (lexical.$isTextNode(focusNode)) {
1599
+ focusDOM = getDOMTextNode(focusDOM);
1600
+ }
1601
+
1602
+ if (anchorNode === undefined || focusNode === undefined || anchorDOM === null || focusDOM === null) {
1603
+ return;
1604
+ }
1605
+
1606
+ if (anchorDOM.nodeName === 'BR') {
1607
+ [anchorDOM, anchorOffset] = getDOMIndexWithinParent(anchorDOM);
1608
+ }
1609
+
1610
+ if (focusDOM.nodeName === 'BR') {
1611
+ [focusDOM, focusOffset] = getDOMIndexWithinParent(focusDOM);
1612
+ }
1613
+
1614
+ const firstChild = anchorDOM.firstChild;
1615
+
1616
+ if (anchorDOM === focusDOM && firstChild != null && firstChild.nodeName === 'BR' && anchorOffset === 0 && focusOffset === 0) {
1617
+ focusOffset = 1;
1618
+ }
1619
+
1620
+ try {
1621
+ range.setStart(anchorDOM, anchorOffset);
1622
+ range.setEnd(focusDOM, focusOffset);
1623
+ } catch (e) {
1624
+ return;
1625
+ }
1626
+
1627
+ if (range.collapsed && (anchorOffset !== focusOffset || anchorKey !== focusKey)) {
1628
+ // Range is backwards, we need to reverse it
1629
+ range.setStart(focusDOM, focusOffset);
1630
+ range.setEnd(anchorDOM, anchorOffset);
1631
+ } // We need to
1632
+
1633
+
1634
+ const rootRect = rootElement.getBoundingClientRect();
1635
+ const computedStyle = getComputedStyle(rootElement);
1636
+ const rootPadding = parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight);
1637
+ const selectionRects = Array.from(range.getClientRects());
1638
+ let selectionRectsLength = selectionRects.length;
1639
+ const selectionsLength = selections.length;
1640
+
1641
+ for (let i = 0; i < selectionRectsLength; i++) {
1642
+ const selectionRect = selectionRects[i];
1643
+
1644
+ if (selectionRect.width + rootPadding === rootRect.width) {
1645
+ // Exclude selections that span the entire element
1646
+ selectionRects.splice(i--, 1);
1647
+ selectionRectsLength--;
1648
+ continue;
1649
+ }
1650
+
1651
+ let selection = selections[i];
1652
+
1653
+ if (selection === undefined) {
1654
+ selection = document.createElement('span');
1655
+ selections[i] = selection;
1656
+ cursorsContainer.appendChild(selection);
1657
+ }
1658
+
1659
+ const style = `position:absolute;top:${selectionRect.top}px;left:${selectionRect.left}px;height:${selectionRect.height}px;width:${selectionRect.width}px;background-color:rgba(${color}, 0.3);pointer-events:none;z-index:10;`;
1660
+ selection.style.cssText = style;
1661
+
1662
+ if (i === selectionRectsLength - 1) {
1663
+ if (caret.parentNode !== selection) {
1664
+ selection.appendChild(caret);
1665
+ }
1666
+ }
1667
+ }
1668
+
1669
+ for (let i = selectionsLength - 1; i >= selectionRectsLength; i--) {
1670
+ const selection = selections[i];
1671
+ cursorsContainer.removeChild(selection);
1672
+ selections.pop();
1673
+ }
1674
+ }
1675
+
1676
+ function syncLocalCursorPosition(binding, provider) {
1677
+ const awareness = provider.awareness;
1678
+ const localState = awareness.getLocalState();
1679
+
1680
+ if (localState === null) {
1681
+ return;
1682
+ }
1683
+
1684
+ const anchorPos = localState.anchorPos;
1685
+ const focusPos = localState.focusPos;
1686
+
1687
+ if (anchorPos !== null && focusPos !== null) {
1688
+ const anchorAbsPos = createAbsolutePosition(anchorPos, binding);
1689
+ const focusAbsPos = createAbsolutePosition(focusPos, binding);
1690
+
1691
+ if (anchorAbsPos !== null && focusAbsPos !== null) {
1692
+ const [anchorCollabNode, anchorOffset] = getCollabNodeAndOffset(anchorAbsPos.type, anchorAbsPos.index);
1693
+ const [focusCollabNode, focusOffset] = getCollabNodeAndOffset(focusAbsPos.type, focusAbsPos.index);
1694
+
1695
+ if (anchorCollabNode !== null && focusCollabNode !== null) {
1696
+ const anchorKey = anchorCollabNode.getKey();
1697
+ const focusKey = focusCollabNode.getKey();
1698
+ const selection = lexical.$getSelection();
1699
+
1700
+ if (!lexical.$isRangeSelection(selection)) {
1701
+ return;
1702
+ }
1703
+
1704
+ const anchor = selection.anchor;
1705
+ const focus = selection.focus;
1706
+
1707
+ if (anchor.key !== anchorKey || anchor.offset !== anchorOffset) {
1708
+ const anchorNode = lexical.$getNodeByKey(anchorKey);
1709
+ selection.anchor.set(anchorKey, anchorOffset, lexical.$isElementNode(anchorNode) ? 'element' : 'text');
1710
+ }
1711
+
1712
+ if (focus.key !== focusKey || focus.offset !== focusOffset) {
1713
+ const focusNode = lexical.$getNodeByKey(focusKey);
1714
+ selection.focus.set(focusKey, focusOffset, lexical.$isElementNode(focusNode) ? 'element' : 'text');
1715
+ }
1716
+ }
1717
+ }
1718
+ }
1719
+ }
1720
+
1721
+ function getCollabNodeAndOffset(sharedType, offset) {
1722
+ // $FlowFixMe: internal field
1723
+ const collabNode = sharedType._collabNode;
1724
+
1725
+ if (collabNode === undefined) {
1726
+ return [null, 0];
1727
+ }
1728
+
1729
+ if (collabNode instanceof CollabElementNode) {
1730
+ const {
1731
+ node,
1732
+ offset: collabNodeOffset
1733
+ } = getPositionFromElementAndOffset(collabNode, offset, true);
1734
+
1735
+ if (node === null) {
1736
+ return [collabNode, 0];
1737
+ } else {
1738
+ return [node, collabNodeOffset];
1739
+ }
1740
+ }
1741
+
1742
+ return [null, 0];
1743
+ }
1744
+
1745
+ function syncCursorPositions(binding, provider) {
1746
+ const awarenessStates = Array.from(provider.awareness.getStates());
1747
+ const localClientID = binding.clientID;
1748
+ const cursors = binding.cursors;
1749
+ const editor = binding.editor;
1750
+ const nodeMap = editor._editorState._nodeMap;
1751
+ const visitedClientIDs = new Set();
1752
+
1753
+ for (let i = 0; i < awarenessStates.length; i++) {
1754
+ const awarenessState = awarenessStates[i];
1755
+ const [clientID, awareness] = awarenessState;
1756
+
1757
+ if (clientID !== localClientID) {
1758
+ visitedClientIDs.add(clientID);
1759
+ const {
1760
+ anchorPos,
1761
+ focusPos,
1762
+ name,
1763
+ color,
1764
+ focusing
1765
+ } = awareness;
1766
+ let selection = null;
1767
+ let cursor = cursors.get(clientID);
1768
+
1769
+ if (cursor === undefined) {
1770
+ cursor = createCursor(name, color);
1771
+ cursors.set(clientID, cursor);
1772
+ }
1773
+
1774
+ if (anchorPos !== null && focusPos !== null && focusing) {
1775
+ const anchorAbsPos = createAbsolutePosition(anchorPos, binding);
1776
+ const focusAbsPos = createAbsolutePosition(focusPos, binding);
1777
+
1778
+ if (anchorAbsPos !== null && focusAbsPos !== null) {
1779
+ const [anchorCollabNode, anchorOffset] = getCollabNodeAndOffset(anchorAbsPos.type, anchorAbsPos.index);
1780
+ const [focusCollabNode, focusOffset] = getCollabNodeAndOffset(focusAbsPos.type, focusAbsPos.index);
1781
+
1782
+ if (anchorCollabNode !== null && focusCollabNode !== null) {
1783
+ const anchorKey = anchorCollabNode.getKey();
1784
+ const focusKey = focusCollabNode.getKey();
1785
+ selection = cursor.selection;
1786
+
1787
+ if (selection === null) {
1788
+ selection = createCursorSelection(cursor, anchorKey, anchorOffset, focusKey, focusOffset);
1789
+ } else {
1790
+ const anchor = selection.anchor;
1791
+ const focus = selection.focus;
1792
+ anchor.key = anchorKey;
1793
+ anchor.offset = anchorOffset;
1794
+ focus.key = focusKey;
1795
+ focus.offset = focusOffset;
1796
+ }
1797
+ }
1798
+ }
1799
+ }
1800
+
1801
+ updateCursor(binding, cursor, selection, nodeMap);
1802
+ }
1803
+ }
1804
+
1805
+ const allClientIDs = Array.from(cursors.keys());
1806
+
1807
+ for (let i = 0; i < allClientIDs.length; i++) {
1808
+ const clientID = allClientIDs[i];
1809
+
1810
+ if (!visitedClientIDs.has(clientID)) {
1811
+ const cursor = cursors.get(clientID);
1812
+
1813
+ if (cursor !== undefined) {
1814
+ destroyCursor(binding, cursor);
1815
+ cursors.delete(clientID);
1816
+ }
1817
+ }
1818
+ }
1819
+ }
1820
+ function syncLexicalSelectionToYjs(binding, provider, prevSelection, nextSelection) {
1821
+ const awareness = provider.awareness;
1822
+ const localState = awareness.getLocalState();
1823
+
1824
+ if (localState === null) {
1825
+ return;
1826
+ }
1827
+
1828
+ const {
1829
+ anchorPos: currentAnchorPos,
1830
+ focusPos: currentFocusPos,
1831
+ name,
1832
+ color,
1833
+ focusing
1834
+ } = localState;
1835
+ let anchorPos = null;
1836
+ let focusPos = null;
1837
+
1838
+ if (nextSelection === null || currentAnchorPos !== null && !nextSelection.is(prevSelection)) {
1839
+ if (prevSelection === null) {
1840
+ return;
1841
+ }
1842
+ }
1843
+
1844
+ if (lexical.$isRangeSelection(nextSelection)) {
1845
+ anchorPos = createRelativePosition(nextSelection.anchor, binding);
1846
+ focusPos = createRelativePosition(nextSelection.focus, binding);
1847
+ }
1848
+
1849
+ if (shouldUpdatePosition(currentAnchorPos, anchorPos) || shouldUpdatePosition(currentFocusPos, focusPos)) {
1850
+ awareness.setLocalState({
1851
+ anchorPos,
1852
+ color,
1853
+ focusPos,
1854
+ focusing,
1855
+ name
1856
+ });
1857
+ }
1858
+ }
1859
+
1860
+ /**
1861
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1862
+ *
1863
+ * This source code is licensed under the MIT license found in the
1864
+ * LICENSE file in the root directory of this source tree.
1865
+ *
1866
+ *
1867
+ */
1868
+ class OffsetView {
1869
+ constructor(offsetMap, firstNode, blockOffsetSize = 1) {
1870
+ this._offsetMap = offsetMap;
1871
+ this._firstNode = firstNode;
1872
+ this._blockOffsetSize = blockOffsetSize;
1873
+ }
1874
+
1875
+ createSelectionFromOffsets(originalStart, originalEnd, diffOffsetView) {
1876
+ const firstNode = this._firstNode;
1877
+
1878
+ if (firstNode === null) {
1879
+ return null;
1880
+ }
1881
+
1882
+ let start = originalStart;
1883
+ let end = originalEnd;
1884
+ let startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
1885
+ let endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
1886
+
1887
+ if (diffOffsetView !== undefined) {
1888
+ start = $getAdjustedOffsetFromDiff(start, startOffsetNode, diffOffsetView, this, this._blockOffsetSize);
1889
+ startOffsetNode = $searchForNodeWithOffset(firstNode, start, this._blockOffsetSize);
1890
+ end = $getAdjustedOffsetFromDiff(end, endOffsetNode, diffOffsetView, this, this._blockOffsetSize);
1891
+ endOffsetNode = $searchForNodeWithOffset(firstNode, end, this._blockOffsetSize);
1892
+ }
1893
+
1894
+ if (startOffsetNode === null || endOffsetNode === null) {
1895
+ return null;
1896
+ }
1897
+
1898
+ let startKey = startOffsetNode.key;
1899
+ let endKey = endOffsetNode.key;
1900
+ const startNode = lexical.$getNodeByKey(startKey);
1901
+ const endNode = lexical.$getNodeByKey(endKey);
1902
+
1903
+ if (startNode === null || endNode === null) {
1904
+ return null;
1905
+ }
1906
+
1907
+ let startOffset = 0;
1908
+ let endOffset = 0;
1909
+ let startType = 'element';
1910
+ let endType = 'element';
1911
+
1912
+ if (startOffsetNode.type === 'text') {
1913
+ startOffset = start - startOffsetNode.start;
1914
+ startType = 'text'; // If we are at the edge of a text node and we
1915
+ // don't have a collapsed selection, then let's
1916
+ // try and correct the offset node.
1917
+
1918
+ const sibling = startNode.getNextSibling();
1919
+
1920
+ if (start !== end && startOffset === startNode.getTextContentSize() && lexical.$isTextNode(sibling)) {
1921
+ startOffset = 0;
1922
+ startKey = sibling.__key;
1923
+ }
1924
+ } else if (startOffsetNode.type === 'inline') {
1925
+ startKey = startNode.getParentOrThrow().getKey();
1926
+ startOffset = end > startOffsetNode.start ? startOffsetNode.end : startOffsetNode.start;
1927
+ }
1928
+
1929
+ if (endOffsetNode.type === 'text') {
1930
+ endOffset = end - endOffsetNode.start;
1931
+ endType = 'text';
1932
+ } else if (endOffsetNode.type === 'inline') {
1933
+ endKey = endNode.getParentOrThrow().getKey();
1934
+ endOffset = end > endOffsetNode.start ? endOffsetNode.end : endOffsetNode.start;
1935
+ }
1936
+
1937
+ const selection = lexical.$createRangeSelection();
1938
+
1939
+ if (selection === null) {
1940
+ return null;
1941
+ }
1942
+
1943
+ selection.anchor.set(startKey, startOffset, startType);
1944
+ selection.focus.set(endKey, endOffset, endType);
1945
+ return selection;
1946
+ }
1947
+
1948
+ getOffsetsFromSelection(selection) {
1949
+ const anchor = selection.anchor;
1950
+ const focus = selection.focus;
1951
+ const offsetMap = this._offsetMap;
1952
+ const anchorOffset = anchor.offset;
1953
+ const focusOffset = focus.offset;
1954
+ let start = -1;
1955
+ let end = -1;
1956
+
1957
+ if (anchor.type === 'text') {
1958
+ const offsetNode = offsetMap.get(anchor.key);
1959
+
1960
+ if (offsetNode !== undefined) {
1961
+ start = offsetNode.start + anchorOffset;
1962
+ }
1963
+ } else {
1964
+ const node = anchor.getNode().getDescendantByIndex(anchorOffset);
1965
+ const offsetNode = offsetMap.get(node.getKey());
1966
+
1967
+ if (offsetNode !== undefined) {
1968
+ const isAtEnd = node.getIndexWithinParent() !== anchorOffset;
1969
+ start = isAtEnd ? offsetNode.end : offsetNode.start;
1970
+ }
1971
+ }
1972
+
1973
+ if (focus.type === 'text') {
1974
+ const offsetNode = offsetMap.get(focus.key);
1975
+
1976
+ if (offsetNode !== undefined) {
1977
+ end = offsetNode.start + focus.offset;
1978
+ }
1979
+ } else {
1980
+ const node = focus.getNode().getDescendantByIndex(focusOffset);
1981
+ const offsetNode = offsetMap.get(node.getKey());
1982
+
1983
+ if (offsetNode !== undefined) {
1984
+ const isAtEnd = node.getIndexWithinParent() !== focusOffset;
1985
+ end = isAtEnd ? offsetNode.end : offsetNode.start;
1986
+ }
1987
+ }
1988
+
1989
+ return [start, end];
1990
+ }
1991
+
1992
+ }
1993
+
1994
+ function $getAdjustedOffsetFromDiff(offset, offsetNode, prevOffsetView, offsetView, blockOffsetSize) {
1995
+ const prevOffsetMap = prevOffsetView._offsetMap;
1996
+ const offsetMap = offsetView._offsetMap;
1997
+ const visited = new Set();
1998
+ let adjustedOffset = offset;
1999
+ let currentNode = offsetNode;
2000
+
2001
+ while (currentNode !== null) {
2002
+ const key = currentNode.key;
2003
+ const prevNode = prevOffsetMap.get(key);
2004
+ const diff = currentNode.end - currentNode.start;
2005
+ visited.add(key);
2006
+
2007
+ if (prevNode === undefined) {
2008
+ adjustedOffset += diff;
2009
+ } else {
2010
+ const prevDiff = prevNode.end - prevNode.start;
2011
+
2012
+ if (prevDiff !== diff) {
2013
+ adjustedOffset += diff - prevDiff;
2014
+ }
2015
+ }
2016
+
2017
+ const sibling = currentNode.prev;
2018
+
2019
+ if (sibling !== null) {
2020
+ currentNode = sibling;
2021
+ continue;
2022
+ }
2023
+
2024
+ let parent = currentNode.parent;
2025
+
2026
+ while (parent !== null) {
2027
+ let parentSibling = parent.prev;
2028
+
2029
+ if (parentSibling !== null) {
2030
+ const parentSiblingKey = parentSibling.key;
2031
+ const prevParentSibling = prevOffsetMap.get(parentSiblingKey);
2032
+ const parentDiff = parentSibling.end - parentSibling.start;
2033
+ visited.add(parentSiblingKey);
2034
+
2035
+ if (prevParentSibling === undefined) {
2036
+ adjustedOffset += parentDiff;
2037
+ } else {
2038
+ const prevParentDiff = prevParentSibling.end - prevParentSibling.start;
2039
+
2040
+ if (prevParentDiff !== parentDiff) {
2041
+ adjustedOffset += parentDiff - prevParentDiff;
2042
+ }
2043
+ }
2044
+
2045
+ parentSibling = parentSibling.prev;
2046
+ }
2047
+
2048
+ parent = parent.parent;
2049
+ }
2050
+
2051
+ break;
2052
+ } // Now traverse through the old offsets nodes and find any nodes we missed
2053
+ // above, because they were not in the latest offset node view (they have been
2054
+ // deleted).
2055
+
2056
+
2057
+ const prevFirstNode = prevOffsetView._firstNode;
2058
+
2059
+ if (prevFirstNode !== null) {
2060
+ currentNode = $searchForNodeWithOffset(prevFirstNode, offset, blockOffsetSize);
2061
+ let alreadyVisistedParentOfCurrentNode = false;
2062
+
2063
+ while (currentNode !== null) {
2064
+ if (!visited.has(currentNode.key)) {
2065
+ alreadyVisistedParentOfCurrentNode = true;
2066
+ break;
2067
+ }
2068
+
2069
+ currentNode = currentNode.parent;
2070
+ }
2071
+
2072
+ if (!alreadyVisistedParentOfCurrentNode) {
2073
+ while (currentNode !== null) {
2074
+ const key = currentNode.key;
2075
+
2076
+ if (!visited.has(key)) {
2077
+ const node = offsetMap.get(key);
2078
+ const prevDiff = currentNode.end - currentNode.start;
2079
+
2080
+ if (node === undefined) {
2081
+ adjustedOffset -= prevDiff;
2082
+ } else {
2083
+ const diff = node.end - node.start;
2084
+
2085
+ if (prevDiff !== diff) {
2086
+ adjustedOffset += diff - prevDiff;
2087
+ }
2088
+ }
2089
+ }
2090
+
2091
+ currentNode = currentNode.prev;
2092
+ }
2093
+ }
2094
+ }
2095
+
2096
+ return adjustedOffset;
2097
+ }
2098
+
2099
+ function $searchForNodeWithOffset(firstNode, offset, blockOffsetSize) {
2100
+ let currentNode = firstNode;
2101
+
2102
+ while (currentNode !== null) {
2103
+ const end = currentNode.end + (currentNode.type !== 'element' || blockOffsetSize === 0 ? 1 : 0);
2104
+
2105
+ if (offset < end) {
2106
+ const child = currentNode.child;
2107
+
2108
+ if (child !== null) {
2109
+ currentNode = child;
2110
+ continue;
2111
+ }
2112
+
2113
+ return currentNode;
2114
+ }
2115
+
2116
+ const sibling = currentNode.next;
2117
+
2118
+ if (sibling === null) {
2119
+ break;
2120
+ }
2121
+
2122
+ currentNode = sibling;
2123
+ }
2124
+
2125
+ return null;
2126
+ }
2127
+
2128
+ function $createInternalOffsetNode(child, type, start, end, key, parent) {
2129
+ // $FlowFixMe: not sure why Flow doesn't like this?
2130
+ return {
2131
+ child,
2132
+ end,
2133
+ key,
2134
+ next: null,
2135
+ parent,
2136
+ prev: null,
2137
+ start,
2138
+ type
2139
+ };
2140
+ }
2141
+
2142
+ function $createOffsetNode(state, key, parent, nodeMap, offsetMap, blockOffsetSize) {
2143
+ const node = nodeMap.get(key);
2144
+
2145
+ if (node === undefined) {
2146
+ {
2147
+ throw Error(`createOffsetModel: could not find node by key`);
2148
+ }
2149
+ }
2150
+
2151
+ const start = state.offset;
2152
+
2153
+ if (lexical.$isElementNode(node)) {
2154
+ const childKeys = node.__children;
2155
+ const blockIsEmpty = childKeys.length === 0;
2156
+ const child = blockIsEmpty ? null : $createOffsetChild(state, childKeys, null, nodeMap, offsetMap, blockOffsetSize); // If the prev node was not a block or the block is empty, we should
2157
+ // account for the user being able to selection the block (due to the \n).
2158
+
2159
+ if (!state.prevIsBlock || blockIsEmpty) {
2160
+ state.prevIsBlock = true;
2161
+ state.offset += blockOffsetSize;
2162
+ }
2163
+
2164
+ const offsetNode = $createInternalOffsetNode(child, 'element', start, start, key, parent);
2165
+
2166
+ if (child !== null) {
2167
+ child.parent = offsetNode;
2168
+ }
2169
+
2170
+ const end = state.offset;
2171
+ offsetNode.end = end;
2172
+ offsetMap.set(key, offsetNode);
2173
+ return offsetNode;
2174
+ }
2175
+
2176
+ state.prevIsBlock = false;
2177
+ const isText = lexical.$isTextNode(node); // $FlowFixMe: isText means __text is available
2178
+
2179
+ const length = isText ? node.__text.length : 1;
2180
+ const end = state.offset += length;
2181
+ const offsetNode = $createInternalOffsetNode(null, isText ? 'text' : 'inline', start, end, key, parent);
2182
+ offsetMap.set(key, offsetNode);
2183
+ return offsetNode;
2184
+ }
2185
+
2186
+ function $createOffsetChild(state, children, parent, nodeMap, offsetMap, blockOffsetSize) {
2187
+ let firstNode = null;
2188
+ let currentNode = null;
2189
+ const childrenLength = children.length;
2190
+
2191
+ for (let i = 0; i < childrenLength; i++) {
2192
+ const childKey = children[i];
2193
+ const offsetNode = $createOffsetNode(state, childKey, parent, nodeMap, offsetMap, blockOffsetSize);
2194
+
2195
+ if (currentNode === null) {
2196
+ firstNode = offsetNode;
2197
+ } else {
2198
+ offsetNode.prev = currentNode;
2199
+ currentNode.next = offsetNode;
2200
+ }
2201
+
2202
+ currentNode = offsetNode;
2203
+ }
2204
+
2205
+ return firstNode;
2206
+ }
2207
+
2208
+ function $createOffsetView(editor, blockOffsetSize = 1, editorState) {
2209
+ const targetEditorState = editorState || editor._pendingEditorState || editor._editorState;
2210
+ const nodeMap = targetEditorState._nodeMap; // $FlowFixMe: root is always in the Map
2211
+
2212
+ const root = nodeMap.get('root');
2213
+ const offsetMap = new Map();
2214
+ const state = {
2215
+ offset: 0,
2216
+ prevIsBlock: false
2217
+ };
2218
+ const node = $createOffsetChild(state, root.__children, null, nodeMap, offsetMap, blockOffsetSize);
2219
+ return new OffsetView(offsetMap, node, blockOffsetSize);
2220
+ }
2221
+
2222
+ /**
2223
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2224
+ *
2225
+ * This source code is licensed under the MIT license found in the
2226
+ * LICENSE file in the root directory of this source tree.
2227
+ *
2228
+ *
2229
+ */
2230
+
2231
+ function syncEvent(binding, event) {
2232
+ const {
2233
+ target
2234
+ } = event;
2235
+ const collabNode = getOrInitCollabNodeFromSharedType(binding, target); // $FlowFixMe: internal field
2236
+
2237
+ const decoratorStateValue = target._lexicalValue; // Check if this event relates to a decorator state change.
2238
+
2239
+ if (decoratorStateValue !== undefined && collabNode instanceof CollabDecoratorNode) {
2240
+ if (target instanceof yjs.Map) {
2241
+ // Sync decorator state value
2242
+ syncYjsDecoratorMapToLexical(binding, collabNode, target, decoratorStateValue, event.keysChanged);
2243
+ } else if (target instanceof yjs.Array && lexical.isDecoratorArray(decoratorStateValue)) {
2244
+ // Sync decorator state value
2245
+ const deltas = event.delta;
2246
+ let offset = 0;
2247
+
2248
+ for (let i = 0; i < deltas.length; i++) {
2249
+ const delta = deltas[i];
2250
+ const retainOp = delta.retain;
2251
+ const deleteOp = delta.delete;
2252
+ const insertOp = delta.insert;
2253
+
2254
+ if (retainOp !== undefined) {
2255
+ offset += retainOp;
2256
+ } else if (deleteOp !== undefined) {
2257
+ mutationFromCollab(() => {
2258
+ const elements = decoratorStateValue._array.slice(offset, offset + deleteOp);
2259
+
2260
+ elements.forEach(element => {
2261
+ if (lexical.isDecoratorArray(element) || lexical.isDecoratorMap(element)) {
2262
+ element.destroy();
2263
+ }
2264
+ });
2265
+ decoratorStateValue.splice(offset, deleteOp);
2266
+ });
2267
+ } else if (insertOp !== undefined) {
2268
+ syncYjsDecoratorArrayValueToLexical(binding, collabNode, target, decoratorStateValue, offset);
2269
+ } else {
2270
+ throw new Error('Not supported');
2271
+ }
2272
+ }
2273
+ }
2274
+
2275
+ return;
2276
+ }
2277
+
2278
+ if (collabNode instanceof CollabElementNode && event instanceof yjs.YTextEvent) {
2279
+ const {
2280
+ keysChanged,
2281
+ childListChanged,
2282
+ delta
2283
+ } = event; // Update
2284
+
2285
+ if (keysChanged.size > 0) {
2286
+ collabNode.syncPropertiesFromYjs(binding, keysChanged);
2287
+ }
2288
+
2289
+ if (childListChanged) {
2290
+ collabNode.applyChildrenYjsDelta(binding, delta);
2291
+ collabNode.syncChildrenFromYjs(binding);
2292
+ }
2293
+ } else if (collabNode instanceof CollabTextNode && event instanceof yjs.YMapEvent) {
2294
+ const {
2295
+ keysChanged
2296
+ } = event; // Update
2297
+
2298
+ if (keysChanged.size > 0) {
2299
+ collabNode.syncPropertiesAndTextFromYjs(binding, keysChanged);
2300
+ }
2301
+ } else if (collabNode instanceof CollabDecoratorNode && event instanceof yjs.YXmlEvent) {
2302
+ const {
2303
+ attributesChanged
2304
+ } = event; // Update
2305
+
2306
+ if (attributesChanged.size > 0) {
2307
+ collabNode.syncPropertiesFromYjs(binding, attributesChanged);
2308
+ }
2309
+ } else {
2310
+ throw new Error('Should never happen');
2311
+ }
2312
+ }
2313
+
2314
+ function syncYjsChangesToLexical(binding, provider, events) {
2315
+ const editor = binding.editor;
2316
+ const currentEditorState = editor._editorState;
2317
+ editor.update(() => {
2318
+ // $FlowFixMe: this is always true
2319
+ const pendingEditorState = editor._pendingEditorState;
2320
+
2321
+ for (let i = 0; i < events.length; i++) {
2322
+ const event = events[i];
2323
+ syncEvent(binding, event);
2324
+ }
2325
+
2326
+ const selection = lexical.$getSelection();
2327
+
2328
+ if (lexical.$isRangeSelection(selection)) {
2329
+ // We can't use Yjs's cursor position here, as it doesn't always
2330
+ // handle selection recovery correctly – especially on elements that
2331
+ // get moved around or split. So instead, we roll our own solution.
2332
+ if (doesSelectionNeedRecovering(selection)) {
2333
+ const prevSelection = currentEditorState._selection;
2334
+
2335
+ if (lexical.$isRangeSelection(prevSelection)) {
2336
+ const prevOffsetView = $createOffsetView(editor, 0, currentEditorState);
2337
+ const nextOffsetView = $createOffsetView(editor, 0, pendingEditorState);
2338
+ const [start, end] = prevOffsetView.getOffsetsFromSelection(prevSelection);
2339
+ const nextSelection = nextOffsetView.createSelectionFromOffsets(start, end, prevOffsetView);
2340
+
2341
+ if (nextSelection !== null) {
2342
+ lexical.$setSelection(nextSelection);
2343
+ } else {
2344
+ // Fallback is to use the Yjs cursor position
2345
+ syncLocalCursorPosition(binding, provider);
2346
+
2347
+ if (doesSelectionNeedRecovering(selection)) {
2348
+ const root = lexical.$getRoot(); // If there was a collision on the top level paragraph
2349
+ // we need to re-add a paragraph
2350
+
2351
+ if (root.getChildrenSize() === 0) {
2352
+ root.append(lexical.$createParagraphNode());
2353
+ } // Fallback
2354
+
2355
+
2356
+ lexical.$getRoot().selectEnd();
2357
+ }
2358
+ }
2359
+ }
2360
+
2361
+ syncLexicalSelectionToYjs(binding, provider, prevSelection, lexical.$getSelection());
2362
+ } else {
2363
+ syncLocalCursorPosition(binding, provider);
2364
+ }
2365
+ }
2366
+ }, {
2367
+ onUpdate: () => {
2368
+ syncCursorPositions(binding, provider);
2369
+ },
2370
+ skipTransforms: true,
2371
+ tag: 'collaboration'
2372
+ });
2373
+ }
2374
+
2375
+ function handleNormalizationMergeConflicts(binding, normalizedNodes) {
2376
+ // We handle the merge opperations here
2377
+ const normalizedNodesKeys = Array.from(normalizedNodes);
2378
+ const collabNodeMap = binding.collabNodeMap;
2379
+ const mergedNodes = [];
2380
+
2381
+ for (let i = 0; i < normalizedNodesKeys.length; i++) {
2382
+ const nodeKey = normalizedNodesKeys[i];
2383
+ const lexicalNode = lexical.$getNodeByKey(nodeKey);
2384
+ const collabNode = collabNodeMap.get(nodeKey);
2385
+
2386
+ if (collabNode instanceof CollabTextNode) {
2387
+ if (lexical.$isTextNode(lexicalNode)) {
2388
+ // We mutate the text collab nodes after removing
2389
+ // all the dead nodes first, otherwise offsets break.
2390
+ mergedNodes.push([collabNode, lexicalNode.__text]);
2391
+ } else {
2392
+ const offset = collabNode.getOffset();
2393
+
2394
+ if (offset === -1) {
2395
+ continue;
2396
+ }
2397
+
2398
+ const parent = collabNode._parent;
2399
+ collabNode._normalized = true;
2400
+
2401
+ parent._xmlText.delete(offset, 1);
2402
+
2403
+ collabNodeMap.delete(nodeKey);
2404
+ const parentChildren = parent._children;
2405
+ const index = parentChildren.indexOf(collabNode);
2406
+ parentChildren.splice(index, 1);
2407
+ }
2408
+ }
2409
+ }
2410
+
2411
+ for (let i = 0; i < mergedNodes.length; i++) {
2412
+ const [collabNode, text] = mergedNodes[i];
2413
+ collabNode._text = text;
2414
+ }
2415
+ }
2416
+
2417
+ function syncLexicalUpdateToYjs(binding, provider, prevEditorState, currEditorState, dirtyElements, dirtyLeaves, normalizedNodes, tags) {
2418
+ syncWithTransaction(binding, () => {
2419
+ currEditorState.read(() => {
2420
+ // We check if the update has come from a origin where the origin
2421
+ // was the collaboration binding previously. This can help us
2422
+ // prevent unecessarily re-diffing and possible re-applying
2423
+ // the same change editor state again. For example, if a user
2424
+ // types a character and we get it, we don't want to then insert
2425
+ // the same character again. The exception to this heuristic is
2426
+ // when we need to handle normalization merge conflicts.
2427
+ if (tags.has('collaboration')) {
2428
+ if (normalizedNodes.size > 0) {
2429
+ handleNormalizationMergeConflicts(binding, normalizedNodes);
2430
+ }
2431
+
2432
+ return;
2433
+ }
2434
+
2435
+ if (dirtyElements.has('root')) {
2436
+ const prevNodeMap = prevEditorState._nodeMap;
2437
+ const nextLexicalRoot = lexical.$getRoot();
2438
+ const collabRoot = binding.root;
2439
+ collabRoot.syncPropertiesFromLexical(binding, nextLexicalRoot, prevNodeMap);
2440
+ collabRoot.syncChildrenFromLexical(binding, nextLexicalRoot, prevNodeMap, dirtyElements, dirtyLeaves);
2441
+ }
2442
+
2443
+ const selection = lexical.$getSelection();
2444
+ const prevSelection = prevEditorState._selection;
2445
+ syncLexicalSelectionToYjs(binding, provider, prevSelection, selection);
2446
+ });
2447
+ });
2448
+ }
2449
+
2450
+ /**
2451
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2452
+ *
2453
+ * This source code is licensed under the MIT license found in the
2454
+ * LICENSE file in the root directory of this source tree.
2455
+ *
2456
+ *
2457
+ */
2458
+ function createUndoManager(binding, root) {
2459
+ return new yjs.UndoManager(root, {
2460
+ trackedOrigins: new Set([binding, null])
2461
+ });
2462
+ }
2463
+ function initLocalState(provider, name, color, focusing) {
2464
+ provider.awareness.setLocalState({
2465
+ anchorPos: null,
2466
+ color,
2467
+ focusPos: null,
2468
+ focusing: focusing,
2469
+ name
2470
+ });
2471
+ }
2472
+ function setLocalStateFocus(provider, focusing) {
2473
+ const {
2474
+ awareness
2475
+ } = provider;
2476
+ awareness.setLocalState({ ...awareness.getLocalState(),
2477
+ focusing
2478
+ });
2479
+ }
2480
+
2481
+ exports.createBinding = createBinding;
2482
+ exports.createUndoManager = createUndoManager;
2483
+ exports.initLocalState = initLocalState;
2484
+ exports.setLocalStateFocus = setLocalStateFocus;
2485
+ exports.syncCursorPositions = syncCursorPositions;
2486
+ exports.syncLexicalUpdateToYjs = syncLexicalUpdateToYjs;
2487
+ exports.syncYjsChangesToLexical = syncYjsChangesToLexical;