@tiptap/y-tiptap 1.0.0 → 1.1.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/dist/src/lib.d.ts +8 -4
- package/dist/src/plugins/cursor-plugin.d.ts +6 -6
- package/dist/src/plugins/sync-plugin.d.ts +31 -6
- package/dist/src/utils.d.ts +1 -0
- package/dist/y-tiptap.cjs +270 -241
- package/dist/y-tiptap.cjs.map +1 -1
- package/dist/y-tiptap.js +264 -237
- package/dist/y-tiptap.js.map +1 -1
- package/package.json +2 -2
package/dist/y-tiptap.cjs
CHANGED
|
@@ -3,21 +3,24 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var Y = require('yjs');
|
|
6
|
-
var
|
|
7
|
-
var
|
|
6
|
+
var prosemirrorView = require('prosemirror-view');
|
|
7
|
+
var prosemirrorState = require('prosemirror-state');
|
|
8
8
|
require('y-protocols/awareness');
|
|
9
9
|
var mutex = require('lib0/mutex');
|
|
10
|
-
var PModel = require('
|
|
10
|
+
var PModel = require('prosemirror-model');
|
|
11
11
|
var math = require('lib0/math');
|
|
12
|
+
var object = require('lib0/object');
|
|
12
13
|
var set = require('lib0/set');
|
|
13
14
|
var diff = require('lib0/diff');
|
|
14
15
|
var error = require('lib0/error');
|
|
16
|
+
var state = require('@tiptap/pm/state');
|
|
15
17
|
var random = require('lib0/random');
|
|
16
18
|
var environment = require('lib0/environment');
|
|
17
19
|
var dom = require('lib0/dom');
|
|
18
20
|
var eventloop = require('lib0/eventloop');
|
|
19
|
-
var f = require('lib0/function');
|
|
20
21
|
var map = require('lib0/map');
|
|
22
|
+
var sha256 = require('lib0/hash/sha256');
|
|
23
|
+
var buf = require('lib0/buffer');
|
|
21
24
|
|
|
22
25
|
function _interopNamespace(e) {
|
|
23
26
|
if (e && e.__esModule) return e;
|
|
@@ -40,14 +43,16 @@ function _interopNamespace(e) {
|
|
|
40
43
|
var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
|
|
41
44
|
var PModel__namespace = /*#__PURE__*/_interopNamespace(PModel);
|
|
42
45
|
var math__namespace = /*#__PURE__*/_interopNamespace(math);
|
|
46
|
+
var object__namespace = /*#__PURE__*/_interopNamespace(object);
|
|
43
47
|
var set__namespace = /*#__PURE__*/_interopNamespace(set);
|
|
44
48
|
var error__namespace = /*#__PURE__*/_interopNamespace(error);
|
|
45
49
|
var random__namespace = /*#__PURE__*/_interopNamespace(random);
|
|
46
50
|
var environment__namespace = /*#__PURE__*/_interopNamespace(environment);
|
|
47
51
|
var dom__namespace = /*#__PURE__*/_interopNamespace(dom);
|
|
48
52
|
var eventloop__namespace = /*#__PURE__*/_interopNamespace(eventloop);
|
|
49
|
-
var f__namespace = /*#__PURE__*/_interopNamespace(f);
|
|
50
53
|
var map__namespace = /*#__PURE__*/_interopNamespace(map);
|
|
54
|
+
var sha256__namespace = /*#__PURE__*/_interopNamespace(sha256);
|
|
55
|
+
var buf__namespace = /*#__PURE__*/_interopNamespace(buf);
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
58
|
* The unique prosemirror plugin key for syncPlugin
|
|
@@ -70,11 +75,41 @@ const yUndoPluginKey = new state.PluginKey('y-undo');
|
|
|
70
75
|
*/
|
|
71
76
|
const yCursorPluginKey = new state.PluginKey('yjs-cursor');
|
|
72
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Custom function to transform sha256 hash to N byte
|
|
80
|
+
*
|
|
81
|
+
* @param {Uint8Array} digest
|
|
82
|
+
*/
|
|
83
|
+
const _convolute = digest => {
|
|
84
|
+
const N = 6;
|
|
85
|
+
for (let i = N; i < digest.length; i++) {
|
|
86
|
+
digest[i % N] = digest[i % N] ^ digest[i];
|
|
87
|
+
}
|
|
88
|
+
return digest.slice(0, N)
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* @param {any} json
|
|
93
|
+
*/
|
|
94
|
+
const hashOfJSON = (json) => buf__namespace.toBase64(_convolute(sha256__namespace.digest(buf__namespace.encodeAny(json))));
|
|
95
|
+
|
|
73
96
|
/**
|
|
74
97
|
* @module bindings/prosemirror
|
|
75
98
|
*/
|
|
76
99
|
|
|
77
|
-
|
|
100
|
+
/**
|
|
101
|
+
* @typedef {Object} BindingMetadata
|
|
102
|
+
* @property {ProsemirrorMapping} BindingMetadata.mapping
|
|
103
|
+
* @property {Map<import('prosemirror-model').MarkType, boolean>} BindingMetadata.isOMark - is overlapping mark
|
|
104
|
+
*/
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @return BindingMetadata
|
|
108
|
+
*/
|
|
109
|
+
const createEmptyMeta = () => ({
|
|
110
|
+
mapping: new Map(),
|
|
111
|
+
isOMark: new Map()
|
|
112
|
+
});
|
|
78
113
|
|
|
79
114
|
/**
|
|
80
115
|
* @param {Y.Item} item
|
|
@@ -148,7 +183,7 @@ const ySyncPlugin = (yXmlFragment, {
|
|
|
148
183
|
} = {}) => {
|
|
149
184
|
let initialContentChanged = false;
|
|
150
185
|
const binding = new ProsemirrorBinding(yXmlFragment, mapping);
|
|
151
|
-
const plugin = new
|
|
186
|
+
const plugin = new prosemirrorState.Plugin({
|
|
152
187
|
props: {
|
|
153
188
|
editable: (state) => {
|
|
154
189
|
const syncState = ySyncPluginKey.getState(state);
|
|
@@ -281,31 +316,48 @@ const ySyncPlugin = (yXmlFragment, {
|
|
|
281
316
|
};
|
|
282
317
|
|
|
283
318
|
/**
|
|
284
|
-
* @param {
|
|
285
|
-
* @param {
|
|
319
|
+
* @param {import('prosemirror-state').Transaction} tr
|
|
320
|
+
* @param {ReturnType<typeof getRelativeSelection>} relSel
|
|
286
321
|
* @param {ProsemirrorBinding} binding
|
|
287
322
|
*/
|
|
288
323
|
const restoreRelativeSelection = (tr, relSel, binding) => {
|
|
289
324
|
if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
325
|
+
if (relSel.type === 'all') {
|
|
326
|
+
tr.setSelection(new prosemirrorState.AllSelection(tr.doc));
|
|
327
|
+
} else if (relSel.type === 'node') {
|
|
328
|
+
const anchor = relativePositionToAbsolutePosition(
|
|
329
|
+
binding.doc,
|
|
330
|
+
binding.type,
|
|
331
|
+
relSel.anchor,
|
|
332
|
+
binding.mapping
|
|
333
|
+
);
|
|
334
|
+
tr.setSelection(prosemirrorState.NodeSelection.create(tr.doc, anchor));
|
|
335
|
+
} else {
|
|
336
|
+
const anchor = relativePositionToAbsolutePosition(
|
|
337
|
+
binding.doc,
|
|
338
|
+
binding.type,
|
|
339
|
+
relSel.anchor,
|
|
340
|
+
binding.mapping
|
|
341
|
+
);
|
|
342
|
+
const head = relativePositionToAbsolutePosition(
|
|
343
|
+
binding.doc,
|
|
344
|
+
binding.type,
|
|
345
|
+
relSel.head,
|
|
346
|
+
binding.mapping
|
|
347
|
+
);
|
|
348
|
+
if (anchor !== null && head !== null) {
|
|
349
|
+
tr.setSelection(prosemirrorState.TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head)));
|
|
350
|
+
}
|
|
304
351
|
}
|
|
305
352
|
}
|
|
306
353
|
};
|
|
307
354
|
|
|
355
|
+
/**
|
|
356
|
+
* @param {ProsemirrorBinding} pmbinding
|
|
357
|
+
* @param {import('prosemirror-state').EditorState} state
|
|
358
|
+
*/
|
|
308
359
|
const getRelativeSelection = (pmbinding, state) => ({
|
|
360
|
+
type: /** @type {any} */ (state.selection).jsonID,
|
|
309
361
|
anchor: absolutePositionToRelativePosition(
|
|
310
362
|
state.selection.anchor,
|
|
311
363
|
pmbinding.type,
|
|
@@ -337,6 +389,12 @@ class ProsemirrorBinding {
|
|
|
337
389
|
this.prosemirrorView = null;
|
|
338
390
|
this.mux = mutex.createMutex();
|
|
339
391
|
this.mapping = mapping;
|
|
392
|
+
/**
|
|
393
|
+
* Is overlapping mark - i.e. mark does not exclude itself.
|
|
394
|
+
*
|
|
395
|
+
* @type {Map<import('prosemirror-model').MarkType, boolean>}
|
|
396
|
+
*/
|
|
397
|
+
this.isOMark = new Map();
|
|
340
398
|
this._observeFunction = this._typeChanged.bind(this);
|
|
341
399
|
/**
|
|
342
400
|
* @type {Y.Doc}
|
|
@@ -431,7 +489,7 @@ class ProsemirrorBinding {
|
|
|
431
489
|
createNodeFromYElement(
|
|
432
490
|
/** @type {Y.XmlElement} */ (t),
|
|
433
491
|
this.prosemirrorView.state.schema,
|
|
434
|
-
this
|
|
492
|
+
this
|
|
435
493
|
)
|
|
436
494
|
).filter((n) => n !== null);
|
|
437
495
|
// @ts-ignore
|
|
@@ -456,7 +514,7 @@ class ProsemirrorBinding {
|
|
|
456
514
|
createNodeFromYElement(
|
|
457
515
|
/** @type {Y.XmlElement} */ (t),
|
|
458
516
|
this.prosemirrorView.state.schema,
|
|
459
|
-
this
|
|
517
|
+
this
|
|
460
518
|
)
|
|
461
519
|
).filter((n) => n !== null);
|
|
462
520
|
// @ts-ignore
|
|
@@ -474,7 +532,7 @@ class ProsemirrorBinding {
|
|
|
474
532
|
const clampedAnchor = math__namespace.min(math__namespace.max(sel.anchor, 0), tr.doc.content.size);
|
|
475
533
|
const clampedHead = math__namespace.min(math__namespace.max(sel.head, 0), tr.doc.content.size);
|
|
476
534
|
|
|
477
|
-
tr.setSelection(
|
|
535
|
+
tr.setSelection(prosemirrorState.TextSelection.create(tr.doc, clampedAnchor, clampedHead));
|
|
478
536
|
}
|
|
479
537
|
this.prosemirrorView.dispatch(
|
|
480
538
|
tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this })
|
|
@@ -493,6 +551,7 @@ class ProsemirrorBinding {
|
|
|
493
551
|
* @type {Y.Doc}
|
|
494
552
|
*/
|
|
495
553
|
let historyDoc = this.doc;
|
|
554
|
+
let historyType = this.type;
|
|
496
555
|
if (!snapshot) {
|
|
497
556
|
snapshot = Y__namespace.snapshot(this.doc);
|
|
498
557
|
}
|
|
@@ -506,6 +565,29 @@ class ProsemirrorBinding {
|
|
|
506
565
|
prevSnapshot = Y__namespace.snapshot(historyDoc);
|
|
507
566
|
Y__namespace.applyUpdateV2(historyDoc, snapshot);
|
|
508
567
|
snapshot = Y__namespace.snapshot(historyDoc);
|
|
568
|
+
if (historyType._item === null) {
|
|
569
|
+
/**
|
|
570
|
+
* If is a root type, we need to find the root key in the initial document
|
|
571
|
+
* and use it to get the history type.
|
|
572
|
+
*/
|
|
573
|
+
const rootKey = Array.from(this.doc.share.keys()).find(
|
|
574
|
+
(key) => this.doc.share.get(key) === this.type
|
|
575
|
+
);
|
|
576
|
+
historyType = historyDoc.getXmlFragment(rootKey);
|
|
577
|
+
} else {
|
|
578
|
+
/**
|
|
579
|
+
* If it is a sub type, we use the item id to find the history type.
|
|
580
|
+
*/
|
|
581
|
+
const historyStructs =
|
|
582
|
+
historyDoc.store.clients.get(historyType._item.id.client) ?? [];
|
|
583
|
+
const itemIndex = Y__namespace.findIndexSS(
|
|
584
|
+
historyStructs,
|
|
585
|
+
historyType._item.id.clock
|
|
586
|
+
);
|
|
587
|
+
const item = /** @type {Y.Item} */ (historyStructs[itemIndex]);
|
|
588
|
+
const content = /** @type {Y.ContentType} */ (item.content);
|
|
589
|
+
historyType = /** @type {Y.XmlFragment} */ (content.type);
|
|
590
|
+
}
|
|
509
591
|
}
|
|
510
592
|
// clear mapping because we are going to rerender
|
|
511
593
|
this.mapping.clear();
|
|
@@ -513,6 +595,9 @@ class ProsemirrorBinding {
|
|
|
513
595
|
historyDoc.transact((transaction) => {
|
|
514
596
|
// before rendering, we are going to sanitize ops and split deleted ops
|
|
515
597
|
// if they were deleted by seperate users.
|
|
598
|
+
/**
|
|
599
|
+
* @type {Y.PermanentUserData}
|
|
600
|
+
*/
|
|
516
601
|
const pud = pluginState.permanentUserData;
|
|
517
602
|
if (pud) {
|
|
518
603
|
pud.dss.forEach((ds) => {
|
|
@@ -539,7 +624,7 @@ class ProsemirrorBinding {
|
|
|
539
624
|
};
|
|
540
625
|
// Create document fragment and render
|
|
541
626
|
const fragmentContent = Y__namespace.typeListToArraySnapshot(
|
|
542
|
-
|
|
627
|
+
historyType,
|
|
543
628
|
new Y__namespace.Snapshot(prevSnapshot.ds, snapshot.sv)
|
|
544
629
|
).map((t) => {
|
|
545
630
|
if (
|
|
@@ -549,7 +634,7 @@ class ProsemirrorBinding {
|
|
|
549
634
|
return createNodeFromYElement(
|
|
550
635
|
t,
|
|
551
636
|
this.prosemirrorView.state.schema,
|
|
552
|
-
new Map(),
|
|
637
|
+
{ mapping: new Map(), isOMark: new Map() },
|
|
553
638
|
snapshot,
|
|
554
639
|
prevSnapshot,
|
|
555
640
|
computeYChange
|
|
@@ -610,7 +695,7 @@ class ProsemirrorBinding {
|
|
|
610
695
|
createNodeIfNotExists(
|
|
611
696
|
/** @type {Y.XmlElement | Y.XmlHook} */ (t),
|
|
612
697
|
this.prosemirrorView.state.schema,
|
|
613
|
-
this
|
|
698
|
+
this
|
|
614
699
|
)
|
|
615
700
|
).filter((n) => n !== null);
|
|
616
701
|
// @ts-ignore
|
|
@@ -630,9 +715,12 @@ class ProsemirrorBinding {
|
|
|
630
715
|
});
|
|
631
716
|
}
|
|
632
717
|
|
|
718
|
+
/**
|
|
719
|
+
* @param {import('prosemirror-model').Node} doc
|
|
720
|
+
*/
|
|
633
721
|
_prosemirrorChanged (doc) {
|
|
634
722
|
this.doc.transact(() => {
|
|
635
|
-
updateYFragment(this.doc, this.type, doc, this
|
|
723
|
+
updateYFragment(this.doc, this.type, doc, this);
|
|
636
724
|
this.beforeTransactionSelection = getRelativeSelection(
|
|
637
725
|
this,
|
|
638
726
|
this.prosemirrorView.state
|
|
@@ -665,7 +753,7 @@ class ProsemirrorBinding {
|
|
|
665
753
|
* @private
|
|
666
754
|
* @param {Y.XmlElement | Y.XmlHook} el
|
|
667
755
|
* @param {PModel.Schema} schema
|
|
668
|
-
* @param {
|
|
756
|
+
* @param {BindingMetadata} meta
|
|
669
757
|
* @param {Y.Snapshot} [snapshot]
|
|
670
758
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
671
759
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -674,18 +762,18 @@ class ProsemirrorBinding {
|
|
|
674
762
|
const createNodeIfNotExists = (
|
|
675
763
|
el,
|
|
676
764
|
schema,
|
|
677
|
-
|
|
765
|
+
meta,
|
|
678
766
|
snapshot,
|
|
679
767
|
prevSnapshot,
|
|
680
768
|
computeYChange
|
|
681
769
|
) => {
|
|
682
|
-
const node = /** @type {PModel.Node} */ (mapping.get(el));
|
|
770
|
+
const node = /** @type {PModel.Node} */ (meta.mapping.get(el));
|
|
683
771
|
if (node === undefined) {
|
|
684
772
|
if (el instanceof Y__namespace.XmlElement) {
|
|
685
773
|
return createNodeFromYElement(
|
|
686
774
|
el,
|
|
687
775
|
schema,
|
|
688
|
-
|
|
776
|
+
meta,
|
|
689
777
|
snapshot,
|
|
690
778
|
prevSnapshot,
|
|
691
779
|
computeYChange
|
|
@@ -701,7 +789,7 @@ const createNodeIfNotExists = (
|
|
|
701
789
|
* @private
|
|
702
790
|
* @param {Y.XmlElement} el
|
|
703
791
|
* @param {any} schema
|
|
704
|
-
* @param {
|
|
792
|
+
* @param {BindingMetadata} meta
|
|
705
793
|
* @param {Y.Snapshot} [snapshot]
|
|
706
794
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
707
795
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -710,18 +798,21 @@ const createNodeIfNotExists = (
|
|
|
710
798
|
const createNodeFromYElement = (
|
|
711
799
|
el,
|
|
712
800
|
schema,
|
|
713
|
-
|
|
801
|
+
meta,
|
|
714
802
|
snapshot,
|
|
715
803
|
prevSnapshot,
|
|
716
804
|
computeYChange
|
|
717
805
|
) => {
|
|
718
806
|
const children = [];
|
|
807
|
+
/**
|
|
808
|
+
* @param {Y.XmlElement | Y.XmlText} type
|
|
809
|
+
*/
|
|
719
810
|
const createChildren = (type) => {
|
|
720
|
-
if (type
|
|
811
|
+
if (type instanceof Y__namespace.XmlElement) {
|
|
721
812
|
const n = createNodeIfNotExists(
|
|
722
813
|
type,
|
|
723
814
|
schema,
|
|
724
|
-
|
|
815
|
+
meta,
|
|
725
816
|
snapshot,
|
|
726
817
|
prevSnapshot,
|
|
727
818
|
computeYChange
|
|
@@ -733,7 +824,7 @@ const createNodeFromYElement = (
|
|
|
733
824
|
// If the next ytext exists and was created by us, move the content to the current ytext.
|
|
734
825
|
// This is a fix for #160 -- duplication of characters when two Y.Text exist next to each
|
|
735
826
|
// other.
|
|
736
|
-
const nextytext = type._item.right?.content
|
|
827
|
+
const nextytext = /** @type {Y.ContentType} */ (type._item.right?.content)?.type;
|
|
737
828
|
if (nextytext instanceof Y__namespace.Text && !nextytext._item.deleted && nextytext._item.id.client === nextytext.doc.clientID) {
|
|
738
829
|
type.applyDelta([
|
|
739
830
|
{ retain: type.length },
|
|
@@ -747,7 +838,7 @@ const createNodeFromYElement = (
|
|
|
747
838
|
const ns = createTextNodesFromYText(
|
|
748
839
|
type,
|
|
749
840
|
schema,
|
|
750
|
-
|
|
841
|
+
meta,
|
|
751
842
|
snapshot,
|
|
752
843
|
prevSnapshot,
|
|
753
844
|
computeYChange
|
|
@@ -780,32 +871,15 @@ const createNodeFromYElement = (
|
|
|
780
871
|
: { type: 'added' };
|
|
781
872
|
}
|
|
782
873
|
}
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
for (const key in attrs) {
|
|
787
|
-
if (key.startsWith(MarkPrefix)) {
|
|
788
|
-
const markName = key.replace(MarkPrefix, '');
|
|
789
|
-
const markValue = attrs[key];
|
|
790
|
-
if (isObject(markValue)) {
|
|
791
|
-
nodeMarks.push(schema.mark(markName, /** @type {Object} */ (markValue).attrs));
|
|
792
|
-
} else if (Array.isArray(markValue)) {
|
|
793
|
-
nodeMarks.push(...markValue.map(attrs => schema.mark(markName, attrs)));
|
|
794
|
-
}
|
|
795
|
-
} else {
|
|
796
|
-
nodeAttrs[key] = attrs[key];
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const node = schema.node(el.nodeName, nodeAttrs, children, nodeMarks);
|
|
801
|
-
mapping.set(el, node);
|
|
874
|
+
const node = schema.node(el.nodeName, attrs, children);
|
|
875
|
+
meta.mapping.set(el, node);
|
|
802
876
|
return node
|
|
803
877
|
} catch (e) {
|
|
804
878
|
// an error occured while creating the node. This is probably a result of a concurrent action.
|
|
805
879
|
/** @type {Y.Doc} */ (el.doc).transact((transaction) => {
|
|
806
880
|
/** @type {Y.Item} */ (el._item).delete(transaction);
|
|
807
881
|
}, ySyncPluginKey);
|
|
808
|
-
mapping.delete(el);
|
|
882
|
+
meta.mapping.delete(el);
|
|
809
883
|
return null
|
|
810
884
|
}
|
|
811
885
|
};
|
|
@@ -813,8 +887,8 @@ const createNodeFromYElement = (
|
|
|
813
887
|
/**
|
|
814
888
|
* @private
|
|
815
889
|
* @param {Y.XmlText} text
|
|
816
|
-
* @param {
|
|
817
|
-
* @param {
|
|
890
|
+
* @param {import('prosemirror-model').Schema} schema
|
|
891
|
+
* @param {BindingMetadata} _meta
|
|
818
892
|
* @param {Y.Snapshot} [snapshot]
|
|
819
893
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
820
894
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -823,7 +897,7 @@ const createNodeFromYElement = (
|
|
|
823
897
|
const createTextNodesFromYText = (
|
|
824
898
|
text,
|
|
825
899
|
schema,
|
|
826
|
-
|
|
900
|
+
_meta,
|
|
827
901
|
snapshot,
|
|
828
902
|
prevSnapshot,
|
|
829
903
|
computeYChange
|
|
@@ -833,19 +907,7 @@ const createTextNodesFromYText = (
|
|
|
833
907
|
try {
|
|
834
908
|
for (let i = 0; i < deltas.length; i++) {
|
|
835
909
|
const delta = deltas[i];
|
|
836
|
-
|
|
837
|
-
for (const markName in delta.attributes) {
|
|
838
|
-
if (Array.isArray(delta.attributes[markName])) {
|
|
839
|
-
// multiple marks of same type
|
|
840
|
-
delta.attributes[markName].forEach(attrs => {
|
|
841
|
-
marks.push(schema.mark(markName, attrs));
|
|
842
|
-
});
|
|
843
|
-
} else {
|
|
844
|
-
// single mark
|
|
845
|
-
marks.push(schema.mark(markName, delta.attributes[markName]));
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
nodes.push(schema.text(delta.insert, marks));
|
|
910
|
+
nodes.push(schema.text(delta.insert, attributesToMarks(delta.attributes, schema)));
|
|
849
911
|
}
|
|
850
912
|
} catch (e) {
|
|
851
913
|
// an error occured while creating the node. This is probably a result of a concurrent action.
|
|
@@ -861,67 +923,70 @@ const createTextNodesFromYText = (
|
|
|
861
923
|
/**
|
|
862
924
|
* @private
|
|
863
925
|
* @param {Array<any>} nodes prosemirror node
|
|
864
|
-
* @param {
|
|
926
|
+
* @param {BindingMetadata} meta
|
|
865
927
|
* @return {Y.XmlText}
|
|
866
928
|
*/
|
|
867
|
-
const createTypeFromTextNodes = (nodes,
|
|
929
|
+
const createTypeFromTextNodes = (nodes, meta) => {
|
|
868
930
|
const type = new Y__namespace.XmlText();
|
|
869
931
|
const delta = nodes.map((node) => ({
|
|
870
932
|
// @ts-ignore
|
|
871
933
|
insert: node.text,
|
|
872
|
-
attributes: marksToAttributes(node.marks)
|
|
934
|
+
attributes: marksToAttributes(node.marks, meta)
|
|
873
935
|
}));
|
|
874
936
|
type.applyDelta(delta);
|
|
875
|
-
mapping.set(type, nodes);
|
|
937
|
+
meta.mapping.set(type, nodes);
|
|
876
938
|
return type
|
|
877
939
|
};
|
|
878
940
|
|
|
879
941
|
/**
|
|
880
942
|
* @private
|
|
881
943
|
* @param {any} node prosemirror node
|
|
882
|
-
* @param {
|
|
944
|
+
* @param {BindingMetadata} meta
|
|
883
945
|
* @return {Y.XmlElement}
|
|
884
946
|
*/
|
|
885
|
-
const createTypeFromElementNode = (node,
|
|
947
|
+
const createTypeFromElementNode = (node, meta) => {
|
|
886
948
|
const type = new Y__namespace.XmlElement(node.type.name);
|
|
887
|
-
const nodeMarksAttr = nodeMarksToAttributes(node.marks);
|
|
888
949
|
for (const key in node.attrs) {
|
|
889
950
|
const val = node.attrs[key];
|
|
890
951
|
if (val !== null && key !== 'ychange') {
|
|
891
952
|
type.setAttribute(key, val);
|
|
892
953
|
}
|
|
893
954
|
}
|
|
894
|
-
for (const key in nodeMarksAttr) {
|
|
895
|
-
type.setAttribute(key, nodeMarksAttr[key]);
|
|
896
|
-
}
|
|
897
955
|
type.insert(
|
|
898
956
|
0,
|
|
899
957
|
normalizePNodeContent(node).map((n) =>
|
|
900
|
-
createTypeFromTextOrElementNode(n,
|
|
958
|
+
createTypeFromTextOrElementNode(n, meta)
|
|
901
959
|
)
|
|
902
960
|
);
|
|
903
|
-
mapping.set(type, node);
|
|
961
|
+
meta.mapping.set(type, node);
|
|
904
962
|
return type
|
|
905
963
|
};
|
|
906
964
|
|
|
907
965
|
/**
|
|
908
966
|
* @private
|
|
909
967
|
* @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
|
|
910
|
-
* @param {
|
|
968
|
+
* @param {BindingMetadata} meta
|
|
911
969
|
* @return {Y.XmlElement|Y.XmlText}
|
|
912
970
|
*/
|
|
913
|
-
const createTypeFromTextOrElementNode = (node,
|
|
971
|
+
const createTypeFromTextOrElementNode = (node, meta) =>
|
|
914
972
|
node instanceof Array
|
|
915
|
-
? createTypeFromTextNodes(node,
|
|
916
|
-
: createTypeFromElementNode(node,
|
|
973
|
+
? createTypeFromTextNodes(node, meta)
|
|
974
|
+
: createTypeFromElementNode(node, meta);
|
|
917
975
|
|
|
976
|
+
/**
|
|
977
|
+
* @param {any} val
|
|
978
|
+
*/
|
|
918
979
|
const isObject = (val) => typeof val === 'object' && val !== null;
|
|
919
980
|
|
|
981
|
+
/**
|
|
982
|
+
* @param {any} pattrs
|
|
983
|
+
* @param {any} yattrs
|
|
984
|
+
*/
|
|
920
985
|
const equalAttrs = (pattrs, yattrs) => {
|
|
921
986
|
const keys = Object.keys(pattrs).filter((key) => pattrs[key] !== null);
|
|
922
987
|
let eq =
|
|
923
988
|
keys.length ===
|
|
924
|
-
|
|
989
|
+
Object.keys(yattrs).filter((key) => yattrs[key] !== null).length;
|
|
925
990
|
for (let i = 0; i < keys.length && eq; i++) {
|
|
926
991
|
const key = keys[i];
|
|
927
992
|
const l = pattrs[key];
|
|
@@ -932,20 +997,6 @@ const equalAttrs = (pattrs, yattrs) => {
|
|
|
932
997
|
return eq
|
|
933
998
|
};
|
|
934
999
|
|
|
935
|
-
const equalMarks = (pmarks, yattrs) => {
|
|
936
|
-
const keys = Object.keys(yattrs).filter((key) => key.startsWith(MarkPrefix));
|
|
937
|
-
let eq =
|
|
938
|
-
keys.length === pmarks.length;
|
|
939
|
-
const pMarkAttr = nodeMarksToAttributes(pmarks);
|
|
940
|
-
for (let i = 0; i < keys.length && eq; i++) {
|
|
941
|
-
const key = keys[i];
|
|
942
|
-
const l = pMarkAttr[key];
|
|
943
|
-
const r = yattrs[key];
|
|
944
|
-
eq = key === 'ychange' || f__namespace.equalityDeep(l, r);
|
|
945
|
-
}
|
|
946
|
-
return eq
|
|
947
|
-
};
|
|
948
|
-
|
|
949
1000
|
/**
|
|
950
1001
|
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
|
|
951
1002
|
*/
|
|
@@ -980,17 +1031,14 @@ const normalizePNodeContent = (pnode) => {
|
|
|
980
1031
|
const equalYTextPText = (ytext, ptexts) => {
|
|
981
1032
|
const delta = ytext.toDelta();
|
|
982
1033
|
return delta.length === ptexts.length &&
|
|
983
|
-
delta.every((d, i) =>
|
|
1034
|
+
delta.every(/** @type {(d:any,i:number) => boolean} */ (d, i) =>
|
|
984
1035
|
d.insert === /** @type {any} */ (ptexts[i]).text &&
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
return equalAttrs(mark.attrs, yattrs)
|
|
992
|
-
}
|
|
993
|
-
)
|
|
1036
|
+
object__namespace.keys(d.attributes || {}).length === ptexts[i].marks.length &&
|
|
1037
|
+
object__namespace.every(d.attributes, (attr, yattrname) => {
|
|
1038
|
+
const markname = yattr2markname(yattrname);
|
|
1039
|
+
const pmarks = ptexts[i].marks;
|
|
1040
|
+
return equalAttrs(attr, pmarks.find(/** @param {any} mark */ mark => mark.type.name === markname)?.attrs)
|
|
1041
|
+
})
|
|
994
1042
|
)
|
|
995
1043
|
};
|
|
996
1044
|
|
|
@@ -1005,8 +1053,7 @@ const equalYTypePNode = (ytype, pnode) => {
|
|
|
1005
1053
|
) {
|
|
1006
1054
|
const normalizedContent = normalizePNodeContent(pnode);
|
|
1007
1055
|
return ytype._length === normalizedContent.length &&
|
|
1008
|
-
equalAttrs(
|
|
1009
|
-
equalMarks(pnode.marks, ytype.getAttributes()) &&
|
|
1056
|
+
equalAttrs(ytype.getAttributes(), pnode.attrs) &&
|
|
1010
1057
|
ytype.toArray().every((ychild, i) =>
|
|
1011
1058
|
equalYTypePNode(ychild, normalizedContent[i])
|
|
1012
1059
|
)
|
|
@@ -1029,10 +1076,10 @@ const mappedIdentity = (mapped, pcontent) =>
|
|
|
1029
1076
|
/**
|
|
1030
1077
|
* @param {Y.XmlElement} ytype
|
|
1031
1078
|
* @param {PModel.Node} pnode
|
|
1032
|
-
* @param {
|
|
1079
|
+
* @param {BindingMetadata} meta
|
|
1033
1080
|
* @return {{ foundMappedChild: boolean, equalityFactor: number }}
|
|
1034
1081
|
*/
|
|
1035
|
-
const computeChildEqualityFactor = (ytype, pnode,
|
|
1082
|
+
const computeChildEqualityFactor = (ytype, pnode, meta) => {
|
|
1036
1083
|
const yChildren = ytype.toArray();
|
|
1037
1084
|
const pChildren = normalizePNodeContent(pnode);
|
|
1038
1085
|
const pChildCnt = pChildren.length;
|
|
@@ -1044,7 +1091,7 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1044
1091
|
for (; left < minCnt; left++) {
|
|
1045
1092
|
const leftY = yChildren[left];
|
|
1046
1093
|
const leftP = pChildren[left];
|
|
1047
|
-
if (mappedIdentity(mapping.get(leftY), leftP)) {
|
|
1094
|
+
if (mappedIdentity(meta.mapping.get(leftY), leftP)) {
|
|
1048
1095
|
foundMappedChild = true; // definite (good) match!
|
|
1049
1096
|
} else if (!equalYTypePNode(leftY, leftP)) {
|
|
1050
1097
|
break
|
|
@@ -1053,7 +1100,7 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1053
1100
|
for (; left + right < minCnt; right++) {
|
|
1054
1101
|
const rightY = yChildren[yChildCnt - right - 1];
|
|
1055
1102
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1056
|
-
if (mappedIdentity(mapping.get(rightY), rightP)) {
|
|
1103
|
+
if (mappedIdentity(meta.mapping.get(rightY), rightP)) {
|
|
1057
1104
|
foundMappedChild = true;
|
|
1058
1105
|
} else if (!equalYTypePNode(rightY, rightP)) {
|
|
1059
1106
|
break
|
|
@@ -1065,6 +1112,9 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1065
1112
|
}
|
|
1066
1113
|
};
|
|
1067
1114
|
|
|
1115
|
+
/**
|
|
1116
|
+
* @param {Y.Text} ytext
|
|
1117
|
+
*/
|
|
1068
1118
|
const ytextTrans = (ytext) => {
|
|
1069
1119
|
let str = '';
|
|
1070
1120
|
/**
|
|
@@ -1093,14 +1143,14 @@ const ytextTrans = (ytext) => {
|
|
|
1093
1143
|
*
|
|
1094
1144
|
* @param {Y.Text} ytext
|
|
1095
1145
|
* @param {Array<any>} ptexts
|
|
1096
|
-
* @param {
|
|
1146
|
+
* @param {BindingMetadata} meta
|
|
1097
1147
|
*/
|
|
1098
|
-
const updateYText = (ytext, ptexts,
|
|
1099
|
-
mapping.set(ytext, ptexts);
|
|
1148
|
+
const updateYText = (ytext, ptexts, meta) => {
|
|
1149
|
+
meta.mapping.set(ytext, ptexts);
|
|
1100
1150
|
const { nAttrs, str } = ytextTrans(ytext);
|
|
1101
1151
|
const content = ptexts.map((p) => ({
|
|
1102
1152
|
insert: /** @type {any} */ (p).text,
|
|
1103
|
-
attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks))
|
|
1153
|
+
attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks, meta))
|
|
1104
1154
|
}));
|
|
1105
1155
|
const { insert, remove, index } = diff.simpleDiff(
|
|
1106
1156
|
str,
|
|
@@ -1113,30 +1163,40 @@ const updateYText = (ytext, ptexts, mapping) => {
|
|
|
1113
1163
|
);
|
|
1114
1164
|
};
|
|
1115
1165
|
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1166
|
+
const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/;
|
|
1167
|
+
/**
|
|
1168
|
+
* @param {string} attrName
|
|
1169
|
+
*/
|
|
1170
|
+
const yattr2markname = attrName => hashedMarkNameRegex.exec(attrName)?.[1] ?? attrName;
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* @todo move this to markstoattributes
|
|
1174
|
+
*
|
|
1175
|
+
* @param {Object<string, any>} attrs
|
|
1176
|
+
* @param {import('prosemirror-model').Schema} schema
|
|
1177
|
+
*/
|
|
1178
|
+
const attributesToMarks = (attrs, schema) => {
|
|
1179
|
+
/**
|
|
1180
|
+
* @type {Array<import('prosemirror-model').Mark>}
|
|
1181
|
+
*/
|
|
1182
|
+
const marks = [];
|
|
1183
|
+
for (const markName in attrs) {
|
|
1184
|
+
// remove hashes if necessary
|
|
1185
|
+
marks.push(schema.mark(yattr2markname(markName), attrs[markName]));
|
|
1186
|
+
}
|
|
1187
|
+
return marks
|
|
1133
1188
|
};
|
|
1134
1189
|
|
|
1135
|
-
|
|
1190
|
+
/**
|
|
1191
|
+
* @param {Array<import('prosemirror-model').Mark>} marks
|
|
1192
|
+
* @param {BindingMetadata} meta
|
|
1193
|
+
*/
|
|
1194
|
+
const marksToAttributes = (marks, meta) => {
|
|
1136
1195
|
const pattrs = {};
|
|
1137
1196
|
marks.forEach((mark) => {
|
|
1138
1197
|
if (mark.type.name !== 'ychange') {
|
|
1139
|
-
|
|
1198
|
+
const isOverlapping = map__namespace.setIfUndefined(meta.isOMark, mark.type, () => !mark.type.excludes(mark.type));
|
|
1199
|
+
pattrs[isOverlapping ? `${mark.type.name}--${hashOfJSON(mark.toJSON())}` : mark.type.name] = mark.attrs;
|
|
1140
1200
|
}
|
|
1141
1201
|
});
|
|
1142
1202
|
return pattrs
|
|
@@ -1145,7 +1205,7 @@ const nodeMarksToAttributes = (marks) => {
|
|
|
1145
1205
|
/**
|
|
1146
1206
|
* Update a yDom node by syncing the current content of the prosemirror node.
|
|
1147
1207
|
*
|
|
1148
|
-
* This is a y-
|
|
1208
|
+
* This is a y-prosemirror internal feature that you can use at your own risk.
|
|
1149
1209
|
*
|
|
1150
1210
|
* @private
|
|
1151
1211
|
* @unstable
|
|
@@ -1153,27 +1213,24 @@ const nodeMarksToAttributes = (marks) => {
|
|
|
1153
1213
|
* @param {{transact: Function}} y
|
|
1154
1214
|
* @param {Y.XmlFragment} yDomFragment
|
|
1155
1215
|
* @param {any} pNode
|
|
1156
|
-
* @param {
|
|
1216
|
+
* @param {BindingMetadata} meta
|
|
1157
1217
|
*/
|
|
1158
|
-
const updateYFragment = (y, yDomFragment, pNode,
|
|
1218
|
+
const updateYFragment = (y, yDomFragment, pNode, meta) => {
|
|
1159
1219
|
if (
|
|
1160
1220
|
yDomFragment instanceof Y__namespace.XmlElement &&
|
|
1161
1221
|
yDomFragment.nodeName !== pNode.type.name
|
|
1162
1222
|
) {
|
|
1163
1223
|
throw new Error('node name mismatch!')
|
|
1164
1224
|
}
|
|
1165
|
-
mapping.set(yDomFragment, pNode);
|
|
1225
|
+
meta.mapping.set(yDomFragment, pNode);
|
|
1166
1226
|
// update attributes
|
|
1167
1227
|
if (yDomFragment instanceof Y__namespace.XmlElement) {
|
|
1168
1228
|
const yDomAttrs = yDomFragment.getAttributes();
|
|
1169
1229
|
const pAttrs = pNode.attrs;
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
if (attrs[key] !== null) {
|
|
1175
|
-
if (yDomAttrs[key] !== attrs[key] && key !== 'ychange') {
|
|
1176
|
-
yDomFragment.setAttribute(key, attrs[key]);
|
|
1230
|
+
for (const key in pAttrs) {
|
|
1231
|
+
if (pAttrs[key] !== null) {
|
|
1232
|
+
if (yDomAttrs[key] !== pAttrs[key] && key !== 'ychange') {
|
|
1233
|
+
yDomFragment.setAttribute(key, pAttrs[key]);
|
|
1177
1234
|
}
|
|
1178
1235
|
} else {
|
|
1179
1236
|
yDomFragment.removeAttribute(key);
|
|
@@ -1181,7 +1238,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1181
1238
|
}
|
|
1182
1239
|
// remove all keys that are no longer in pAttrs
|
|
1183
1240
|
for (const key in yDomAttrs) {
|
|
1184
|
-
if (
|
|
1241
|
+
if (pAttrs[key] === undefined) {
|
|
1185
1242
|
yDomFragment.removeAttribute(key);
|
|
1186
1243
|
}
|
|
1187
1244
|
}
|
|
@@ -1198,10 +1255,10 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1198
1255
|
for (; left < minCnt; left++) {
|
|
1199
1256
|
const leftY = yChildren[left];
|
|
1200
1257
|
const leftP = pChildren[left];
|
|
1201
|
-
if (!mappedIdentity(mapping.get(leftY), leftP)) {
|
|
1258
|
+
if (!mappedIdentity(meta.mapping.get(leftY), leftP)) {
|
|
1202
1259
|
if (equalYTypePNode(leftY, leftP)) {
|
|
1203
1260
|
// update mapping
|
|
1204
|
-
mapping.set(leftY, leftP);
|
|
1261
|
+
meta.mapping.set(leftY, leftP);
|
|
1205
1262
|
} else {
|
|
1206
1263
|
break
|
|
1207
1264
|
}
|
|
@@ -1211,10 +1268,10 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1211
1268
|
for (; right + left + 1 < minCnt; right++) {
|
|
1212
1269
|
const rightY = yChildren[yChildCnt - right - 1];
|
|
1213
1270
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1214
|
-
if (!mappedIdentity(mapping.get(rightY), rightP)) {
|
|
1271
|
+
if (!mappedIdentity(meta.mapping.get(rightY), rightP)) {
|
|
1215
1272
|
if (equalYTypePNode(rightY, rightP)) {
|
|
1216
1273
|
// update mapping
|
|
1217
|
-
mapping.set(rightY, rightP);
|
|
1274
|
+
meta.mapping.set(rightY, rightP);
|
|
1218
1275
|
} else {
|
|
1219
1276
|
break
|
|
1220
1277
|
}
|
|
@@ -1229,7 +1286,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1229
1286
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1230
1287
|
if (leftY instanceof Y__namespace.XmlText && leftP instanceof Array) {
|
|
1231
1288
|
if (!equalYTextPText(leftY, leftP)) {
|
|
1232
|
-
updateYText(leftY, leftP,
|
|
1289
|
+
updateYText(leftY, leftP, meta);
|
|
1233
1290
|
}
|
|
1234
1291
|
left += 1;
|
|
1235
1292
|
} else {
|
|
@@ -1242,12 +1299,12 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1242
1299
|
const equalityLeft = computeChildEqualityFactor(
|
|
1243
1300
|
/** @type {Y.XmlElement} */ (leftY),
|
|
1244
1301
|
/** @type {PModel.Node} */ (leftP),
|
|
1245
|
-
|
|
1302
|
+
meta
|
|
1246
1303
|
);
|
|
1247
1304
|
const equalityRight = computeChildEqualityFactor(
|
|
1248
1305
|
/** @type {Y.XmlElement} */ (rightY),
|
|
1249
1306
|
/** @type {PModel.Node} */ (rightP),
|
|
1250
|
-
|
|
1307
|
+
meta
|
|
1251
1308
|
);
|
|
1252
1309
|
if (
|
|
1253
1310
|
equalityLeft.foundMappedChild && !equalityRight.foundMappedChild
|
|
@@ -1270,7 +1327,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1270
1327
|
y,
|
|
1271
1328
|
/** @type {Y.XmlFragment} */ (leftY),
|
|
1272
1329
|
/** @type {PModel.Node} */ (leftP),
|
|
1273
|
-
|
|
1330
|
+
meta
|
|
1274
1331
|
);
|
|
1275
1332
|
left += 1;
|
|
1276
1333
|
} else if (updateRight) {
|
|
@@ -1278,14 +1335,14 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1278
1335
|
y,
|
|
1279
1336
|
/** @type {Y.XmlFragment} */ (rightY),
|
|
1280
1337
|
/** @type {PModel.Node} */ (rightP),
|
|
1281
|
-
|
|
1338
|
+
meta
|
|
1282
1339
|
);
|
|
1283
1340
|
right += 1;
|
|
1284
1341
|
} else {
|
|
1285
|
-
mapping.delete(yDomFragment.get(left));
|
|
1342
|
+
meta.mapping.delete(yDomFragment.get(left));
|
|
1286
1343
|
yDomFragment.delete(left, 1);
|
|
1287
1344
|
yDomFragment.insert(left, [
|
|
1288
|
-
createTypeFromTextOrElementNode(leftP,
|
|
1345
|
+
createTypeFromTextOrElementNode(leftP, meta)
|
|
1289
1346
|
]);
|
|
1290
1347
|
left += 1;
|
|
1291
1348
|
}
|
|
@@ -1295,18 +1352,18 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1295
1352
|
if (
|
|
1296
1353
|
yChildCnt === 1 && pChildCnt === 0 && yChildren[0] instanceof Y__namespace.XmlText
|
|
1297
1354
|
) {
|
|
1298
|
-
mapping.delete(yChildren[0]);
|
|
1299
|
-
// Edge case handling https://github.com/yjs/y-
|
|
1355
|
+
meta.mapping.delete(yChildren[0]);
|
|
1356
|
+
// Edge case handling https://github.com/yjs/y-prosemirror/issues/108
|
|
1300
1357
|
// Only delete the content of the Y.Text to retain remote changes on the same Y.Text object
|
|
1301
1358
|
yChildren[0].delete(0, yChildren[0].length);
|
|
1302
1359
|
} else if (yDelLen > 0) {
|
|
1303
|
-
yDomFragment.slice(left, left + yDelLen).forEach(type => mapping.delete(type));
|
|
1360
|
+
yDomFragment.slice(left, left + yDelLen).forEach(type => meta.mapping.delete(type));
|
|
1304
1361
|
yDomFragment.delete(left, yDelLen);
|
|
1305
1362
|
}
|
|
1306
1363
|
if (left + right < pChildCnt) {
|
|
1307
1364
|
const ins = [];
|
|
1308
1365
|
for (let i = left; i < pChildCnt - right; i++) {
|
|
1309
|
-
ins.push(createTypeFromTextOrElementNode(pChildren[i],
|
|
1366
|
+
ins.push(createTypeFromTextOrElementNode(pChildren[i], meta));
|
|
1310
1367
|
}
|
|
1311
1368
|
yDomFragment.insert(left, ins);
|
|
1312
1369
|
}
|
|
@@ -1516,7 +1573,7 @@ const yXmlFragmentToProseMirrorFragment = (yXmlFragment, schema) => {
|
|
|
1516
1573
|
createNodeFromYElement(
|
|
1517
1574
|
/** @type {Y.XmlElement} */ (t),
|
|
1518
1575
|
schema,
|
|
1519
|
-
|
|
1576
|
+
createEmptyMeta()
|
|
1520
1577
|
)
|
|
1521
1578
|
).filter((n) => n !== null);
|
|
1522
1579
|
return PModel.Fragment.fromArray(fragmentContent)
|
|
@@ -1537,21 +1594,20 @@ const yXmlFragmentToProseMirrorRootNode = (yXmlFragment, schema) =>
|
|
|
1537
1594
|
*
|
|
1538
1595
|
* @param {Y.XmlFragment} yXmlFragment
|
|
1539
1596
|
* @param {Schema} schema
|
|
1597
|
+
*
|
|
1598
|
+
* @todo deprecate mapping property
|
|
1540
1599
|
*/
|
|
1541
1600
|
const initProseMirrorDoc = (yXmlFragment, schema) => {
|
|
1542
|
-
|
|
1543
|
-
* @type {ProsemirrorMapping}
|
|
1544
|
-
*/
|
|
1545
|
-
const mapping = new Map();
|
|
1601
|
+
const meta = createEmptyMeta();
|
|
1546
1602
|
const fragmentContent = yXmlFragment.toArray().map((t) =>
|
|
1547
1603
|
createNodeFromYElement(
|
|
1548
1604
|
/** @type {Y.XmlElement} */ (t),
|
|
1549
1605
|
schema,
|
|
1550
|
-
|
|
1606
|
+
meta
|
|
1551
1607
|
)
|
|
1552
1608
|
).filter((n) => n !== null);
|
|
1553
1609
|
const doc = schema.topNodeType.create(null, PModel.Fragment.fromArray(fragmentContent));
|
|
1554
|
-
return { doc, mapping }
|
|
1610
|
+
return { doc, meta, mapping: meta.mapping }
|
|
1555
1611
|
};
|
|
1556
1612
|
|
|
1557
1613
|
/**
|
|
@@ -1594,7 +1650,7 @@ function prosemirrorToYDoc (doc, xmlFragment = 'prosemirror') {
|
|
|
1594
1650
|
function prosemirrorToYXmlFragment (doc, xmlFragment) {
|
|
1595
1651
|
const type = xmlFragment || new Y__namespace.XmlFragment();
|
|
1596
1652
|
const ydoc = type.doc ? type.doc : { transact: (transaction) => transaction(undefined) };
|
|
1597
|
-
updateYFragment(ydoc, type, doc, new Map());
|
|
1653
|
+
updateYFragment(ydoc, type, doc, { mapping: new Map(), isOMark: new Map() });
|
|
1598
1654
|
return type
|
|
1599
1655
|
}
|
|
1600
1656
|
|
|
@@ -1690,7 +1746,10 @@ function yDocToProsemirrorJSON (
|
|
|
1690
1746
|
function yXmlFragmentToProsemirrorJSON (xmlFragment) {
|
|
1691
1747
|
const items = xmlFragment.toArray();
|
|
1692
1748
|
|
|
1693
|
-
|
|
1749
|
+
/**
|
|
1750
|
+
* @param {Y.AbstractType} item
|
|
1751
|
+
*/
|
|
1752
|
+
const serialize = item => {
|
|
1694
1753
|
/**
|
|
1695
1754
|
* @type {Object} NodeObject
|
|
1696
1755
|
* @property {string} NodeObject.type
|
|
@@ -1700,79 +1759,49 @@ function yXmlFragmentToProsemirrorJSON (xmlFragment) {
|
|
|
1700
1759
|
let response;
|
|
1701
1760
|
|
|
1702
1761
|
// TODO: Must be a better way to detect text nodes than this
|
|
1703
|
-
if (
|
|
1762
|
+
if (item instanceof Y__namespace.XmlText) {
|
|
1704
1763
|
const delta = item.toDelta();
|
|
1705
|
-
response = delta.map((d) => {
|
|
1764
|
+
response = delta.map(/** @param {any} d */ (d) => {
|
|
1706
1765
|
const text = {
|
|
1707
1766
|
type: 'text',
|
|
1708
1767
|
text: d.insert
|
|
1709
1768
|
};
|
|
1710
|
-
|
|
1711
1769
|
if (d.attributes) {
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
type
|
|
1721
|
-
};
|
|
1722
|
-
|
|
1723
|
-
if (Object.keys(singleAttrs)) {
|
|
1724
|
-
mark.attrs = singleAttrs;
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
marks.push(mark);
|
|
1728
|
-
});
|
|
1729
|
-
} else {
|
|
1730
|
-
const mark = {
|
|
1731
|
-
type
|
|
1732
|
-
};
|
|
1733
|
-
|
|
1734
|
-
if (Object.keys(attrs)) {
|
|
1735
|
-
mark.attrs = attrs;
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
marks.push(mark);
|
|
1770
|
+
text.marks = Object.keys(d.attributes).map((type_) => {
|
|
1771
|
+
const attrs = d.attributes[type_];
|
|
1772
|
+
const type = yattr2markname(type_);
|
|
1773
|
+
const mark = {
|
|
1774
|
+
type
|
|
1775
|
+
};
|
|
1776
|
+
if (Object.keys(attrs)) {
|
|
1777
|
+
mark.attrs = attrs;
|
|
1739
1778
|
}
|
|
1779
|
+
return mark
|
|
1740
1780
|
});
|
|
1741
|
-
|
|
1742
|
-
text.marks = marks;
|
|
1743
1781
|
}
|
|
1744
1782
|
return text
|
|
1745
1783
|
});
|
|
1746
|
-
} else {
|
|
1784
|
+
} else if (item instanceof Y__namespace.XmlElement) {
|
|
1747
1785
|
response = {
|
|
1748
1786
|
type: item.nodeName
|
|
1749
1787
|
};
|
|
1750
1788
|
|
|
1751
1789
|
const attrs = item.getAttributes();
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
for (const key of Object.keys(attrs).filter((key) => !key.startsWith(MarkPrefix))) {
|
|
1755
|
-
if (!response.attrs) response.attrs = {};
|
|
1756
|
-
response.attrs[key] = attrs[key];
|
|
1757
|
-
}
|
|
1758
|
-
|
|
1759
|
-
// Check whether the attrs contains marks, if so, add them to the response
|
|
1760
|
-
if (Object.keys(attrs).some((key) => key.startsWith(MarkPrefix))) {
|
|
1761
|
-
response.marks = Object.keys(attrs)
|
|
1762
|
-
.filter((key) => key.startsWith(MarkPrefix))
|
|
1763
|
-
.map((key) => {
|
|
1764
|
-
return attrs[key]
|
|
1765
|
-
});
|
|
1790
|
+
if (Object.keys(attrs).length) {
|
|
1791
|
+
response.attrs = attrs;
|
|
1766
1792
|
}
|
|
1767
1793
|
|
|
1768
1794
|
const children = item.toArray();
|
|
1769
1795
|
if (children.length) {
|
|
1770
1796
|
response.content = children.map(serialize).flat();
|
|
1771
1797
|
}
|
|
1798
|
+
} else {
|
|
1799
|
+
// expected either Y.XmlElement or Y.XmlText
|
|
1800
|
+
error__namespace.unexpectedCase();
|
|
1772
1801
|
}
|
|
1773
1802
|
|
|
1774
1803
|
return response
|
|
1775
|
-
}
|
|
1804
|
+
};
|
|
1776
1805
|
|
|
1777
1806
|
return {
|
|
1778
1807
|
type: 'doc',
|
|
@@ -1815,7 +1844,7 @@ const defaultCursorBuilder = (user) => {
|
|
|
1815
1844
|
* Default generator for the selection attributes
|
|
1816
1845
|
*
|
|
1817
1846
|
* @param {any} user user data
|
|
1818
|
-
* @return {import('
|
|
1847
|
+
* @return {import('prosemirror-view').DecorationAttrs}
|
|
1819
1848
|
*/
|
|
1820
1849
|
const defaultSelectionBuilder = (user) => {
|
|
1821
1850
|
return {
|
|
@@ -1830,8 +1859,8 @@ const rxValidColor = /^#[0-9a-fA-F]{6}$/;
|
|
|
1830
1859
|
* @param {any} state
|
|
1831
1860
|
* @param {Awareness} awareness
|
|
1832
1861
|
* @param {function(number, number, any):boolean} awarenessFilter
|
|
1833
|
-
* @param {
|
|
1834
|
-
* @param {
|
|
1862
|
+
* @param {(user: { name: string, color: string }, clientId: number) => Element} createCursor
|
|
1863
|
+
* @param {(user: { name: string, color: string }, clientId: number) => import('prosemirror-view').DecorationAttrs} createSelection
|
|
1835
1864
|
* @return {any} DecorationSet
|
|
1836
1865
|
*/
|
|
1837
1866
|
const createDecorations = (
|
|
@@ -1849,7 +1878,7 @@ const createDecorations = (
|
|
|
1849
1878
|
ystate.binding.mapping.size === 0
|
|
1850
1879
|
) {
|
|
1851
1880
|
// do not render cursors while snapshot is active
|
|
1852
|
-
return
|
|
1881
|
+
return prosemirrorView.DecorationSet.create(state.doc, [])
|
|
1853
1882
|
}
|
|
1854
1883
|
awareness.getStates().forEach((aw, clientId) => {
|
|
1855
1884
|
if (!awarenessFilter(y.clientID, clientId, aw)) {
|
|
@@ -1861,7 +1890,7 @@ const createDecorations = (
|
|
|
1861
1890
|
if (user.color == null) {
|
|
1862
1891
|
user.color = '#ffa500';
|
|
1863
1892
|
} else if (!rxValidColor.test(user.color)) {
|
|
1864
|
-
// We only support 6-digit RGB colors in y-
|
|
1893
|
+
// We only support 6-digit RGB colors in y-prosemirror
|
|
1865
1894
|
console.warn('A user uses an unsupported color format', user);
|
|
1866
1895
|
}
|
|
1867
1896
|
if (user.name == null) {
|
|
@@ -1884,7 +1913,7 @@ const createDecorations = (
|
|
|
1884
1913
|
anchor = math__namespace.min(anchor, maxsize);
|
|
1885
1914
|
head = math__namespace.min(head, maxsize);
|
|
1886
1915
|
decorations.push(
|
|
1887
|
-
|
|
1916
|
+
prosemirrorView.Decoration.widget(head, () => createCursor(user, clientId), {
|
|
1888
1917
|
key: clientId + '',
|
|
1889
1918
|
side: 10
|
|
1890
1919
|
})
|
|
@@ -1892,7 +1921,7 @@ const createDecorations = (
|
|
|
1892
1921
|
const from = math__namespace.min(anchor, head);
|
|
1893
1922
|
const to = math__namespace.max(anchor, head);
|
|
1894
1923
|
decorations.push(
|
|
1895
|
-
|
|
1924
|
+
prosemirrorView.Decoration.inline(from, to, createSelection(user, clientId), {
|
|
1896
1925
|
inclusiveEnd: true,
|
|
1897
1926
|
inclusiveStart: false
|
|
1898
1927
|
})
|
|
@@ -1900,7 +1929,7 @@ const createDecorations = (
|
|
|
1900
1929
|
}
|
|
1901
1930
|
}
|
|
1902
1931
|
});
|
|
1903
|
-
return
|
|
1932
|
+
return prosemirrorView.DecorationSet.create(state.doc, decorations)
|
|
1904
1933
|
};
|
|
1905
1934
|
|
|
1906
1935
|
/**
|
|
@@ -1911,8 +1940,8 @@ const createDecorations = (
|
|
|
1911
1940
|
* @param {Awareness} awareness
|
|
1912
1941
|
* @param {object} opts
|
|
1913
1942
|
* @param {function(any, any, any):boolean} [opts.awarenessStateFilter]
|
|
1914
|
-
* @param {
|
|
1915
|
-
* @param {
|
|
1943
|
+
* @param {(user: any, clientId: number) => HTMLElement} [opts.cursorBuilder]
|
|
1944
|
+
* @param {(user: any, clientId: number) => import('prosemirror-view').DecorationAttrs} [opts.selectionBuilder]
|
|
1916
1945
|
* @param {function(any):any} [opts.getSelection]
|
|
1917
1946
|
* @param {string} [cursorStateField] By default all editor bindings use the awareness 'cursor' field to propagate cursor information.
|
|
1918
1947
|
* @return {any}
|
|
@@ -1927,7 +1956,7 @@ const yCursorPlugin = (
|
|
|
1927
1956
|
} = {},
|
|
1928
1957
|
cursorStateField = 'cursor'
|
|
1929
1958
|
) =>
|
|
1930
|
-
new
|
|
1959
|
+
new prosemirrorState.Plugin({
|
|
1931
1960
|
key: yCursorPluginKey,
|
|
1932
1961
|
state: {
|
|
1933
1962
|
init (_, state) {
|