@tiptap/y-tiptap 3.0.5 → 3.0.7

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/y-tiptap.js CHANGED
@@ -18,6 +18,7 @@ import * as eventloop from 'lib0/eventloop';
18
18
  import * as map from 'lib0/map';
19
19
  import * as sha256 from 'lib0/hash/sha256';
20
20
  import * as buf from 'lib0/buffer';
21
+ import { ReplaceStep } from 'prosemirror-transform';
21
22
 
22
23
  /**
23
24
  * The unique prosemirror plugin key for syncPlugin
@@ -280,12 +281,38 @@ const ySyncPlugin = (yXmlFragment, {
280
281
  return plugin
281
282
  };
282
283
 
284
+ /**
285
+ * Resolves one text-selection endpoint after a remote update. Falls back to
286
+ * content-based matching when the Yjs resolution is missing or lands in the
287
+ * wrong block, and keeps the Yjs resolution when the fallback finds nothing.
288
+ *
289
+ * @param {import('prosemirror-model').Node} newDoc
290
+ * @param {import('prosemirror-model').Node} oldDoc
291
+ * @param {number|null|undefined} oldAbs
292
+ * @param {number|null} resolved
293
+ * @return {number|null}
294
+ */
295
+ const recoverSelectionEndpoint = (newDoc, oldDoc, oldAbs, resolved) => {
296
+ if (oldAbs == null) {
297
+ return resolved
298
+ }
299
+ const misresolved = resolved === null ||
300
+ (oldAbs > 1 && resolved <= 1) ||
301
+ isMisresolvedAfterStructuralChange(oldDoc, newDoc, oldAbs, resolved);
302
+ if (!misresolved) {
303
+ return resolved
304
+ }
305
+ const recovered = findAbsolutePositionAfterStructuralChange(oldDoc, newDoc, oldAbs);
306
+ return recovered !== null ? recovered : resolved
307
+ };
308
+
283
309
  /**
284
310
  * @param {import('prosemirror-state').Transaction} tr
285
311
  * @param {ReturnType<typeof getRelativeSelection>} relSel
286
312
  * @param {ProsemirrorBinding} binding
313
+ * @param {import('prosemirror-model').Node} [oldDoc]
287
314
  */
288
- const restoreRelativeSelection = (tr, relSel, binding) => {
315
+ const restoreRelativeSelection = (tr, relSel, binding, oldDoc) => {
289
316
  if (relSel !== null && relSel.anchor !== null && relSel.head !== null) {
290
317
  if (relSel.type === 'all') {
291
318
  tr.setSelection(new AllSelection(tr.doc));
@@ -319,18 +346,30 @@ const restoreRelativeSelection = (tr, relSel, binding) => {
319
346
  tr.setSelection(selection);
320
347
  }
321
348
  } else {
322
- const anchor = relativePositionToAbsolutePosition(
349
+ let anchor = relativePositionToAbsolutePosition(
323
350
  binding.doc,
324
351
  binding.type,
325
352
  relSel.anchor,
326
353
  binding.mapping
327
354
  );
328
- const head = relativePositionToAbsolutePosition(
355
+ let head = relativePositionToAbsolutePosition(
329
356
  binding.doc,
330
357
  binding.type,
331
358
  relSel.head,
332
359
  binding.mapping
333
360
  );
361
+ if (oldDoc != null) {
362
+ anchor = recoverSelectionEndpoint(tr.doc, oldDoc, relSel.absAnchor, anchor);
363
+ head = recoverSelectionEndpoint(tr.doc, oldDoc, relSel.absHead, head);
364
+ }
365
+ // Collapse to the surviving endpoint instead of dropping the selection;
366
+ // an unset selection maps through the full-doc replace to the doc start.
367
+ if (anchor === null) {
368
+ anchor = head;
369
+ }
370
+ if (head === null) {
371
+ head = anchor;
372
+ }
334
373
  if (anchor !== null && head !== null) {
335
374
  tr.setSelection(TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head)));
336
375
  }
@@ -411,7 +450,9 @@ const getRelativeSelection = (pmbinding, state) => {
411
450
  state.selection.head,
412
451
  pmbinding.type,
413
452
  pmbinding.mapping
414
- )
453
+ ),
454
+ absAnchor: state.selection.anchor,
455
+ absHead: state.selection.head
415
456
  }
416
457
  };
417
458
 
@@ -736,6 +777,9 @@ class ProsemirrorBinding {
736
777
  );
737
778
  transaction.changed.forEach(delType);
738
779
  transaction.changedParentTypes.forEach(delType);
780
+ // Rebuild the full Y↔PM mapping so relative cursor positions resolve against
781
+ // current node sizes after structural changes (e.g. drag-and-drop block moves).
782
+ this.mapping.clear();
739
783
  const fragmentContent = this.type.toArray().map((t) =>
740
784
  createNodeIfNotExists(
741
785
  /** @type {Y.XmlElement | Y.XmlHook} */ (t),
@@ -743,13 +787,14 @@ class ProsemirrorBinding {
743
787
  this
744
788
  )
745
789
  ).filter((n) => n !== null);
790
+ const oldDoc = this.prosemirrorView.state.doc;
746
791
  // @ts-ignore
747
792
  let tr = this._tr.replace(
748
793
  0,
749
794
  this.prosemirrorView.state.doc.content.size,
750
795
  new PModel.Slice(PModel.Fragment.from(fragmentContent), 0, 0)
751
796
  );
752
- restoreRelativeSelection(tr, this.beforeTransactionSelection, this);
797
+ restoreRelativeSelection(tr, this.beforeTransactionSelection, this, oldDoc);
753
798
  tr = tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, isUndoRedoOperation: transaction.origin instanceof Y.UndoManager });
754
799
  if (
755
800
  this.beforeTransactionSelection !== null && this._isLocalCursorInView()
@@ -1635,6 +1680,26 @@ const absolutePositionToRelativePosition = (pos, type, mapping) => {
1635
1680
  return Y.createRelativePositionFromTypeIndex(type, type._length, -1)
1636
1681
  };
1637
1682
 
1683
+ /**
1684
+ * Item-id based relative positions can misresolve to the document start after
1685
+ * block reorder during collaborative drag-and-drop.
1686
+ *
1687
+ * @param {Y.Doc} y
1688
+ * @param {Y.RelativePosition} relPos
1689
+ * @param {number|null} absPos
1690
+ * @return {boolean}
1691
+ */
1692
+ const isMisresolvedTextPosition = (y, relPos, absPos) => {
1693
+ if (absPos === null) {
1694
+ return false
1695
+ }
1696
+ const decoded = Y.createAbsolutePositionFromRelativePosition(relPos, y);
1697
+ return decoded !== null &&
1698
+ decoded.type instanceof Y.XmlText &&
1699
+ relPos.item !== null &&
1700
+ absPos <= 1
1701
+ };
1702
+
1638
1703
  const createRelativePosition = (type, item) => {
1639
1704
  let typeid = null;
1640
1705
  let tname = null;
@@ -1712,7 +1777,279 @@ const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) =>
1712
1777
  }
1713
1778
  type = /** @type {Y.AbstractType} */ (parent);
1714
1779
  }
1715
- return pos - 1 // we don't count the most outer tag, because it is a fragment
1780
+ const absPos = pos - 1; // we don't count the most outer tag, because it is a fragment
1781
+ if (isMisresolvedTextPosition(y, relPos, absPos)) {
1782
+ return null
1783
+ }
1784
+ return absPos
1785
+ };
1786
+
1787
+ /**
1788
+ * Shallow attrs comparison. Attr values are primitives in most schemas;
1789
+ * non-primitive values fail the check and callers fall back to text matching.
1790
+ *
1791
+ * @param {Object<string, any>} a
1792
+ * @param {Object<string, any>} b
1793
+ * @return {boolean}
1794
+ */
1795
+ const attrsEqual = (a, b) => {
1796
+ if (a === b) {
1797
+ return true
1798
+ }
1799
+ const aKeys = Object.keys(a);
1800
+ return aKeys.length === Object.keys(b).length && aKeys.every((k) => a[k] === b[k])
1801
+ };
1802
+
1803
+ /**
1804
+ * Returns true when any attr deviates from its spec default or has none.
1805
+ * Default-only attrs cannot tell same-type siblings apart.
1806
+ *
1807
+ * @param {import('prosemirror-model').Node} node
1808
+ * @return {boolean}
1809
+ */
1810
+ const hasDistinctiveAttrs = (node) => {
1811
+ const specAttrs = node.type.spec.attrs || {};
1812
+ return Object.keys(node.attrs).some((key) => {
1813
+ const spec = specAttrs[key];
1814
+ return spec == null ||
1815
+ !Object.prototype.hasOwnProperty.call(spec, 'default') ||
1816
+ spec.default !== node.attrs[key]
1817
+ })
1818
+ };
1819
+
1820
+ /**
1821
+ * Remaps a position into a matched block by walking the same child-index path
1822
+ * it had in the old block. A raw byte offset would overshoot into a sibling
1823
+ * inner textblock when the old block contains local keystrokes that are not
1824
+ * yet part of the rebuilt document.
1825
+ *
1826
+ * @param {import('prosemirror-model').ResolvedPos} $oldPos
1827
+ * @param {number} newBlockStart
1828
+ * @param {import('prosemirror-model').Node} newBlock
1829
+ * @return {number|null}
1830
+ */
1831
+ const remapIntoBlock = ($oldPos, newBlockStart, newBlock) => {
1832
+ let pos = newBlockStart + 1;
1833
+ let node = newBlock;
1834
+ for (let depth = 1; depth < $oldPos.depth; depth++) {
1835
+ const idx = $oldPos.index(depth);
1836
+ if (idx >= node.childCount) {
1837
+ return null
1838
+ }
1839
+ for (let i = 0; i < idx; i++) {
1840
+ pos += node.child(i).nodeSize;
1841
+ }
1842
+ pos += 1;
1843
+ node = node.child(idx);
1844
+ if (node.type !== $oldPos.node(depth + 1).type) {
1845
+ return null
1846
+ }
1847
+ }
1848
+ if (!node.isTextblock) {
1849
+ return null
1850
+ }
1851
+ return pos + Math.min($oldPos.parentOffset, node.content.size)
1852
+ };
1853
+
1854
+ /**
1855
+ * @param {import('prosemirror-model').Node} oldDoc
1856
+ * @param {import('prosemirror-model').Node} newDoc
1857
+ * @param {number} absPos
1858
+ * @return {number|null}
1859
+ */
1860
+ const findAbsolutePositionAfterStructuralChange = (oldDoc, newDoc, absPos) => {
1861
+ let pos = 0;
1862
+ let targetIdx = 0;
1863
+ for (; targetIdx < oldDoc.childCount; targetIdx++) {
1864
+ const child = oldDoc.child(targetIdx);
1865
+ if (pos + child.nodeSize > absPos) {
1866
+ break
1867
+ }
1868
+ pos += child.nodeSize;
1869
+ }
1870
+ if (targetIdx >= oldDoc.childCount) {
1871
+ return null
1872
+ }
1873
+ const targetChild = oldDoc.child(targetIdx);
1874
+ const $oldPos = oldDoc.resolve(absPos);
1875
+
1876
+ /**
1877
+ * @param {number} newBlockStart
1878
+ * @param {import('prosemirror-model').Node} newBlock
1879
+ * @return {number|null}
1880
+ */
1881
+ const place = (newBlockStart, newBlock) => {
1882
+ // Positions between top-level blocks carry no inner path; clamp them just
1883
+ // inside the matched block like the previous raw-offset remap did.
1884
+ if ($oldPos.depth === 0) {
1885
+ const remapped = newBlockStart + (absPos - pos);
1886
+ const contentStart = newBlockStart + 1;
1887
+ const contentEnd = newBlockStart + newBlock.nodeSize - 1;
1888
+ return Math.max(contentStart, Math.min(remapped, contentEnd))
1889
+ }
1890
+ return remapIntoBlock($oldPos, newBlockStart, newBlock)
1891
+ };
1892
+
1893
+ /**
1894
+ * Finds the Nth block in newDoc matching `pred`, where N is the number of
1895
+ * matching blocks in oldDoc up to and including the target block.
1896
+ *
1897
+ * @param {function(import('prosemirror-model').Node): boolean} pred
1898
+ * @param {boolean} requireUnique
1899
+ * @return {number|null}
1900
+ */
1901
+ const findByPredicate = (pred, requireUnique = false) => {
1902
+ let occurrence = 0;
1903
+ for (let i = 0; i <= targetIdx; i++) {
1904
+ if (pred(oldDoc.child(i))) {
1905
+ occurrence++;
1906
+ }
1907
+ }
1908
+ let matchCount = 0;
1909
+ let matchStart = -1;
1910
+ let matchBlock = null;
1911
+ let newPos = 0;
1912
+ for (let i = 0; i < newDoc.childCount; i++) {
1913
+ const child = newDoc.child(i);
1914
+ if (pred(child)) {
1915
+ matchCount++;
1916
+ if (matchCount === occurrence) {
1917
+ matchStart = newPos;
1918
+ matchBlock = child;
1919
+ }
1920
+ }
1921
+ newPos += child.nodeSize;
1922
+ }
1923
+ if (matchBlock === null || (requireUnique && (occurrence !== 1 || matchCount !== 1))) {
1924
+ return null
1925
+ }
1926
+ return place(matchStart, matchBlock)
1927
+ };
1928
+
1929
+ /**
1930
+ * @param {import('prosemirror-model').Node} child
1931
+ * @return {boolean}
1932
+ */
1933
+ const sameTypeAndAttrs = (child) =>
1934
+ child.type === targetChild.type && attrsEqual(child.attrs, targetChild.attrs);
1935
+ const oldText = targetChild.textContent;
1936
+
1937
+ const byAll = findByPredicate((child) => sameTypeAndAttrs(child) && child.textContent === oldText);
1938
+ if (byAll !== null) {
1939
+ return byAll
1940
+ }
1941
+
1942
+ // Text must be matched before attrs: after a remote attr-only edit, the
1943
+ // attrs pass would steer the cursor into a sibling that kept the old attrs.
1944
+ const byText = findByPredicate(
1945
+ (child) => child.type === targetChild.type && child.textContent === oldText
1946
+ );
1947
+ if (byText !== null) {
1948
+ return byText
1949
+ }
1950
+
1951
+ // In-flight local typing diverges the text between both docs. Distinctive
1952
+ // attrs still identify the block; default-only attrs match every sibling.
1953
+ if (hasDistinctiveAttrs(targetChild)) {
1954
+ const byAttrs = findByPredicate(sameTypeAndAttrs, true);
1955
+ if (byAttrs !== null) {
1956
+ return byAttrs
1957
+ }
1958
+ }
1959
+
1960
+ // Trailing in-flight keystrokes leave a prefix relation between old and new
1961
+ // text. Empty text is a prefix of everything and must never match.
1962
+ return findByPredicate(
1963
+ (child) => sameTypeAndAttrs(child) &&
1964
+ oldText !== '' && child.textContent !== '' &&
1965
+ (oldText.startsWith(child.textContent) || child.textContent.startsWith(oldText)),
1966
+ true
1967
+ )
1968
+ };
1969
+
1970
+ /**
1971
+ * Returns true when a transaction changes block structure rather than only
1972
+ * editing inline content inside existing blocks.
1973
+ *
1974
+ * @param {import('prosemirror-state').Transaction} tr
1975
+ * @param {import('prosemirror-model').Node} oldDoc
1976
+ * @return {boolean}
1977
+ */
1978
+ const isStructuralTransaction = (tr, oldDoc) => {
1979
+ if (!tr.docChanged) {
1980
+ return false
1981
+ }
1982
+ if (tr.doc.childCount !== oldDoc.childCount) {
1983
+ return true
1984
+ }
1985
+ for (const step of tr.steps) {
1986
+ if (step instanceof ReplaceStep) {
1987
+ if (step.from === 0 && step.to === oldDoc.content.size) {
1988
+ return true
1989
+ }
1990
+ if (step.slice.content.size > 0) {
1991
+ let hasBlock = false;
1992
+ step.slice.content.forEach((node) => {
1993
+ if (node.isBlock) {
1994
+ hasBlock = true;
1995
+ }
1996
+ });
1997
+ if (hasBlock) {
1998
+ return true
1999
+ }
2000
+ } else if (step.to > step.from) {
2001
+ const $from = oldDoc.resolve(step.from);
2002
+ const $to = oldDoc.resolve(step.to);
2003
+ if ($from.depth === 0 && $to.depth === 0 && $from.index() !== $to.index()) {
2004
+ return true
2005
+ }
2006
+ }
2007
+ }
2008
+ }
2009
+ return false
2010
+ };
2011
+
2012
+ /**
2013
+ * Detect stale relative positions after structural changes that resolve to the
2014
+ * wrong text block or to the start of the correct block.
2015
+ *
2016
+ * @param {import('prosemirror-model').Node} oldDoc
2017
+ * @param {import('prosemirror-model').Node} newDoc
2018
+ * @param {number} oldAbs
2019
+ * @param {number|null} resolvedAbs
2020
+ * @return {boolean}
2021
+ */
2022
+ const isMisresolvedAfterStructuralChange = (oldDoc, newDoc, oldAbs, resolvedAbs) => {
2023
+ if (resolvedAbs === null) {
2024
+ return false
2025
+ }
2026
+ const $old = oldDoc.resolve(oldAbs);
2027
+ const $new = newDoc.resolve(resolvedAbs);
2028
+ if (!$old.parent.isTextblock) {
2029
+ return false
2030
+ }
2031
+ // A textblock cursor cannot legitimately resolve into a non-textblock;
2032
+ // a structural reorder replaced the block via delete + insert.
2033
+ if (!$new.parent.isTextblock) {
2034
+ return true
2035
+ }
2036
+ if ($old.parent.textContent !== $new.parent.textContent) {
2037
+ return true
2038
+ }
2039
+ if ($old.parentOffset !== 0 && $new.parentOffset === 0) {
2040
+ return true
2041
+ }
2042
+ const bothAtStart = $old.parentOffset === 0 && $new.parentOffset === 0;
2043
+ // A changed offset, type or attrs hints at a same-text sibling. When all
2044
+ // of them agree there is no signal left and the Yjs resolution must win.
2045
+ const suspicious = $old.parentOffset !== $new.parentOffset ||
2046
+ $old.parent.type !== $new.parent.type ||
2047
+ !attrsEqual($old.parent.attrs, $new.parent.attrs);
2048
+ if (bothAtStart || suspicious) {
2049
+ const expected = findAbsolutePositionAfterStructuralChange(oldDoc, newDoc, oldAbs);
2050
+ return expected !== null && expected !== resolvedAbs
2051
+ }
2052
+ return false
1716
2053
  };
1717
2054
 
1718
2055
  /**
@@ -2131,11 +2468,21 @@ const yCursorPlugin = (
2131
2468
  selectionBuilder
2132
2469
  )
2133
2470
  },
2134
- apply (tr, prevState, _oldState, newState) {
2471
+ apply (tr, prevState, oldState, newState) {
2135
2472
  const ystate = ySyncPluginKey.getState(newState);
2136
2473
  const yCursorState = tr.getMeta(yCursorPluginKey);
2474
+ const isRemoteChange = ystate && ystate.isChangeOrigin;
2137
2475
  if (
2138
- (ystate && ystate.isChangeOrigin) ||
2476
+ tr.docChanged &&
2477
+ !isRemoteChange &&
2478
+ isStructuralTransaction(tr, oldState.doc)
2479
+ ) {
2480
+ // The ProseMirror document leads the Yjs mapping during local moves.
2481
+ // Hide stale awareness until the collaborator publishes its new cursor.
2482
+ return DecorationSet.empty
2483
+ }
2484
+ if (
2485
+ isRemoteChange ||
2139
2486
  (yCursorState && yCursorState.awarenessUpdated)
2140
2487
  ) {
2141
2488
  return createDecorations(
@@ -2146,7 +2493,10 @@ const yCursorPlugin = (
2146
2493
  selectionBuilder
2147
2494
  )
2148
2495
  }
2149
- return prevState.map(tr.mapping, tr.doc)
2496
+ if (tr.docChanged) {
2497
+ return prevState.map(tr.mapping, tr.doc)
2498
+ }
2499
+ return prevState
2150
2500
  }
2151
2501
  },
2152
2502
  props: {
@@ -2161,15 +2511,19 @@ const yCursorPlugin = (
2161
2511
  setMeta(view, yCursorPluginKey, { awarenessUpdated: true });
2162
2512
  }
2163
2513
  };
2164
- const updateCursorInfo = () => {
2165
- const ystate = ySyncPluginKey.getState(view.state);
2514
+ /**
2515
+ * @param {import('prosemirror-view').EditorView} editorView
2516
+ * @param {{ force?: boolean }} [opts]
2517
+ */
2518
+ const updateCursorInfo = (editorView, opts = {}) => {
2519
+ const ystate = ySyncPluginKey.getState(editorView.state);
2166
2520
  // `ystate` is undefined during sync-plugin init and Y.Doc transitions;
2167
2521
  // reading from it here would throw and break cursor tracking for the view.
2168
2522
  if (!ystate || !ystate.binding) return
2169
2523
  // @note We make implicit checks when checking for the cursor property
2170
2524
  const current = awareness.getLocalState() || {};
2171
- if (view.hasFocus()) {
2172
- const selection = getSelection(view.state);
2525
+ if (editorView.hasFocus()) {
2526
+ const selection = getSelection(editorView.state);
2173
2527
  /**
2174
2528
  * @type {Y.RelativePosition}
2175
2529
  */
@@ -2187,6 +2541,7 @@ const yCursorPlugin = (
2187
2541
  ystate.binding.mapping
2188
2542
  );
2189
2543
  if (
2544
+ opts.force ||
2190
2545
  current.cursor == null ||
2191
2546
  !Y.compareRelativePositions(
2192
2547
  Y.createRelativePositionFromJSON(current.cursor.anchor),
@@ -2215,14 +2570,23 @@ const yCursorPlugin = (
2215
2570
  awareness.setLocalStateField(cursorStateField, null);
2216
2571
  }
2217
2572
  };
2573
+ const onFocusChange = () => updateCursorInfo(view);
2218
2574
  awareness.on('change', awarenessListener);
2219
- view.dom.addEventListener('focusin', updateCursorInfo);
2220
- view.dom.addEventListener('focusout', updateCursorInfo);
2575
+ view.dom.addEventListener('focusin', onFocusChange);
2576
+ view.dom.addEventListener('focusout', onFocusChange);
2221
2577
  return {
2222
- update: updateCursorInfo,
2578
+ update: (editorView, prevState) => {
2579
+ const ystate = ySyncPluginKey.getState(editorView.state);
2580
+ const forceAwarenessUpdate = !!(
2581
+ ystate?.isChangeOrigin &&
2582
+ prevState?.doc &&
2583
+ !prevState.doc.eq(editorView.state.doc)
2584
+ );
2585
+ updateCursorInfo(editorView, { force: forceAwarenessUpdate });
2586
+ },
2223
2587
  destroy: () => {
2224
- view.dom.removeEventListener('focusin', updateCursorInfo);
2225
- view.dom.removeEventListener('focusout', updateCursorInfo);
2588
+ view.dom.removeEventListener('focusin', onFocusChange);
2589
+ view.dom.removeEventListener('focusout', onFocusChange);
2226
2590
  awareness.off('change', awarenessListener);
2227
2591
  awareness.setLocalStateField(cursorStateField, null);
2228
2592
  }
@@ -2344,5 +2708,5 @@ const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins =
2344
2708
  }
2345
2709
  });
2346
2710
 
2347
- export { ProsemirrorBinding, absolutePositionToRelativePosition, createDecorations, defaultAwarenessStateFilter, defaultCursorBuilder, defaultDeleteFilter, defaultProtectedNodes, defaultSelectionBuilder, getRelativeSelection, initProseMirrorDoc, isVisible, prosemirrorJSONToYDoc, prosemirrorJSONToYXmlFragment, prosemirrorToYDoc, prosemirrorToYXmlFragment, redo, relativePositionToAbsolutePosition, setMeta, undo, updateYFragment, yCursorPlugin, yCursorPluginKey, yDocToProsemirror, yDocToProsemirrorJSON, ySyncPlugin, ySyncPluginKey, yUndoPlugin, yUndoPluginKey, yXmlFragmentToProseMirrorFragment, yXmlFragmentToProseMirrorRootNode, yXmlFragmentToProsemirror, yXmlFragmentToProsemirrorJSON };
2711
+ export { ProsemirrorBinding, absolutePositionToRelativePosition, createDecorations, defaultAwarenessStateFilter, defaultCursorBuilder, defaultDeleteFilter, defaultProtectedNodes, defaultSelectionBuilder, findAbsolutePositionAfterStructuralChange, getRelativeSelection, initProseMirrorDoc, isMisresolvedAfterStructuralChange, isMisresolvedTextPosition, isStructuralTransaction, isVisible, prosemirrorJSONToYDoc, prosemirrorJSONToYXmlFragment, prosemirrorToYDoc, prosemirrorToYXmlFragment, redo, relativePositionToAbsolutePosition, setMeta, undo, updateYFragment, yCursorPlugin, yCursorPluginKey, yDocToProsemirror, yDocToProsemirrorJSON, ySyncPlugin, ySyncPluginKey, yUndoPlugin, yUndoPluginKey, yXmlFragmentToProseMirrorFragment, yXmlFragmentToProseMirrorRootNode, yXmlFragmentToProsemirror, yXmlFragmentToProsemirrorJSON };
2348
2712
  //# sourceMappingURL=y-tiptap.js.map