@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.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
2
|
import { Item, ContentType, Text, XmlElement, UndoManager } from 'yjs';
|
|
3
|
-
import { DecorationSet, Decoration } from '
|
|
4
|
-
import {
|
|
3
|
+
import { DecorationSet, Decoration } from 'prosemirror-view';
|
|
4
|
+
import { Plugin, TextSelection, AllSelection, NodeSelection } from 'prosemirror-state';
|
|
5
5
|
import 'y-protocols/awareness';
|
|
6
6
|
import { createMutex } from 'lib0/mutex';
|
|
7
|
-
import * as PModel from '
|
|
8
|
-
import { Fragment, Node } from '
|
|
7
|
+
import * as PModel from 'prosemirror-model';
|
|
8
|
+
import { Fragment, Node } from 'prosemirror-model';
|
|
9
9
|
import * as math from 'lib0/math';
|
|
10
|
+
import * as object from 'lib0/object';
|
|
10
11
|
import * as set from 'lib0/set';
|
|
11
12
|
import { simpleDiff } from 'lib0/diff';
|
|
12
13
|
import * as error from 'lib0/error';
|
|
14
|
+
import { PluginKey, Plugin as Plugin$1 } from '@tiptap/pm/state';
|
|
13
15
|
import * as random from 'lib0/random';
|
|
14
16
|
import * as environment from 'lib0/environment';
|
|
15
17
|
import * as dom from 'lib0/dom';
|
|
16
18
|
import * as eventloop from 'lib0/eventloop';
|
|
17
|
-
import * as f from 'lib0/function';
|
|
18
19
|
import * as map from 'lib0/map';
|
|
20
|
+
import * as sha256 from 'lib0/hash/sha256';
|
|
21
|
+
import * as buf from 'lib0/buffer';
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* The unique prosemirror plugin key for syncPlugin
|
|
@@ -38,11 +41,41 @@ const yUndoPluginKey = new PluginKey('y-undo');
|
|
|
38
41
|
*/
|
|
39
42
|
const yCursorPluginKey = new PluginKey('yjs-cursor');
|
|
40
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Custom function to transform sha256 hash to N byte
|
|
46
|
+
*
|
|
47
|
+
* @param {Uint8Array} digest
|
|
48
|
+
*/
|
|
49
|
+
const _convolute = digest => {
|
|
50
|
+
const N = 6;
|
|
51
|
+
for (let i = N; i < digest.length; i++) {
|
|
52
|
+
digest[i % N] = digest[i % N] ^ digest[i];
|
|
53
|
+
}
|
|
54
|
+
return digest.slice(0, N)
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @param {any} json
|
|
59
|
+
*/
|
|
60
|
+
const hashOfJSON = (json) => buf.toBase64(_convolute(sha256.digest(buf.encodeAny(json))));
|
|
61
|
+
|
|
41
62
|
/**
|
|
42
63
|
* @module bindings/prosemirror
|
|
43
64
|
*/
|
|
44
65
|
|
|
45
|
-
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {Object} BindingMetadata
|
|
68
|
+
* @property {ProsemirrorMapping} BindingMetadata.mapping
|
|
69
|
+
* @property {Map<import('prosemirror-model').MarkType, boolean>} BindingMetadata.isOMark - is overlapping mark
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @return BindingMetadata
|
|
74
|
+
*/
|
|
75
|
+
const createEmptyMeta = () => ({
|
|
76
|
+
mapping: new Map(),
|
|
77
|
+
isOMark: new Map()
|
|
78
|
+
});
|
|
46
79
|
|
|
47
80
|
/**
|
|
48
81
|
* @param {Y.Item} item
|
|
@@ -249,31 +282,48 @@ const ySyncPlugin = (yXmlFragment, {
|
|
|
249
282
|
};
|
|
250
283
|
|
|
251
284
|
/**
|
|
252
|
-
* @param {
|
|
253
|
-
* @param {
|
|
285
|
+
* @param {import('prosemirror-state').Transaction} tr
|
|
286
|
+
* @param {ReturnType<typeof getRelativeSelection>} relSel
|
|
254
287
|
* @param {ProsemirrorBinding} binding
|
|
255
288
|
*/
|
|
256
289
|
const restoreRelativeSelection = (tr, relSel, binding) => {
|
|
257
290
|
if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
291
|
+
if (relSel.type === 'all') {
|
|
292
|
+
tr.setSelection(new AllSelection(tr.doc));
|
|
293
|
+
} else if (relSel.type === 'node') {
|
|
294
|
+
const anchor = relativePositionToAbsolutePosition(
|
|
295
|
+
binding.doc,
|
|
296
|
+
binding.type,
|
|
297
|
+
relSel.anchor,
|
|
298
|
+
binding.mapping
|
|
299
|
+
);
|
|
300
|
+
tr.setSelection(NodeSelection.create(tr.doc, anchor));
|
|
301
|
+
} else {
|
|
302
|
+
const anchor = relativePositionToAbsolutePosition(
|
|
303
|
+
binding.doc,
|
|
304
|
+
binding.type,
|
|
305
|
+
relSel.anchor,
|
|
306
|
+
binding.mapping
|
|
307
|
+
);
|
|
308
|
+
const head = relativePositionToAbsolutePosition(
|
|
309
|
+
binding.doc,
|
|
310
|
+
binding.type,
|
|
311
|
+
relSel.head,
|
|
312
|
+
binding.mapping
|
|
313
|
+
);
|
|
314
|
+
if (anchor !== null && head !== null) {
|
|
315
|
+
tr.setSelection(TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head)));
|
|
316
|
+
}
|
|
272
317
|
}
|
|
273
318
|
}
|
|
274
319
|
};
|
|
275
320
|
|
|
321
|
+
/**
|
|
322
|
+
* @param {ProsemirrorBinding} pmbinding
|
|
323
|
+
* @param {import('prosemirror-state').EditorState} state
|
|
324
|
+
*/
|
|
276
325
|
const getRelativeSelection = (pmbinding, state) => ({
|
|
326
|
+
type: /** @type {any} */ (state.selection).jsonID,
|
|
277
327
|
anchor: absolutePositionToRelativePosition(
|
|
278
328
|
state.selection.anchor,
|
|
279
329
|
pmbinding.type,
|
|
@@ -305,6 +355,12 @@ class ProsemirrorBinding {
|
|
|
305
355
|
this.prosemirrorView = null;
|
|
306
356
|
this.mux = createMutex();
|
|
307
357
|
this.mapping = mapping;
|
|
358
|
+
/**
|
|
359
|
+
* Is overlapping mark - i.e. mark does not exclude itself.
|
|
360
|
+
*
|
|
361
|
+
* @type {Map<import('prosemirror-model').MarkType, boolean>}
|
|
362
|
+
*/
|
|
363
|
+
this.isOMark = new Map();
|
|
308
364
|
this._observeFunction = this._typeChanged.bind(this);
|
|
309
365
|
/**
|
|
310
366
|
* @type {Y.Doc}
|
|
@@ -399,7 +455,7 @@ class ProsemirrorBinding {
|
|
|
399
455
|
createNodeFromYElement(
|
|
400
456
|
/** @type {Y.XmlElement} */ (t),
|
|
401
457
|
this.prosemirrorView.state.schema,
|
|
402
|
-
this
|
|
458
|
+
this
|
|
403
459
|
)
|
|
404
460
|
).filter((n) => n !== null);
|
|
405
461
|
// @ts-ignore
|
|
@@ -424,7 +480,7 @@ class ProsemirrorBinding {
|
|
|
424
480
|
createNodeFromYElement(
|
|
425
481
|
/** @type {Y.XmlElement} */ (t),
|
|
426
482
|
this.prosemirrorView.state.schema,
|
|
427
|
-
this
|
|
483
|
+
this
|
|
428
484
|
)
|
|
429
485
|
).filter((n) => n !== null);
|
|
430
486
|
// @ts-ignore
|
|
@@ -461,6 +517,7 @@ class ProsemirrorBinding {
|
|
|
461
517
|
* @type {Y.Doc}
|
|
462
518
|
*/
|
|
463
519
|
let historyDoc = this.doc;
|
|
520
|
+
let historyType = this.type;
|
|
464
521
|
if (!snapshot) {
|
|
465
522
|
snapshot = Y.snapshot(this.doc);
|
|
466
523
|
}
|
|
@@ -474,6 +531,29 @@ class ProsemirrorBinding {
|
|
|
474
531
|
prevSnapshot = Y.snapshot(historyDoc);
|
|
475
532
|
Y.applyUpdateV2(historyDoc, snapshot);
|
|
476
533
|
snapshot = Y.snapshot(historyDoc);
|
|
534
|
+
if (historyType._item === null) {
|
|
535
|
+
/**
|
|
536
|
+
* If is a root type, we need to find the root key in the initial document
|
|
537
|
+
* and use it to get the history type.
|
|
538
|
+
*/
|
|
539
|
+
const rootKey = Array.from(this.doc.share.keys()).find(
|
|
540
|
+
(key) => this.doc.share.get(key) === this.type
|
|
541
|
+
);
|
|
542
|
+
historyType = historyDoc.getXmlFragment(rootKey);
|
|
543
|
+
} else {
|
|
544
|
+
/**
|
|
545
|
+
* If it is a sub type, we use the item id to find the history type.
|
|
546
|
+
*/
|
|
547
|
+
const historyStructs =
|
|
548
|
+
historyDoc.store.clients.get(historyType._item.id.client) ?? [];
|
|
549
|
+
const itemIndex = Y.findIndexSS(
|
|
550
|
+
historyStructs,
|
|
551
|
+
historyType._item.id.clock
|
|
552
|
+
);
|
|
553
|
+
const item = /** @type {Y.Item} */ (historyStructs[itemIndex]);
|
|
554
|
+
const content = /** @type {Y.ContentType} */ (item.content);
|
|
555
|
+
historyType = /** @type {Y.XmlFragment} */ (content.type);
|
|
556
|
+
}
|
|
477
557
|
}
|
|
478
558
|
// clear mapping because we are going to rerender
|
|
479
559
|
this.mapping.clear();
|
|
@@ -481,6 +561,9 @@ class ProsemirrorBinding {
|
|
|
481
561
|
historyDoc.transact((transaction) => {
|
|
482
562
|
// before rendering, we are going to sanitize ops and split deleted ops
|
|
483
563
|
// if they were deleted by seperate users.
|
|
564
|
+
/**
|
|
565
|
+
* @type {Y.PermanentUserData}
|
|
566
|
+
*/
|
|
484
567
|
const pud = pluginState.permanentUserData;
|
|
485
568
|
if (pud) {
|
|
486
569
|
pud.dss.forEach((ds) => {
|
|
@@ -507,7 +590,7 @@ class ProsemirrorBinding {
|
|
|
507
590
|
};
|
|
508
591
|
// Create document fragment and render
|
|
509
592
|
const fragmentContent = Y.typeListToArraySnapshot(
|
|
510
|
-
|
|
593
|
+
historyType,
|
|
511
594
|
new Y.Snapshot(prevSnapshot.ds, snapshot.sv)
|
|
512
595
|
).map((t) => {
|
|
513
596
|
if (
|
|
@@ -517,7 +600,7 @@ class ProsemirrorBinding {
|
|
|
517
600
|
return createNodeFromYElement(
|
|
518
601
|
t,
|
|
519
602
|
this.prosemirrorView.state.schema,
|
|
520
|
-
new Map(),
|
|
603
|
+
{ mapping: new Map(), isOMark: new Map() },
|
|
521
604
|
snapshot,
|
|
522
605
|
prevSnapshot,
|
|
523
606
|
computeYChange
|
|
@@ -578,7 +661,7 @@ class ProsemirrorBinding {
|
|
|
578
661
|
createNodeIfNotExists(
|
|
579
662
|
/** @type {Y.XmlElement | Y.XmlHook} */ (t),
|
|
580
663
|
this.prosemirrorView.state.schema,
|
|
581
|
-
this
|
|
664
|
+
this
|
|
582
665
|
)
|
|
583
666
|
).filter((n) => n !== null);
|
|
584
667
|
// @ts-ignore
|
|
@@ -598,9 +681,12 @@ class ProsemirrorBinding {
|
|
|
598
681
|
});
|
|
599
682
|
}
|
|
600
683
|
|
|
684
|
+
/**
|
|
685
|
+
* @param {import('prosemirror-model').Node} doc
|
|
686
|
+
*/
|
|
601
687
|
_prosemirrorChanged (doc) {
|
|
602
688
|
this.doc.transact(() => {
|
|
603
|
-
updateYFragment(this.doc, this.type, doc, this
|
|
689
|
+
updateYFragment(this.doc, this.type, doc, this);
|
|
604
690
|
this.beforeTransactionSelection = getRelativeSelection(
|
|
605
691
|
this,
|
|
606
692
|
this.prosemirrorView.state
|
|
@@ -633,7 +719,7 @@ class ProsemirrorBinding {
|
|
|
633
719
|
* @private
|
|
634
720
|
* @param {Y.XmlElement | Y.XmlHook} el
|
|
635
721
|
* @param {PModel.Schema} schema
|
|
636
|
-
* @param {
|
|
722
|
+
* @param {BindingMetadata} meta
|
|
637
723
|
* @param {Y.Snapshot} [snapshot]
|
|
638
724
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
639
725
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -642,18 +728,18 @@ class ProsemirrorBinding {
|
|
|
642
728
|
const createNodeIfNotExists = (
|
|
643
729
|
el,
|
|
644
730
|
schema,
|
|
645
|
-
|
|
731
|
+
meta,
|
|
646
732
|
snapshot,
|
|
647
733
|
prevSnapshot,
|
|
648
734
|
computeYChange
|
|
649
735
|
) => {
|
|
650
|
-
const node = /** @type {PModel.Node} */ (mapping.get(el));
|
|
736
|
+
const node = /** @type {PModel.Node} */ (meta.mapping.get(el));
|
|
651
737
|
if (node === undefined) {
|
|
652
738
|
if (el instanceof Y.XmlElement) {
|
|
653
739
|
return createNodeFromYElement(
|
|
654
740
|
el,
|
|
655
741
|
schema,
|
|
656
|
-
|
|
742
|
+
meta,
|
|
657
743
|
snapshot,
|
|
658
744
|
prevSnapshot,
|
|
659
745
|
computeYChange
|
|
@@ -669,7 +755,7 @@ const createNodeIfNotExists = (
|
|
|
669
755
|
* @private
|
|
670
756
|
* @param {Y.XmlElement} el
|
|
671
757
|
* @param {any} schema
|
|
672
|
-
* @param {
|
|
758
|
+
* @param {BindingMetadata} meta
|
|
673
759
|
* @param {Y.Snapshot} [snapshot]
|
|
674
760
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
675
761
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -678,18 +764,21 @@ const createNodeIfNotExists = (
|
|
|
678
764
|
const createNodeFromYElement = (
|
|
679
765
|
el,
|
|
680
766
|
schema,
|
|
681
|
-
|
|
767
|
+
meta,
|
|
682
768
|
snapshot,
|
|
683
769
|
prevSnapshot,
|
|
684
770
|
computeYChange
|
|
685
771
|
) => {
|
|
686
772
|
const children = [];
|
|
773
|
+
/**
|
|
774
|
+
* @param {Y.XmlElement | Y.XmlText} type
|
|
775
|
+
*/
|
|
687
776
|
const createChildren = (type) => {
|
|
688
|
-
if (type
|
|
777
|
+
if (type instanceof Y.XmlElement) {
|
|
689
778
|
const n = createNodeIfNotExists(
|
|
690
779
|
type,
|
|
691
780
|
schema,
|
|
692
|
-
|
|
781
|
+
meta,
|
|
693
782
|
snapshot,
|
|
694
783
|
prevSnapshot,
|
|
695
784
|
computeYChange
|
|
@@ -701,7 +790,7 @@ const createNodeFromYElement = (
|
|
|
701
790
|
// If the next ytext exists and was created by us, move the content to the current ytext.
|
|
702
791
|
// This is a fix for #160 -- duplication of characters when two Y.Text exist next to each
|
|
703
792
|
// other.
|
|
704
|
-
const nextytext = type._item.right?.content
|
|
793
|
+
const nextytext = /** @type {Y.ContentType} */ (type._item.right?.content)?.type;
|
|
705
794
|
if (nextytext instanceof Y.Text && !nextytext._item.deleted && nextytext._item.id.client === nextytext.doc.clientID) {
|
|
706
795
|
type.applyDelta([
|
|
707
796
|
{ retain: type.length },
|
|
@@ -715,7 +804,7 @@ const createNodeFromYElement = (
|
|
|
715
804
|
const ns = createTextNodesFromYText(
|
|
716
805
|
type,
|
|
717
806
|
schema,
|
|
718
|
-
|
|
807
|
+
meta,
|
|
719
808
|
snapshot,
|
|
720
809
|
prevSnapshot,
|
|
721
810
|
computeYChange
|
|
@@ -748,32 +837,15 @@ const createNodeFromYElement = (
|
|
|
748
837
|
: { type: 'added' };
|
|
749
838
|
}
|
|
750
839
|
}
|
|
751
|
-
const
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
for (const key in attrs) {
|
|
755
|
-
if (key.startsWith(MarkPrefix)) {
|
|
756
|
-
const markName = key.replace(MarkPrefix, '');
|
|
757
|
-
const markValue = attrs[key];
|
|
758
|
-
if (isObject(markValue)) {
|
|
759
|
-
nodeMarks.push(schema.mark(markName, /** @type {Object} */ (markValue).attrs));
|
|
760
|
-
} else if (Array.isArray(markValue)) {
|
|
761
|
-
nodeMarks.push(...markValue.map(attrs => schema.mark(markName, attrs)));
|
|
762
|
-
}
|
|
763
|
-
} else {
|
|
764
|
-
nodeAttrs[key] = attrs[key];
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
const node = schema.node(el.nodeName, nodeAttrs, children, nodeMarks);
|
|
769
|
-
mapping.set(el, node);
|
|
840
|
+
const node = schema.node(el.nodeName, attrs, children);
|
|
841
|
+
meta.mapping.set(el, node);
|
|
770
842
|
return node
|
|
771
843
|
} catch (e) {
|
|
772
844
|
// an error occured while creating the node. This is probably a result of a concurrent action.
|
|
773
845
|
/** @type {Y.Doc} */ (el.doc).transact((transaction) => {
|
|
774
846
|
/** @type {Y.Item} */ (el._item).delete(transaction);
|
|
775
847
|
}, ySyncPluginKey);
|
|
776
|
-
mapping.delete(el);
|
|
848
|
+
meta.mapping.delete(el);
|
|
777
849
|
return null
|
|
778
850
|
}
|
|
779
851
|
};
|
|
@@ -781,8 +853,8 @@ const createNodeFromYElement = (
|
|
|
781
853
|
/**
|
|
782
854
|
* @private
|
|
783
855
|
* @param {Y.XmlText} text
|
|
784
|
-
* @param {
|
|
785
|
-
* @param {
|
|
856
|
+
* @param {import('prosemirror-model').Schema} schema
|
|
857
|
+
* @param {BindingMetadata} _meta
|
|
786
858
|
* @param {Y.Snapshot} [snapshot]
|
|
787
859
|
* @param {Y.Snapshot} [prevSnapshot]
|
|
788
860
|
* @param {function('removed' | 'added', Y.ID):any} [computeYChange]
|
|
@@ -791,7 +863,7 @@ const createNodeFromYElement = (
|
|
|
791
863
|
const createTextNodesFromYText = (
|
|
792
864
|
text,
|
|
793
865
|
schema,
|
|
794
|
-
|
|
866
|
+
_meta,
|
|
795
867
|
snapshot,
|
|
796
868
|
prevSnapshot,
|
|
797
869
|
computeYChange
|
|
@@ -801,19 +873,7 @@ const createTextNodesFromYText = (
|
|
|
801
873
|
try {
|
|
802
874
|
for (let i = 0; i < deltas.length; i++) {
|
|
803
875
|
const delta = deltas[i];
|
|
804
|
-
|
|
805
|
-
for (const markName in delta.attributes) {
|
|
806
|
-
if (Array.isArray(delta.attributes[markName])) {
|
|
807
|
-
// multiple marks of same type
|
|
808
|
-
delta.attributes[markName].forEach(attrs => {
|
|
809
|
-
marks.push(schema.mark(markName, attrs));
|
|
810
|
-
});
|
|
811
|
-
} else {
|
|
812
|
-
// single mark
|
|
813
|
-
marks.push(schema.mark(markName, delta.attributes[markName]));
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
nodes.push(schema.text(delta.insert, marks));
|
|
876
|
+
nodes.push(schema.text(delta.insert, attributesToMarks(delta.attributes, schema)));
|
|
817
877
|
}
|
|
818
878
|
} catch (e) {
|
|
819
879
|
// an error occured while creating the node. This is probably a result of a concurrent action.
|
|
@@ -829,67 +889,70 @@ const createTextNodesFromYText = (
|
|
|
829
889
|
/**
|
|
830
890
|
* @private
|
|
831
891
|
* @param {Array<any>} nodes prosemirror node
|
|
832
|
-
* @param {
|
|
892
|
+
* @param {BindingMetadata} meta
|
|
833
893
|
* @return {Y.XmlText}
|
|
834
894
|
*/
|
|
835
|
-
const createTypeFromTextNodes = (nodes,
|
|
895
|
+
const createTypeFromTextNodes = (nodes, meta) => {
|
|
836
896
|
const type = new Y.XmlText();
|
|
837
897
|
const delta = nodes.map((node) => ({
|
|
838
898
|
// @ts-ignore
|
|
839
899
|
insert: node.text,
|
|
840
|
-
attributes: marksToAttributes(node.marks)
|
|
900
|
+
attributes: marksToAttributes(node.marks, meta)
|
|
841
901
|
}));
|
|
842
902
|
type.applyDelta(delta);
|
|
843
|
-
mapping.set(type, nodes);
|
|
903
|
+
meta.mapping.set(type, nodes);
|
|
844
904
|
return type
|
|
845
905
|
};
|
|
846
906
|
|
|
847
907
|
/**
|
|
848
908
|
* @private
|
|
849
909
|
* @param {any} node prosemirror node
|
|
850
|
-
* @param {
|
|
910
|
+
* @param {BindingMetadata} meta
|
|
851
911
|
* @return {Y.XmlElement}
|
|
852
912
|
*/
|
|
853
|
-
const createTypeFromElementNode = (node,
|
|
913
|
+
const createTypeFromElementNode = (node, meta) => {
|
|
854
914
|
const type = new Y.XmlElement(node.type.name);
|
|
855
|
-
const nodeMarksAttr = nodeMarksToAttributes(node.marks);
|
|
856
915
|
for (const key in node.attrs) {
|
|
857
916
|
const val = node.attrs[key];
|
|
858
917
|
if (val !== null && key !== 'ychange') {
|
|
859
918
|
type.setAttribute(key, val);
|
|
860
919
|
}
|
|
861
920
|
}
|
|
862
|
-
for (const key in nodeMarksAttr) {
|
|
863
|
-
type.setAttribute(key, nodeMarksAttr[key]);
|
|
864
|
-
}
|
|
865
921
|
type.insert(
|
|
866
922
|
0,
|
|
867
923
|
normalizePNodeContent(node).map((n) =>
|
|
868
|
-
createTypeFromTextOrElementNode(n,
|
|
924
|
+
createTypeFromTextOrElementNode(n, meta)
|
|
869
925
|
)
|
|
870
926
|
);
|
|
871
|
-
mapping.set(type, node);
|
|
927
|
+
meta.mapping.set(type, node);
|
|
872
928
|
return type
|
|
873
929
|
};
|
|
874
930
|
|
|
875
931
|
/**
|
|
876
932
|
* @private
|
|
877
933
|
* @param {PModel.Node|Array<PModel.Node>} node prosemirror text node
|
|
878
|
-
* @param {
|
|
934
|
+
* @param {BindingMetadata} meta
|
|
879
935
|
* @return {Y.XmlElement|Y.XmlText}
|
|
880
936
|
*/
|
|
881
|
-
const createTypeFromTextOrElementNode = (node,
|
|
937
|
+
const createTypeFromTextOrElementNode = (node, meta) =>
|
|
882
938
|
node instanceof Array
|
|
883
|
-
? createTypeFromTextNodes(node,
|
|
884
|
-
: createTypeFromElementNode(node,
|
|
939
|
+
? createTypeFromTextNodes(node, meta)
|
|
940
|
+
: createTypeFromElementNode(node, meta);
|
|
885
941
|
|
|
942
|
+
/**
|
|
943
|
+
* @param {any} val
|
|
944
|
+
*/
|
|
886
945
|
const isObject = (val) => typeof val === 'object' && val !== null;
|
|
887
946
|
|
|
947
|
+
/**
|
|
948
|
+
* @param {any} pattrs
|
|
949
|
+
* @param {any} yattrs
|
|
950
|
+
*/
|
|
888
951
|
const equalAttrs = (pattrs, yattrs) => {
|
|
889
952
|
const keys = Object.keys(pattrs).filter((key) => pattrs[key] !== null);
|
|
890
953
|
let eq =
|
|
891
954
|
keys.length ===
|
|
892
|
-
|
|
955
|
+
Object.keys(yattrs).filter((key) => yattrs[key] !== null).length;
|
|
893
956
|
for (let i = 0; i < keys.length && eq; i++) {
|
|
894
957
|
const key = keys[i];
|
|
895
958
|
const l = pattrs[key];
|
|
@@ -900,20 +963,6 @@ const equalAttrs = (pattrs, yattrs) => {
|
|
|
900
963
|
return eq
|
|
901
964
|
};
|
|
902
965
|
|
|
903
|
-
const equalMarks = (pmarks, yattrs) => {
|
|
904
|
-
const keys = Object.keys(yattrs).filter((key) => key.startsWith(MarkPrefix));
|
|
905
|
-
let eq =
|
|
906
|
-
keys.length === pmarks.length;
|
|
907
|
-
const pMarkAttr = nodeMarksToAttributes(pmarks);
|
|
908
|
-
for (let i = 0; i < keys.length && eq; i++) {
|
|
909
|
-
const key = keys[i];
|
|
910
|
-
const l = pMarkAttr[key];
|
|
911
|
-
const r = yattrs[key];
|
|
912
|
-
eq = key === 'ychange' || f.equalityDeep(l, r);
|
|
913
|
-
}
|
|
914
|
-
return eq
|
|
915
|
-
};
|
|
916
|
-
|
|
917
966
|
/**
|
|
918
967
|
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
|
|
919
968
|
*/
|
|
@@ -948,17 +997,14 @@ const normalizePNodeContent = (pnode) => {
|
|
|
948
997
|
const equalYTextPText = (ytext, ptexts) => {
|
|
949
998
|
const delta = ytext.toDelta();
|
|
950
999
|
return delta.length === ptexts.length &&
|
|
951
|
-
delta.every((d, i) =>
|
|
1000
|
+
delta.every(/** @type {(d:any,i:number) => boolean} */ (d, i) =>
|
|
952
1001
|
d.insert === /** @type {any} */ (ptexts[i]).text &&
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
return equalAttrs(mark.attrs, yattrs)
|
|
960
|
-
}
|
|
961
|
-
)
|
|
1002
|
+
object.keys(d.attributes || {}).length === ptexts[i].marks.length &&
|
|
1003
|
+
object.every(d.attributes, (attr, yattrname) => {
|
|
1004
|
+
const markname = yattr2markname(yattrname);
|
|
1005
|
+
const pmarks = ptexts[i].marks;
|
|
1006
|
+
return equalAttrs(attr, pmarks.find(/** @param {any} mark */ mark => mark.type.name === markname)?.attrs)
|
|
1007
|
+
})
|
|
962
1008
|
)
|
|
963
1009
|
};
|
|
964
1010
|
|
|
@@ -973,8 +1019,7 @@ const equalYTypePNode = (ytype, pnode) => {
|
|
|
973
1019
|
) {
|
|
974
1020
|
const normalizedContent = normalizePNodeContent(pnode);
|
|
975
1021
|
return ytype._length === normalizedContent.length &&
|
|
976
|
-
equalAttrs(
|
|
977
|
-
equalMarks(pnode.marks, ytype.getAttributes()) &&
|
|
1022
|
+
equalAttrs(ytype.getAttributes(), pnode.attrs) &&
|
|
978
1023
|
ytype.toArray().every((ychild, i) =>
|
|
979
1024
|
equalYTypePNode(ychild, normalizedContent[i])
|
|
980
1025
|
)
|
|
@@ -997,10 +1042,10 @@ const mappedIdentity = (mapped, pcontent) =>
|
|
|
997
1042
|
/**
|
|
998
1043
|
* @param {Y.XmlElement} ytype
|
|
999
1044
|
* @param {PModel.Node} pnode
|
|
1000
|
-
* @param {
|
|
1045
|
+
* @param {BindingMetadata} meta
|
|
1001
1046
|
* @return {{ foundMappedChild: boolean, equalityFactor: number }}
|
|
1002
1047
|
*/
|
|
1003
|
-
const computeChildEqualityFactor = (ytype, pnode,
|
|
1048
|
+
const computeChildEqualityFactor = (ytype, pnode, meta) => {
|
|
1004
1049
|
const yChildren = ytype.toArray();
|
|
1005
1050
|
const pChildren = normalizePNodeContent(pnode);
|
|
1006
1051
|
const pChildCnt = pChildren.length;
|
|
@@ -1012,7 +1057,7 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1012
1057
|
for (; left < minCnt; left++) {
|
|
1013
1058
|
const leftY = yChildren[left];
|
|
1014
1059
|
const leftP = pChildren[left];
|
|
1015
|
-
if (mappedIdentity(mapping.get(leftY), leftP)) {
|
|
1060
|
+
if (mappedIdentity(meta.mapping.get(leftY), leftP)) {
|
|
1016
1061
|
foundMappedChild = true; // definite (good) match!
|
|
1017
1062
|
} else if (!equalYTypePNode(leftY, leftP)) {
|
|
1018
1063
|
break
|
|
@@ -1021,7 +1066,7 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1021
1066
|
for (; left + right < minCnt; right++) {
|
|
1022
1067
|
const rightY = yChildren[yChildCnt - right - 1];
|
|
1023
1068
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1024
|
-
if (mappedIdentity(mapping.get(rightY), rightP)) {
|
|
1069
|
+
if (mappedIdentity(meta.mapping.get(rightY), rightP)) {
|
|
1025
1070
|
foundMappedChild = true;
|
|
1026
1071
|
} else if (!equalYTypePNode(rightY, rightP)) {
|
|
1027
1072
|
break
|
|
@@ -1033,6 +1078,9 @@ const computeChildEqualityFactor = (ytype, pnode, mapping) => {
|
|
|
1033
1078
|
}
|
|
1034
1079
|
};
|
|
1035
1080
|
|
|
1081
|
+
/**
|
|
1082
|
+
* @param {Y.Text} ytext
|
|
1083
|
+
*/
|
|
1036
1084
|
const ytextTrans = (ytext) => {
|
|
1037
1085
|
let str = '';
|
|
1038
1086
|
/**
|
|
@@ -1061,14 +1109,14 @@ const ytextTrans = (ytext) => {
|
|
|
1061
1109
|
*
|
|
1062
1110
|
* @param {Y.Text} ytext
|
|
1063
1111
|
* @param {Array<any>} ptexts
|
|
1064
|
-
* @param {
|
|
1112
|
+
* @param {BindingMetadata} meta
|
|
1065
1113
|
*/
|
|
1066
|
-
const updateYText = (ytext, ptexts,
|
|
1067
|
-
mapping.set(ytext, ptexts);
|
|
1114
|
+
const updateYText = (ytext, ptexts, meta) => {
|
|
1115
|
+
meta.mapping.set(ytext, ptexts);
|
|
1068
1116
|
const { nAttrs, str } = ytextTrans(ytext);
|
|
1069
1117
|
const content = ptexts.map((p) => ({
|
|
1070
1118
|
insert: /** @type {any} */ (p).text,
|
|
1071
|
-
attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks))
|
|
1119
|
+
attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks, meta))
|
|
1072
1120
|
}));
|
|
1073
1121
|
const { insert, remove, index } = simpleDiff(
|
|
1074
1122
|
str,
|
|
@@ -1081,30 +1129,40 @@ const updateYText = (ytext, ptexts, mapping) => {
|
|
|
1081
1129
|
);
|
|
1082
1130
|
};
|
|
1083
1131
|
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1132
|
+
const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/;
|
|
1133
|
+
/**
|
|
1134
|
+
* @param {string} attrName
|
|
1135
|
+
*/
|
|
1136
|
+
const yattr2markname = attrName => hashedMarkNameRegex.exec(attrName)?.[1] ?? attrName;
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* @todo move this to markstoattributes
|
|
1140
|
+
*
|
|
1141
|
+
* @param {Object<string, any>} attrs
|
|
1142
|
+
* @param {import('prosemirror-model').Schema} schema
|
|
1143
|
+
*/
|
|
1144
|
+
const attributesToMarks = (attrs, schema) => {
|
|
1145
|
+
/**
|
|
1146
|
+
* @type {Array<import('prosemirror-model').Mark>}
|
|
1147
|
+
*/
|
|
1148
|
+
const marks = [];
|
|
1149
|
+
for (const markName in attrs) {
|
|
1150
|
+
// remove hashes if necessary
|
|
1151
|
+
marks.push(schema.mark(yattr2markname(markName), attrs[markName]));
|
|
1152
|
+
}
|
|
1153
|
+
return marks
|
|
1101
1154
|
};
|
|
1102
1155
|
|
|
1103
|
-
|
|
1156
|
+
/**
|
|
1157
|
+
* @param {Array<import('prosemirror-model').Mark>} marks
|
|
1158
|
+
* @param {BindingMetadata} meta
|
|
1159
|
+
*/
|
|
1160
|
+
const marksToAttributes = (marks, meta) => {
|
|
1104
1161
|
const pattrs = {};
|
|
1105
1162
|
marks.forEach((mark) => {
|
|
1106
1163
|
if (mark.type.name !== 'ychange') {
|
|
1107
|
-
|
|
1164
|
+
const isOverlapping = map.setIfUndefined(meta.isOMark, mark.type, () => !mark.type.excludes(mark.type));
|
|
1165
|
+
pattrs[isOverlapping ? `${mark.type.name}--${hashOfJSON(mark.toJSON())}` : mark.type.name] = mark.attrs;
|
|
1108
1166
|
}
|
|
1109
1167
|
});
|
|
1110
1168
|
return pattrs
|
|
@@ -1113,7 +1171,7 @@ const nodeMarksToAttributes = (marks) => {
|
|
|
1113
1171
|
/**
|
|
1114
1172
|
* Update a yDom node by syncing the current content of the prosemirror node.
|
|
1115
1173
|
*
|
|
1116
|
-
* This is a y-
|
|
1174
|
+
* This is a y-prosemirror internal feature that you can use at your own risk.
|
|
1117
1175
|
*
|
|
1118
1176
|
* @private
|
|
1119
1177
|
* @unstable
|
|
@@ -1121,27 +1179,24 @@ const nodeMarksToAttributes = (marks) => {
|
|
|
1121
1179
|
* @param {{transact: Function}} y
|
|
1122
1180
|
* @param {Y.XmlFragment} yDomFragment
|
|
1123
1181
|
* @param {any} pNode
|
|
1124
|
-
* @param {
|
|
1182
|
+
* @param {BindingMetadata} meta
|
|
1125
1183
|
*/
|
|
1126
|
-
const updateYFragment = (y, yDomFragment, pNode,
|
|
1184
|
+
const updateYFragment = (y, yDomFragment, pNode, meta) => {
|
|
1127
1185
|
if (
|
|
1128
1186
|
yDomFragment instanceof Y.XmlElement &&
|
|
1129
1187
|
yDomFragment.nodeName !== pNode.type.name
|
|
1130
1188
|
) {
|
|
1131
1189
|
throw new Error('node name mismatch!')
|
|
1132
1190
|
}
|
|
1133
|
-
mapping.set(yDomFragment, pNode);
|
|
1191
|
+
meta.mapping.set(yDomFragment, pNode);
|
|
1134
1192
|
// update attributes
|
|
1135
1193
|
if (yDomFragment instanceof Y.XmlElement) {
|
|
1136
1194
|
const yDomAttrs = yDomFragment.getAttributes();
|
|
1137
1195
|
const pAttrs = pNode.attrs;
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (attrs[key] !== null) {
|
|
1143
|
-
if (yDomAttrs[key] !== attrs[key] && key !== 'ychange') {
|
|
1144
|
-
yDomFragment.setAttribute(key, attrs[key]);
|
|
1196
|
+
for (const key in pAttrs) {
|
|
1197
|
+
if (pAttrs[key] !== null) {
|
|
1198
|
+
if (yDomAttrs[key] !== pAttrs[key] && key !== 'ychange') {
|
|
1199
|
+
yDomFragment.setAttribute(key, pAttrs[key]);
|
|
1145
1200
|
}
|
|
1146
1201
|
} else {
|
|
1147
1202
|
yDomFragment.removeAttribute(key);
|
|
@@ -1149,7 +1204,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1149
1204
|
}
|
|
1150
1205
|
// remove all keys that are no longer in pAttrs
|
|
1151
1206
|
for (const key in yDomAttrs) {
|
|
1152
|
-
if (
|
|
1207
|
+
if (pAttrs[key] === undefined) {
|
|
1153
1208
|
yDomFragment.removeAttribute(key);
|
|
1154
1209
|
}
|
|
1155
1210
|
}
|
|
@@ -1166,10 +1221,10 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1166
1221
|
for (; left < minCnt; left++) {
|
|
1167
1222
|
const leftY = yChildren[left];
|
|
1168
1223
|
const leftP = pChildren[left];
|
|
1169
|
-
if (!mappedIdentity(mapping.get(leftY), leftP)) {
|
|
1224
|
+
if (!mappedIdentity(meta.mapping.get(leftY), leftP)) {
|
|
1170
1225
|
if (equalYTypePNode(leftY, leftP)) {
|
|
1171
1226
|
// update mapping
|
|
1172
|
-
mapping.set(leftY, leftP);
|
|
1227
|
+
meta.mapping.set(leftY, leftP);
|
|
1173
1228
|
} else {
|
|
1174
1229
|
break
|
|
1175
1230
|
}
|
|
@@ -1179,10 +1234,10 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1179
1234
|
for (; right + left + 1 < minCnt; right++) {
|
|
1180
1235
|
const rightY = yChildren[yChildCnt - right - 1];
|
|
1181
1236
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1182
|
-
if (!mappedIdentity(mapping.get(rightY), rightP)) {
|
|
1237
|
+
if (!mappedIdentity(meta.mapping.get(rightY), rightP)) {
|
|
1183
1238
|
if (equalYTypePNode(rightY, rightP)) {
|
|
1184
1239
|
// update mapping
|
|
1185
|
-
mapping.set(rightY, rightP);
|
|
1240
|
+
meta.mapping.set(rightY, rightP);
|
|
1186
1241
|
} else {
|
|
1187
1242
|
break
|
|
1188
1243
|
}
|
|
@@ -1197,7 +1252,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1197
1252
|
const rightP = pChildren[pChildCnt - right - 1];
|
|
1198
1253
|
if (leftY instanceof Y.XmlText && leftP instanceof Array) {
|
|
1199
1254
|
if (!equalYTextPText(leftY, leftP)) {
|
|
1200
|
-
updateYText(leftY, leftP,
|
|
1255
|
+
updateYText(leftY, leftP, meta);
|
|
1201
1256
|
}
|
|
1202
1257
|
left += 1;
|
|
1203
1258
|
} else {
|
|
@@ -1210,12 +1265,12 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1210
1265
|
const equalityLeft = computeChildEqualityFactor(
|
|
1211
1266
|
/** @type {Y.XmlElement} */ (leftY),
|
|
1212
1267
|
/** @type {PModel.Node} */ (leftP),
|
|
1213
|
-
|
|
1268
|
+
meta
|
|
1214
1269
|
);
|
|
1215
1270
|
const equalityRight = computeChildEqualityFactor(
|
|
1216
1271
|
/** @type {Y.XmlElement} */ (rightY),
|
|
1217
1272
|
/** @type {PModel.Node} */ (rightP),
|
|
1218
|
-
|
|
1273
|
+
meta
|
|
1219
1274
|
);
|
|
1220
1275
|
if (
|
|
1221
1276
|
equalityLeft.foundMappedChild && !equalityRight.foundMappedChild
|
|
@@ -1238,7 +1293,7 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1238
1293
|
y,
|
|
1239
1294
|
/** @type {Y.XmlFragment} */ (leftY),
|
|
1240
1295
|
/** @type {PModel.Node} */ (leftP),
|
|
1241
|
-
|
|
1296
|
+
meta
|
|
1242
1297
|
);
|
|
1243
1298
|
left += 1;
|
|
1244
1299
|
} else if (updateRight) {
|
|
@@ -1246,14 +1301,14 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1246
1301
|
y,
|
|
1247
1302
|
/** @type {Y.XmlFragment} */ (rightY),
|
|
1248
1303
|
/** @type {PModel.Node} */ (rightP),
|
|
1249
|
-
|
|
1304
|
+
meta
|
|
1250
1305
|
);
|
|
1251
1306
|
right += 1;
|
|
1252
1307
|
} else {
|
|
1253
|
-
mapping.delete(yDomFragment.get(left));
|
|
1308
|
+
meta.mapping.delete(yDomFragment.get(left));
|
|
1254
1309
|
yDomFragment.delete(left, 1);
|
|
1255
1310
|
yDomFragment.insert(left, [
|
|
1256
|
-
createTypeFromTextOrElementNode(leftP,
|
|
1311
|
+
createTypeFromTextOrElementNode(leftP, meta)
|
|
1257
1312
|
]);
|
|
1258
1313
|
left += 1;
|
|
1259
1314
|
}
|
|
@@ -1263,18 +1318,18 @@ const updateYFragment = (y, yDomFragment, pNode, mapping) => {
|
|
|
1263
1318
|
if (
|
|
1264
1319
|
yChildCnt === 1 && pChildCnt === 0 && yChildren[0] instanceof Y.XmlText
|
|
1265
1320
|
) {
|
|
1266
|
-
mapping.delete(yChildren[0]);
|
|
1267
|
-
// Edge case handling https://github.com/yjs/y-
|
|
1321
|
+
meta.mapping.delete(yChildren[0]);
|
|
1322
|
+
// Edge case handling https://github.com/yjs/y-prosemirror/issues/108
|
|
1268
1323
|
// Only delete the content of the Y.Text to retain remote changes on the same Y.Text object
|
|
1269
1324
|
yChildren[0].delete(0, yChildren[0].length);
|
|
1270
1325
|
} else if (yDelLen > 0) {
|
|
1271
|
-
yDomFragment.slice(left, left + yDelLen).forEach(type => mapping.delete(type));
|
|
1326
|
+
yDomFragment.slice(left, left + yDelLen).forEach(type => meta.mapping.delete(type));
|
|
1272
1327
|
yDomFragment.delete(left, yDelLen);
|
|
1273
1328
|
}
|
|
1274
1329
|
if (left + right < pChildCnt) {
|
|
1275
1330
|
const ins = [];
|
|
1276
1331
|
for (let i = left; i < pChildCnt - right; i++) {
|
|
1277
|
-
ins.push(createTypeFromTextOrElementNode(pChildren[i],
|
|
1332
|
+
ins.push(createTypeFromTextOrElementNode(pChildren[i], meta));
|
|
1278
1333
|
}
|
|
1279
1334
|
yDomFragment.insert(left, ins);
|
|
1280
1335
|
}
|
|
@@ -1484,7 +1539,7 @@ const yXmlFragmentToProseMirrorFragment = (yXmlFragment, schema) => {
|
|
|
1484
1539
|
createNodeFromYElement(
|
|
1485
1540
|
/** @type {Y.XmlElement} */ (t),
|
|
1486
1541
|
schema,
|
|
1487
|
-
|
|
1542
|
+
createEmptyMeta()
|
|
1488
1543
|
)
|
|
1489
1544
|
).filter((n) => n !== null);
|
|
1490
1545
|
return Fragment.fromArray(fragmentContent)
|
|
@@ -1505,21 +1560,20 @@ const yXmlFragmentToProseMirrorRootNode = (yXmlFragment, schema) =>
|
|
|
1505
1560
|
*
|
|
1506
1561
|
* @param {Y.XmlFragment} yXmlFragment
|
|
1507
1562
|
* @param {Schema} schema
|
|
1563
|
+
*
|
|
1564
|
+
* @todo deprecate mapping property
|
|
1508
1565
|
*/
|
|
1509
1566
|
const initProseMirrorDoc = (yXmlFragment, schema) => {
|
|
1510
|
-
|
|
1511
|
-
* @type {ProsemirrorMapping}
|
|
1512
|
-
*/
|
|
1513
|
-
const mapping = new Map();
|
|
1567
|
+
const meta = createEmptyMeta();
|
|
1514
1568
|
const fragmentContent = yXmlFragment.toArray().map((t) =>
|
|
1515
1569
|
createNodeFromYElement(
|
|
1516
1570
|
/** @type {Y.XmlElement} */ (t),
|
|
1517
1571
|
schema,
|
|
1518
|
-
|
|
1572
|
+
meta
|
|
1519
1573
|
)
|
|
1520
1574
|
).filter((n) => n !== null);
|
|
1521
1575
|
const doc = schema.topNodeType.create(null, Fragment.fromArray(fragmentContent));
|
|
1522
|
-
return { doc, mapping }
|
|
1576
|
+
return { doc, meta, mapping: meta.mapping }
|
|
1523
1577
|
};
|
|
1524
1578
|
|
|
1525
1579
|
/**
|
|
@@ -1562,7 +1616,7 @@ function prosemirrorToYDoc (doc, xmlFragment = 'prosemirror') {
|
|
|
1562
1616
|
function prosemirrorToYXmlFragment (doc, xmlFragment) {
|
|
1563
1617
|
const type = xmlFragment || new Y.XmlFragment();
|
|
1564
1618
|
const ydoc = type.doc ? type.doc : { transact: (transaction) => transaction(undefined) };
|
|
1565
|
-
updateYFragment(ydoc, type, doc, new Map());
|
|
1619
|
+
updateYFragment(ydoc, type, doc, { mapping: new Map(), isOMark: new Map() });
|
|
1566
1620
|
return type
|
|
1567
1621
|
}
|
|
1568
1622
|
|
|
@@ -1658,7 +1712,10 @@ function yDocToProsemirrorJSON (
|
|
|
1658
1712
|
function yXmlFragmentToProsemirrorJSON (xmlFragment) {
|
|
1659
1713
|
const items = xmlFragment.toArray();
|
|
1660
1714
|
|
|
1661
|
-
|
|
1715
|
+
/**
|
|
1716
|
+
* @param {Y.AbstractType} item
|
|
1717
|
+
*/
|
|
1718
|
+
const serialize = item => {
|
|
1662
1719
|
/**
|
|
1663
1720
|
* @type {Object} NodeObject
|
|
1664
1721
|
* @property {string} NodeObject.type
|
|
@@ -1668,79 +1725,49 @@ function yXmlFragmentToProsemirrorJSON (xmlFragment) {
|
|
|
1668
1725
|
let response;
|
|
1669
1726
|
|
|
1670
1727
|
// TODO: Must be a better way to detect text nodes than this
|
|
1671
|
-
if (
|
|
1728
|
+
if (item instanceof Y.XmlText) {
|
|
1672
1729
|
const delta = item.toDelta();
|
|
1673
|
-
response = delta.map((d) => {
|
|
1730
|
+
response = delta.map(/** @param {any} d */ (d) => {
|
|
1674
1731
|
const text = {
|
|
1675
1732
|
type: 'text',
|
|
1676
1733
|
text: d.insert
|
|
1677
1734
|
};
|
|
1678
|
-
|
|
1679
1735
|
if (d.attributes) {
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
const
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
type
|
|
1689
|
-
};
|
|
1690
|
-
|
|
1691
|
-
if (Object.keys(singleAttrs)) {
|
|
1692
|
-
mark.attrs = singleAttrs;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
marks.push(mark);
|
|
1696
|
-
});
|
|
1697
|
-
} else {
|
|
1698
|
-
const mark = {
|
|
1699
|
-
type
|
|
1700
|
-
};
|
|
1701
|
-
|
|
1702
|
-
if (Object.keys(attrs)) {
|
|
1703
|
-
mark.attrs = attrs;
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
marks.push(mark);
|
|
1736
|
+
text.marks = Object.keys(d.attributes).map((type_) => {
|
|
1737
|
+
const attrs = d.attributes[type_];
|
|
1738
|
+
const type = yattr2markname(type_);
|
|
1739
|
+
const mark = {
|
|
1740
|
+
type
|
|
1741
|
+
};
|
|
1742
|
+
if (Object.keys(attrs)) {
|
|
1743
|
+
mark.attrs = attrs;
|
|
1707
1744
|
}
|
|
1745
|
+
return mark
|
|
1708
1746
|
});
|
|
1709
|
-
|
|
1710
|
-
text.marks = marks;
|
|
1711
1747
|
}
|
|
1712
1748
|
return text
|
|
1713
1749
|
});
|
|
1714
|
-
} else {
|
|
1750
|
+
} else if (item instanceof Y.XmlElement) {
|
|
1715
1751
|
response = {
|
|
1716
1752
|
type: item.nodeName
|
|
1717
1753
|
};
|
|
1718
1754
|
|
|
1719
1755
|
const attrs = item.getAttributes();
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
for (const key of Object.keys(attrs).filter((key) => !key.startsWith(MarkPrefix))) {
|
|
1723
|
-
if (!response.attrs) response.attrs = {};
|
|
1724
|
-
response.attrs[key] = attrs[key];
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
// Check whether the attrs contains marks, if so, add them to the response
|
|
1728
|
-
if (Object.keys(attrs).some((key) => key.startsWith(MarkPrefix))) {
|
|
1729
|
-
response.marks = Object.keys(attrs)
|
|
1730
|
-
.filter((key) => key.startsWith(MarkPrefix))
|
|
1731
|
-
.map((key) => {
|
|
1732
|
-
return attrs[key]
|
|
1733
|
-
});
|
|
1756
|
+
if (Object.keys(attrs).length) {
|
|
1757
|
+
response.attrs = attrs;
|
|
1734
1758
|
}
|
|
1735
1759
|
|
|
1736
1760
|
const children = item.toArray();
|
|
1737
1761
|
if (children.length) {
|
|
1738
1762
|
response.content = children.map(serialize).flat();
|
|
1739
1763
|
}
|
|
1764
|
+
} else {
|
|
1765
|
+
// expected either Y.XmlElement or Y.XmlText
|
|
1766
|
+
error.unexpectedCase();
|
|
1740
1767
|
}
|
|
1741
1768
|
|
|
1742
1769
|
return response
|
|
1743
|
-
}
|
|
1770
|
+
};
|
|
1744
1771
|
|
|
1745
1772
|
return {
|
|
1746
1773
|
type: 'doc',
|
|
@@ -1783,7 +1810,7 @@ const defaultCursorBuilder = (user) => {
|
|
|
1783
1810
|
* Default generator for the selection attributes
|
|
1784
1811
|
*
|
|
1785
1812
|
* @param {any} user user data
|
|
1786
|
-
* @return {import('
|
|
1813
|
+
* @return {import('prosemirror-view').DecorationAttrs}
|
|
1787
1814
|
*/
|
|
1788
1815
|
const defaultSelectionBuilder = (user) => {
|
|
1789
1816
|
return {
|
|
@@ -1798,8 +1825,8 @@ const rxValidColor = /^#[0-9a-fA-F]{6}$/;
|
|
|
1798
1825
|
* @param {any} state
|
|
1799
1826
|
* @param {Awareness} awareness
|
|
1800
1827
|
* @param {function(number, number, any):boolean} awarenessFilter
|
|
1801
|
-
* @param {
|
|
1802
|
-
* @param {
|
|
1828
|
+
* @param {(user: { name: string, color: string }, clientId: number) => Element} createCursor
|
|
1829
|
+
* @param {(user: { name: string, color: string }, clientId: number) => import('prosemirror-view').DecorationAttrs} createSelection
|
|
1803
1830
|
* @return {any} DecorationSet
|
|
1804
1831
|
*/
|
|
1805
1832
|
const createDecorations = (
|
|
@@ -1829,7 +1856,7 @@ const createDecorations = (
|
|
|
1829
1856
|
if (user.color == null) {
|
|
1830
1857
|
user.color = '#ffa500';
|
|
1831
1858
|
} else if (!rxValidColor.test(user.color)) {
|
|
1832
|
-
// We only support 6-digit RGB colors in y-
|
|
1859
|
+
// We only support 6-digit RGB colors in y-prosemirror
|
|
1833
1860
|
console.warn('A user uses an unsupported color format', user);
|
|
1834
1861
|
}
|
|
1835
1862
|
if (user.name == null) {
|
|
@@ -1852,7 +1879,7 @@ const createDecorations = (
|
|
|
1852
1879
|
anchor = math.min(anchor, maxsize);
|
|
1853
1880
|
head = math.min(head, maxsize);
|
|
1854
1881
|
decorations.push(
|
|
1855
|
-
Decoration.widget(head, () => createCursor(user), {
|
|
1882
|
+
Decoration.widget(head, () => createCursor(user, clientId), {
|
|
1856
1883
|
key: clientId + '',
|
|
1857
1884
|
side: 10
|
|
1858
1885
|
})
|
|
@@ -1860,7 +1887,7 @@ const createDecorations = (
|
|
|
1860
1887
|
const from = math.min(anchor, head);
|
|
1861
1888
|
const to = math.max(anchor, head);
|
|
1862
1889
|
decorations.push(
|
|
1863
|
-
Decoration.inline(from, to, createSelection(user), {
|
|
1890
|
+
Decoration.inline(from, to, createSelection(user, clientId), {
|
|
1864
1891
|
inclusiveEnd: true,
|
|
1865
1892
|
inclusiveStart: false
|
|
1866
1893
|
})
|
|
@@ -1879,8 +1906,8 @@ const createDecorations = (
|
|
|
1879
1906
|
* @param {Awareness} awareness
|
|
1880
1907
|
* @param {object} opts
|
|
1881
1908
|
* @param {function(any, any, any):boolean} [opts.awarenessStateFilter]
|
|
1882
|
-
* @param {
|
|
1883
|
-
* @param {
|
|
1909
|
+
* @param {(user: any, clientId: number) => HTMLElement} [opts.cursorBuilder]
|
|
1910
|
+
* @param {(user: any, clientId: number) => import('prosemirror-view').DecorationAttrs} [opts.selectionBuilder]
|
|
1884
1911
|
* @param {function(any):any} [opts.getSelection]
|
|
1885
1912
|
* @param {string} [cursorStateField] By default all editor bindings use the awareness 'cursor' field to propagate cursor information.
|
|
1886
1913
|
* @return {any}
|
|
@@ -2027,7 +2054,7 @@ const defaultDeleteFilter = (item, protectedNodes) => !(item instanceof Item) ||
|
|
|
2027
2054
|
(item.content.type instanceof XmlElement && protectedNodes.has(item.content.type.nodeName))) ||
|
|
2028
2055
|
item.content.type._length === 0;
|
|
2029
2056
|
|
|
2030
|
-
const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) => new Plugin({
|
|
2057
|
+
const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) => new Plugin$1({
|
|
2031
2058
|
key: yUndoPluginKey,
|
|
2032
2059
|
state: {
|
|
2033
2060
|
init: (initargs, state) => {
|