@tiptap/y-tiptap 3.0.4 → 3.0.6

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 CHANGED
@@ -96,7 +96,11 @@ export function yDocToProsemirrorJSON(ydoc: Y.Doc, xmlFragment?: string): Record
96
96
  export function yXmlFragmentToProsemirrorJSON(xmlFragment: Y.XmlFragment): Record<string, any>;
97
97
  export function setMeta(view: any, key: any, value: any): void;
98
98
  export function absolutePositionToRelativePosition(pos: number, type: Y.XmlFragment, mapping: ProsemirrorMapping): any;
99
+ export function isMisresolvedTextPosition(y: Y.Doc, relPos: Y.RelativePosition, absPos: number | null): boolean;
99
100
  export function relativePositionToAbsolutePosition(y: Y.Doc, documentType: Y.XmlFragment, relPos: any, mapping: ProsemirrorMapping): null | number;
101
+ export function findAbsolutePositionAfterStructuralChange(oldDoc: import('prosemirror-model').Node, newDoc: import('prosemirror-model').Node, absPos: number): number | null;
102
+ export function isStructuralTransaction(tr: import('prosemirror-state').Transaction, oldDoc: import('prosemirror-model').Node): boolean;
103
+ export function isMisresolvedAfterStructuralChange(oldDoc: import('prosemirror-model').Node, newDoc: import('prosemirror-model').Node, oldAbs: number, resolvedAbs: number | null): boolean;
100
104
  export function yXmlFragmentToProseMirrorFragment(yXmlFragment: Y.XmlFragment, schema: Schema): Fragment;
101
105
  export function yXmlFragmentToProseMirrorRootNode(yXmlFragment: Y.XmlFragment, schema: Schema): Node;
102
106
  export function initProseMirrorDoc(yXmlFragment: Y.XmlFragment, schema: Schema): {
@@ -6,8 +6,11 @@ export function isVisible(item: Y.Item, snapshot?: Y.Snapshot): boolean;
6
6
  export function ySyncPlugin(yXmlFragment: Y.XmlFragment, { colors, colorMapping, permanentUserData, onFirstRender, mapping }?: YSyncOpts): any;
7
7
  export function getRelativeSelection(pmbinding: ProsemirrorBinding, state: import('prosemirror-state').EditorState): {
8
8
  type: any;
9
+ depth: any;
9
10
  anchor: any;
10
11
  head: any;
12
+ absAnchor: number;
13
+ absHead: number;
11
14
  };
12
15
  /**
13
16
  * Binding for prosemirror.
@@ -44,8 +47,11 @@ export class ProsemirrorBinding {
44
47
  */
45
48
  beforeTransactionSelection: {
46
49
  type: any;
50
+ depth: any;
47
51
  anchor: any;
48
52
  head: any;
53
+ absAnchor: number;
54
+ absHead: number;
49
55
  };
50
56
  beforeAllTransactions: () => void;
51
57
  afterAllTransactions: () => void;
@@ -2,4 +2,4 @@ export * from "./plugins/cursor-plugin.js";
2
2
  export * from "./plugins/undo-plugin.js";
3
3
  export * from "./plugins/keys.js";
4
4
  export { ySyncPlugin, isVisible, getRelativeSelection, ProsemirrorBinding, updateYFragment } from "./plugins/sync-plugin.js";
5
- export { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta, prosemirrorJSONToYDoc, yDocToProsemirrorJSON, yDocToProsemirror, prosemirrorToYDoc, prosemirrorJSONToYXmlFragment, yXmlFragmentToProsemirrorJSON, yXmlFragmentToProsemirror, prosemirrorToYXmlFragment, yXmlFragmentToProseMirrorRootNode, yXmlFragmentToProseMirrorFragment, initProseMirrorDoc } from "./lib.js";
5
+ export { absolutePositionToRelativePosition, relativePositionToAbsolutePosition, setMeta, findAbsolutePositionAfterStructuralChange, isMisresolvedTextPosition, isMisresolvedAfterStructuralChange, isStructuralTransaction, prosemirrorJSONToYDoc, yDocToProsemirrorJSON, yDocToProsemirror, prosemirrorToYDoc, prosemirrorJSONToYXmlFragment, yXmlFragmentToProsemirrorJSON, yXmlFragmentToProsemirror, prosemirrorToYXmlFragment, yXmlFragmentToProseMirrorRootNode, yXmlFragmentToProseMirrorFragment, initProseMirrorDoc } from "./lib.js";
package/dist/y-tiptap.cjs CHANGED
@@ -20,6 +20,7 @@ var eventloop = require('lib0/eventloop');
20
20
  var map = require('lib0/map');
21
21
  var sha256 = require('lib0/hash/sha256');
22
22
  var buf = require('lib0/buffer');
23
+ var prosemirrorTransform = require('prosemirror-transform');
23
24
 
24
25
  function _interopNamespace(e) {
25
26
  if (e && e.__esModule) return e;
@@ -318,8 +319,9 @@ const ySyncPlugin = (yXmlFragment, {
318
319
  * @param {import('prosemirror-state').Transaction} tr
319
320
  * @param {ReturnType<typeof getRelativeSelection>} relSel
320
321
  * @param {ProsemirrorBinding} binding
322
+ * @param {import('prosemirror-model').Node} [oldDoc]
321
323
  */
322
- const restoreRelativeSelection = (tr, relSel, binding) => {
324
+ const restoreRelativeSelection = (tr, relSel, binding, oldDoc) => {
323
325
  if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
324
326
  if (relSel.type === 'all') {
325
327
  tr.setSelection(new prosemirrorState.AllSelection(tr.doc));
@@ -330,8 +332,12 @@ const restoreRelativeSelection = (tr, relSel, binding) => {
330
332
  relSel.anchor,
331
333
  binding.mapping
332
334
  );
333
- tr.setSelection(createSafeNodeSelection(tr, anchor));
334
- } else {
335
+ // anchor is null when the referenced node was deleted or moved out of
336
+ // binding.type by a remote update; resolving null would throw.
337
+ if (anchor !== null) {
338
+ tr.setSelection(createSafeNodeSelection(tr, anchor));
339
+ }
340
+ } else if (relSel.type === 'nodeRange') {
335
341
  const anchor = relativePositionToAbsolutePosition(
336
342
  binding.doc,
337
343
  binding.type,
@@ -344,6 +350,46 @@ const restoreRelativeSelection = (tr, relSel, binding) => {
344
350
  relSel.head,
345
351
  binding.mapping
346
352
  );
353
+ const selection = createSafeNodeRangeSelection(tr, anchor, head, relSel.depth);
354
+ if (selection !== null) {
355
+ tr.setSelection(selection);
356
+ }
357
+ } else {
358
+ let anchor = relativePositionToAbsolutePosition(
359
+ binding.doc,
360
+ binding.type,
361
+ relSel.anchor,
362
+ binding.mapping
363
+ );
364
+ let head = relativePositionToAbsolutePosition(
365
+ binding.doc,
366
+ binding.type,
367
+ relSel.head,
368
+ binding.mapping
369
+ );
370
+ const needsContentFallback = oldDoc != null &&
371
+ relSel.absAnchor != null &&
372
+ relSel.absHead != null &&
373
+ (
374
+ anchor === null ||
375
+ head === null ||
376
+ (relSel.absAnchor > 1 && anchor !== null && anchor <= 1) ||
377
+ (relSel.absHead > 1 && head !== null && head <= 1) ||
378
+ isMisresolvedAfterStructuralChange(oldDoc, tr.doc, relSel.absAnchor, anchor) ||
379
+ isMisresolvedAfterStructuralChange(oldDoc, tr.doc, relSel.absHead, head)
380
+ );
381
+ if (needsContentFallback) {
382
+ anchor = findAbsolutePositionAfterStructuralChange(
383
+ oldDoc,
384
+ tr.doc,
385
+ relSel.absAnchor
386
+ );
387
+ head = findAbsolutePositionAfterStructuralChange(
388
+ oldDoc,
389
+ tr.doc,
390
+ relSel.absHead
391
+ );
392
+ }
347
393
  if (anchor !== null && head !== null) {
348
394
  tr.setSelection(prosemirrorState.TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head)));
349
395
  }
@@ -368,23 +414,67 @@ const createSafeNodeSelection = (tr, pos) => {
368
414
  }
369
415
  };
370
416
 
417
+ /**
418
+ * Safely reconstructs a NodeRangeSelection from resolved absolute positions.
419
+ *
420
+ * @param {import('prosemirror-state').Transaction} tr - The transaction whose document provides resolved positions.
421
+ * @param {number|null} anchor - Absolute document position marking the start (boundary) of the node range.
422
+ * Use `relativePositionToAbsolutePosition` before calling this function.
423
+ * @param {number|null} head - Absolute document position marking the end (boundary) of the node range.
424
+ * Use `relativePositionToAbsolutePosition` before calling this function.
425
+ * @param {number|undefined} depth - The nesting depth at which the range operates (e.g. 0 for
426
+ * top-level blocks, 1 for blocks nested inside a wrapper). Passed through to
427
+ * `Selection.fromJSON`; ignored by `@tiptap/extension-node-range` < v2.29 but
428
+ * properly stored starting from that version.
429
+ * @returns {import('prosemirror-state').Selection|null} Reconstructed selection, or null if
430
+ * anchor/head could not be resolved.
431
+ */
432
+ const createSafeNodeRangeSelection = (tr, anchor, head, depth) => {
433
+ if (anchor === null || head === null) {
434
+ return null
435
+ }
436
+ const clampedAnchor = Math.min(Math.max(anchor, 0), tr.doc.content.size);
437
+ const clampedHead = Math.min(Math.max(head, 0), tr.doc.content.size);
438
+ try {
439
+ const selection = prosemirrorState.Selection.fromJSON(tr.doc, {
440
+ type: 'nodeRange',
441
+ anchor: clampedAnchor,
442
+ head: clampedHead,
443
+ depth
444
+ });
445
+ if (!selection.ranges.length) {
446
+ return prosemirrorState.TextSelection.near(tr.doc.resolve(clampedAnchor))
447
+ }
448
+ return selection
449
+ } catch (e) {
450
+ return prosemirrorState.TextSelection.near(tr.doc.resolve(clampedAnchor))
451
+ }
452
+ };
453
+
371
454
  /**
372
455
  * @param {ProsemirrorBinding} pmbinding
373
456
  * @param {import('prosemirror-state').EditorState} state
374
457
  */
375
- const getRelativeSelection = (pmbinding, state) => ({
376
- type: /** @type {any} */ (state.selection).jsonID,
377
- anchor: absolutePositionToRelativePosition(
378
- state.selection.anchor,
379
- pmbinding.type,
380
- pmbinding.mapping
381
- ),
382
- head: absolutePositionToRelativePosition(
383
- state.selection.head,
384
- pmbinding.type,
385
- pmbinding.mapping
386
- )
387
- });
458
+ const getRelativeSelection = (pmbinding, state) => {
459
+ const type = /** @type {any} */ (state.selection).jsonID;
460
+ return {
461
+ type,
462
+ // `depth` is only meaningful for NodeRangeSelection; undefined for every other type.
463
+ depth: type === 'nodeRange' ? /** @type {any} */ (state.selection).depth : undefined,
464
+ anchor: absolutePositionToRelativePosition(
465
+ state.selection.anchor,
466
+ pmbinding.type,
467
+ pmbinding.mapping
468
+ ),
469
+ head: absolutePositionToRelativePosition(
470
+ state.selection.head,
471
+ pmbinding.type,
472
+ pmbinding.mapping
473
+ ),
474
+ absAnchor: state.selection.anchor,
475
+ absHead: state.selection.head
476
+ }
477
+ };
388
478
 
389
479
  /**
390
480
  * Binding for prosemirror.
@@ -707,6 +797,9 @@ class ProsemirrorBinding {
707
797
  );
708
798
  transaction.changed.forEach(delType);
709
799
  transaction.changedParentTypes.forEach(delType);
800
+ // Rebuild the full Y↔PM mapping so relative cursor positions resolve against
801
+ // current node sizes after structural changes (e.g. drag-and-drop block moves).
802
+ this.mapping.clear();
710
803
  const fragmentContent = this.type.toArray().map((t) =>
711
804
  createNodeIfNotExists(
712
805
  /** @type {Y.XmlElement | Y.XmlHook} */ (t),
@@ -714,13 +807,14 @@ class ProsemirrorBinding {
714
807
  this
715
808
  )
716
809
  ).filter((n) => n !== null);
810
+ const oldDoc = this.prosemirrorView.state.doc;
717
811
  // @ts-ignore
718
812
  let tr = this._tr.replace(
719
813
  0,
720
814
  this.prosemirrorView.state.doc.content.size,
721
815
  new PModel__namespace.Slice(PModel__namespace.Fragment.from(fragmentContent), 0, 0)
722
816
  );
723
- restoreRelativeSelection(tr, this.beforeTransactionSelection, this);
817
+ restoreRelativeSelection(tr, this.beforeTransactionSelection, this, oldDoc);
724
818
  tr = tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, isUndoRedoOperation: transaction.origin instanceof Y__namespace.UndoManager });
725
819
  if (
726
820
  this.beforeTransactionSelection !== null && this._isLocalCursorInView()
@@ -1606,6 +1700,26 @@ const absolutePositionToRelativePosition = (pos, type, mapping) => {
1606
1700
  return Y__namespace.createRelativePositionFromTypeIndex(type, type._length, -1)
1607
1701
  };
1608
1702
 
1703
+ /**
1704
+ * Item-id based relative positions can misresolve to the document start after
1705
+ * block reorder during collaborative drag-and-drop.
1706
+ *
1707
+ * @param {Y.Doc} y
1708
+ * @param {Y.RelativePosition} relPos
1709
+ * @param {number|null} absPos
1710
+ * @return {boolean}
1711
+ */
1712
+ const isMisresolvedTextPosition = (y, relPos, absPos) => {
1713
+ if (absPos === null) {
1714
+ return false
1715
+ }
1716
+ const decoded = Y__namespace.createAbsolutePositionFromRelativePosition(relPos, y);
1717
+ return decoded !== null &&
1718
+ decoded.type instanceof Y__namespace.XmlText &&
1719
+ relPos.item !== null &&
1720
+ absPos <= 1
1721
+ };
1722
+
1609
1723
  const createRelativePosition = (type, item) => {
1610
1724
  let typeid = null;
1611
1725
  let tname = null;
@@ -1643,7 +1757,11 @@ const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) =>
1643
1757
  if (t instanceof Y__namespace.XmlText) {
1644
1758
  pos += t._length;
1645
1759
  } else {
1646
- pos += /** @type {any} */ (mapping.get(t)).nodeSize;
1760
+ const mapped = mapping.get(t);
1761
+ if (mapped == null) {
1762
+ return null
1763
+ }
1764
+ pos += /** @type {any} */ (mapped).nodeSize;
1647
1765
  }
1648
1766
  }
1649
1767
  n = /** @type {Y.Item} */ (n.right);
@@ -1667,7 +1785,11 @@ const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) =>
1667
1785
  if (contentType instanceof Y__namespace.XmlText) {
1668
1786
  pos += contentType._length;
1669
1787
  } else {
1670
- pos += /** @type {any} */ (mapping.get(contentType)).nodeSize;
1788
+ const mapped = mapping.get(contentType);
1789
+ if (mapped == null) {
1790
+ return null
1791
+ }
1792
+ pos += /** @type {any} */ (mapped).nodeSize;
1671
1793
  }
1672
1794
  }
1673
1795
  n = n.right;
@@ -1675,7 +1797,133 @@ const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) =>
1675
1797
  }
1676
1798
  type = /** @type {Y.AbstractType} */ (parent);
1677
1799
  }
1678
- return pos - 1 // we don't count the most outer tag, because it is a fragment
1800
+ const absPos = pos - 1; // we don't count the most outer tag, because it is a fragment
1801
+ if (isMisresolvedTextPosition(y, relPos, absPos)) {
1802
+ return null
1803
+ }
1804
+ return absPos
1805
+ };
1806
+
1807
+ /**
1808
+ * @param {import('prosemirror-model').Node} oldDoc
1809
+ * @param {import('prosemirror-model').Node} newDoc
1810
+ * @param {number} absPos
1811
+ * @return {number|null}
1812
+ */
1813
+ const findAbsolutePositionAfterStructuralChange = (oldDoc, newDoc, absPos) => {
1814
+ let pos = 0;
1815
+ let targetIdx = 0;
1816
+ for (; targetIdx < oldDoc.childCount; targetIdx++) {
1817
+ const child = oldDoc.child(targetIdx);
1818
+ if (pos + child.nodeSize > absPos) {
1819
+ break
1820
+ }
1821
+ pos += child.nodeSize;
1822
+ }
1823
+ if (targetIdx >= oldDoc.childCount) {
1824
+ return null
1825
+ }
1826
+ const targetChild = oldDoc.child(targetIdx);
1827
+ const offsetInChild = absPos - pos;
1828
+
1829
+ let occurrence = 0;
1830
+ for (let i = 0; i <= targetIdx; i++) {
1831
+ const child = oldDoc.child(i);
1832
+ if (child.type === targetChild.type && child.textContent === targetChild.textContent) {
1833
+ occurrence++;
1834
+ }
1835
+ }
1836
+
1837
+ let matchCount = 0;
1838
+ let newPos = 0;
1839
+ for (let i = 0; i < newDoc.childCount; i++) {
1840
+ const child = newDoc.child(i);
1841
+ if (child.type === targetChild.type && child.textContent === targetChild.textContent) {
1842
+ matchCount++;
1843
+ if (matchCount === occurrence) {
1844
+ const remapped = newPos + offsetInChild;
1845
+ const contentStart = newPos + 1;
1846
+ const contentEnd = newPos + child.nodeSize - 1;
1847
+ return Math.max(contentStart, Math.min(remapped, contentEnd))
1848
+ }
1849
+ }
1850
+ newPos += child.nodeSize;
1851
+ }
1852
+ return null
1853
+ };
1854
+
1855
+ /**
1856
+ * Returns true when a transaction changes block structure rather than only
1857
+ * editing inline content inside existing blocks.
1858
+ *
1859
+ * @param {import('prosemirror-state').Transaction} tr
1860
+ * @param {import('prosemirror-model').Node} oldDoc
1861
+ * @return {boolean}
1862
+ */
1863
+ const isStructuralTransaction = (tr, oldDoc) => {
1864
+ if (!tr.docChanged) {
1865
+ return false
1866
+ }
1867
+ if (tr.doc.childCount !== oldDoc.childCount) {
1868
+ return true
1869
+ }
1870
+ for (const step of tr.steps) {
1871
+ if (step instanceof prosemirrorTransform.ReplaceStep) {
1872
+ if (step.from === 0 && step.to === oldDoc.content.size) {
1873
+ return true
1874
+ }
1875
+ if (step.slice.content.size > 0) {
1876
+ let hasBlock = false;
1877
+ step.slice.content.forEach((node) => {
1878
+ if (node.isBlock) {
1879
+ hasBlock = true;
1880
+ }
1881
+ });
1882
+ if (hasBlock) {
1883
+ return true
1884
+ }
1885
+ } else if (step.to > step.from) {
1886
+ const $from = oldDoc.resolve(step.from);
1887
+ const $to = oldDoc.resolve(step.to);
1888
+ if ($from.depth === 0 && $to.depth === 0 && $from.index() !== $to.index()) {
1889
+ return true
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1894
+ return false
1895
+ };
1896
+
1897
+ /**
1898
+ * Detect stale relative positions after structural changes that resolve to the
1899
+ * wrong text block or to the start of the correct block.
1900
+ *
1901
+ * @param {import('prosemirror-model').Node} oldDoc
1902
+ * @param {import('prosemirror-model').Node} newDoc
1903
+ * @param {number} oldAbs
1904
+ * @param {number|null} resolvedAbs
1905
+ * @return {boolean}
1906
+ */
1907
+ const isMisresolvedAfterStructuralChange = (oldDoc, newDoc, oldAbs, resolvedAbs) => {
1908
+ if (resolvedAbs === null) {
1909
+ return false
1910
+ }
1911
+ const $old = oldDoc.resolve(oldAbs);
1912
+ const $new = newDoc.resolve(resolvedAbs);
1913
+ if (!$old.parent.isTextblock || !$new.parent.isTextblock) {
1914
+ return false
1915
+ }
1916
+ if ($old.parent.textContent !== $new.parent.textContent) {
1917
+ return true
1918
+ }
1919
+ if ($old.parentOffset !== 0 && $new.parentOffset === 0) {
1920
+ return true
1921
+ }
1922
+ if ($old.parentOffset === 0 && $new.parentOffset === 0) {
1923
+ const expected = findAbsolutePositionAfterStructuralChange(oldDoc, newDoc, oldAbs);
1924
+ return expected !== null && expected !== resolvedAbs
1925
+ }
1926
+ return false
1679
1927
  };
1680
1928
 
1681
1929
  /**
@@ -2094,7 +2342,7 @@ const yCursorPlugin = (
2094
2342
  selectionBuilder
2095
2343
  )
2096
2344
  },
2097
- apply (tr, prevState, _oldState, newState) {
2345
+ apply (tr, prevState, oldState, newState) {
2098
2346
  const ystate = ySyncPluginKey.getState(newState);
2099
2347
  const yCursorState = tr.getMeta(yCursorPluginKey);
2100
2348
  if (
@@ -2109,7 +2357,19 @@ const yCursorPlugin = (
2109
2357
  selectionBuilder
2110
2358
  )
2111
2359
  }
2112
- return prevState.map(tr.mapping, tr.doc)
2360
+ if (tr.docChanged) {
2361
+ if (isStructuralTransaction(tr, oldState.doc)) {
2362
+ return createDecorations(
2363
+ newState,
2364
+ awareness,
2365
+ awarenessStateFilter,
2366
+ cursorBuilder,
2367
+ selectionBuilder
2368
+ )
2369
+ }
2370
+ return prevState.map(tr.mapping, tr.doc)
2371
+ }
2372
+ return prevState
2113
2373
  }
2114
2374
  },
2115
2375
  props: {
@@ -2124,15 +2384,19 @@ const yCursorPlugin = (
2124
2384
  setMeta(view, yCursorPluginKey, { awarenessUpdated: true });
2125
2385
  }
2126
2386
  };
2127
- const updateCursorInfo = () => {
2128
- const ystate = ySyncPluginKey.getState(view.state);
2387
+ /**
2388
+ * @param {import('prosemirror-view').EditorView} editorView
2389
+ * @param {{ force?: boolean }} [opts]
2390
+ */
2391
+ const updateCursorInfo = (editorView, opts = {}) => {
2392
+ const ystate = ySyncPluginKey.getState(editorView.state);
2129
2393
  // `ystate` is undefined during sync-plugin init and Y.Doc transitions;
2130
2394
  // reading from it here would throw and break cursor tracking for the view.
2131
2395
  if (!ystate || !ystate.binding) return
2132
2396
  // @note We make implicit checks when checking for the cursor property
2133
2397
  const current = awareness.getLocalState() || {};
2134
- if (view.hasFocus()) {
2135
- const selection = getSelection(view.state);
2398
+ if (editorView.hasFocus()) {
2399
+ const selection = getSelection(editorView.state);
2136
2400
  /**
2137
2401
  * @type {Y.RelativePosition}
2138
2402
  */
@@ -2150,6 +2414,7 @@ const yCursorPlugin = (
2150
2414
  ystate.binding.mapping
2151
2415
  );
2152
2416
  if (
2417
+ opts.force ||
2153
2418
  current.cursor == null ||
2154
2419
  !Y__namespace.compareRelativePositions(
2155
2420
  Y__namespace.createRelativePositionFromJSON(current.cursor.anchor),
@@ -2178,14 +2443,23 @@ const yCursorPlugin = (
2178
2443
  awareness.setLocalStateField(cursorStateField, null);
2179
2444
  }
2180
2445
  };
2446
+ const onFocusChange = () => updateCursorInfo(view);
2181
2447
  awareness.on('change', awarenessListener);
2182
- view.dom.addEventListener('focusin', updateCursorInfo);
2183
- view.dom.addEventListener('focusout', updateCursorInfo);
2448
+ view.dom.addEventListener('focusin', onFocusChange);
2449
+ view.dom.addEventListener('focusout', onFocusChange);
2184
2450
  return {
2185
- update: updateCursorInfo,
2451
+ update: (editorView, prevState) => {
2452
+ const ystate = ySyncPluginKey.getState(editorView.state);
2453
+ const forceAwarenessUpdate = !!(
2454
+ ystate?.isChangeOrigin &&
2455
+ prevState?.doc &&
2456
+ !prevState.doc.eq(editorView.state.doc)
2457
+ );
2458
+ updateCursorInfo(editorView, { force: forceAwarenessUpdate });
2459
+ },
2186
2460
  destroy: () => {
2187
- view.dom.removeEventListener('focusin', updateCursorInfo);
2188
- view.dom.removeEventListener('focusout', updateCursorInfo);
2461
+ view.dom.removeEventListener('focusin', onFocusChange);
2462
+ view.dom.removeEventListener('focusout', onFocusChange);
2189
2463
  awareness.off('change', awarenessListener);
2190
2464
  awareness.setLocalStateField(cursorStateField, null);
2191
2465
  }
@@ -2315,8 +2589,12 @@ exports.defaultCursorBuilder = defaultCursorBuilder;
2315
2589
  exports.defaultDeleteFilter = defaultDeleteFilter;
2316
2590
  exports.defaultProtectedNodes = defaultProtectedNodes;
2317
2591
  exports.defaultSelectionBuilder = defaultSelectionBuilder;
2592
+ exports.findAbsolutePositionAfterStructuralChange = findAbsolutePositionAfterStructuralChange;
2318
2593
  exports.getRelativeSelection = getRelativeSelection;
2319
2594
  exports.initProseMirrorDoc = initProseMirrorDoc;
2595
+ exports.isMisresolvedAfterStructuralChange = isMisresolvedAfterStructuralChange;
2596
+ exports.isMisresolvedTextPosition = isMisresolvedTextPosition;
2597
+ exports.isStructuralTransaction = isStructuralTransaction;
2320
2598
  exports.isVisible = isVisible;
2321
2599
  exports.prosemirrorJSONToYDoc = prosemirrorJSONToYDoc;
2322
2600
  exports.prosemirrorJSONToYXmlFragment = prosemirrorJSONToYXmlFragment;