Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); //#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let yjs = require("yjs"); yjs = __toESM(yjs, 1); let prosemirror_view = require("prosemirror-view"); let prosemirror_state = require("prosemirror-state"); let prosemirror_model = require("prosemirror-model"); prosemirror_model = __toESM(prosemirror_model, 1); let lib0_error = require("lib0/error"); lib0_error = __toESM(lib0_error, 1); let prosemirror_transform = require("prosemirror-transform"); let lib0_eventloop = require("lib0/eventloop"); lib0_eventloop = __toESM(lib0_eventloop, 1); let lib0_math = require("lib0/math"); lib0_math = __toESM(lib0_math, 1); let lib0_mutex = require("lib0/mutex"); let lib0_environment = require("lib0/environment"); lib0_environment = __toESM(lib0_environment, 1); let lib0_dom = require("lib0/dom"); lib0_dom = __toESM(lib0_dom, 1); let lib0_set = require("lib0/set"); lib0_set = __toESM(lib0_set, 1); let lib0_random = require("lib0/random"); lib0_random = __toESM(lib0_random, 1); let lib0_map = require("lib0/map"); lib0_map = __toESM(lib0_map, 1); let lib0_hash_sha256 = require("lib0/hash/sha256"); lib0_hash_sha256 = __toESM(lib0_hash_sha256, 1); let lib0_buffer = require("lib0/buffer"); lib0_buffer = __toESM(lib0_buffer, 1); let lib0_object = require("lib0/object"); lib0_object = __toESM(lib0_object, 1); let lib0_diff = require("lib0/diff"); //#region src/lib/utils/positions/getYType.ts function getYType(item) { if (!item) return null; if (!(item.content instanceof yjs.ContentType)) return null; const type = item.content.type; if (!(type instanceof yjs.XmlFragment || type instanceof yjs.Text || type instanceof yjs.XmlHook)) throw new Error("Expected a Yjs XML type"); return type; } //#endregion //#region src/lib/utils/positions/getYParent.ts function getYParent(item) { if (item === null) return null; if (!(item.parent instanceof yjs.AbstractType)) throw new Error("Expected an integrated Yjs parent"); const type = item.parent; if (!(type instanceof yjs.XmlFragment || type instanceof yjs.Text || type instanceof yjs.XmlHook)) throw new Error("Expected a Yjs XML type"); return type; } //#endregion //#region src/lib/utils/positions/createRelativePosition.ts const createRelativePosition = (type, item) => { let typeid = null; let tname = null; if (type._item === null) tname = yjs.findRootTypeKey(type); else typeid = yjs.createID(type._item.id.client, type._item.id.clock); return new yjs.RelativePosition(typeid, tname, item.id); }; //#endregion //#region src/lib/utils/positions/absolutePositionToRelativePosition.ts /** Converts a ProseMirror position to a Yjs relative position. */ const absolutePositionToRelativePosition = (pos, type, mapping) => { if (pos === 0) return yjs.createRelativePositionFromTypeIndex(type, 0, -1); let n = getYType(type._first); while (n !== null && type !== n) { if (n instanceof yjs.XmlText) { if (n._length >= pos) return yjs.createRelativePositionFromTypeIndex(n, pos, -1); else pos -= n._length; if (n._item !== null && n._item.next !== null) n = getYType(n._item.next); else { do { n = getYParent(n._item); pos--; } while (n !== type && n !== null && n._item !== null && n._item.next === null); if (n !== null && n !== type) n = n._item === null ? null : getYType(n._item.next); } } else { const mapped = mapping.get(n); const pNodeSize = mapped instanceof prosemirror_model.Node ? mapped.nodeSize : 0; if (n._first !== null && pos < pNodeSize) { n = getYType(n._first); pos--; } else { if (pos === 1 && n._length === 0 && pNodeSize > 1) return new yjs.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? yjs.findRootTypeKey(n) : null, null); pos -= pNodeSize; if (n._item !== null && n._item.next !== null) n = getYType(n._item.next); else { var _n$_item; if (pos === 0) { n = n._item === null ? n : getYParent(n._item); if (n === null) throw lib0_error.unexpectedCase(); return new yjs.RelativePosition(n._item === null ? null : n._item.id, n._item === null ? yjs.findRootTypeKey(n) : null, null); } do { n = getYParent(n._item); if (n === null) throw lib0_error.unexpectedCase(); pos--; } while (n !== type && ((_n$_item = n._item) === null || _n$_item === void 0 ? void 0 : _n$_item.next) === null); if (n !== type) { var _n$_item2; n = getYType((_n$_item2 = n._item) === null || _n$_item2 === void 0 ? void 0 : _n$_item2.next); } } } } if (n === null) throw lib0_error.unexpectedCase(); if (pos === 0 && n.constructor !== yjs.XmlText && n !== type) { const item = n._item; const parent = getYParent(item); if (item === null || parent === null) throw lib0_error.unexpectedCase(); return createRelativePosition(parent, item); } } return yjs.createRelativePositionFromTypeIndex(type, type._length, -1); }; //#endregion //#region src/lib/utils/positions/isStructuralTransaction.ts /** Reports whether a transaction changes the document structure. */ const isStructuralTransaction = (tr, oldDoc) => { if (!tr.docChanged) return false; if (tr.doc.childCount !== oldDoc.childCount) return true; for (const step of tr.steps) if (step instanceof prosemirror_transform.ReplaceStep) { if (step.from === 0 && step.to === oldDoc.content.size) return true; if (step.slice.content.size > 0) { let hasBlock = false; step.slice.content.forEach((node) => { if (node.isBlock) hasBlock = true; }); if (hasBlock) return true; } else if (step.to > step.from) { const $from = oldDoc.resolve(step.from); const $to = oldDoc.resolve(step.to); if ($from.depth === 0 && $to.depth === 0 && $from.index() !== $to.index()) return true; } } return false; }; //#endregion //#region src/lib/utils/positions/getYTypeSize.ts function getYTypeSize(type, mapping) { if (type instanceof yjs.XmlText) return type._length; const node = mapping.get(type); return node instanceof prosemirror_model.Node ? node.nodeSize : null; } //#endregion //#region src/lib/utils/positions/getParentOffset.ts function getParentOffset(parent, type, mapping) { let offset = 0; let item = parent._first; while (item !== null) { const child = getYType(item); if (child === type) break; const deleted = item.deleted; item = item.right; if (deleted) continue; if (child === null) return null; const size = getYTypeSize(child, mapping); if (size === null) return null; offset += size; } return offset; } //#endregion //#region src/lib/utils/positions/getYContentOffset.ts function getYContentOffset(type, index, mapping) { let offset = 0; let childIndex = 0; let item = type._first; while (childIndex < type._length && childIndex < index && item !== null) { const current = item; item = item.right; if (current.deleted) continue; const child = getYType(current); if (child === null) return null; const size = getYTypeSize(child, mapping); if (size === null) return null; childIndex++; offset += size; } return offset; } //#endregion //#region src/lib/utils/positions/isMisresolvedTextPosition.ts /** Detects a text position that resolved outside its original block. */ const isMisresolvedTextPosition = (y, relPos, absPos) => { var _decoded$type$_item; if (absPos === null) return false; const decoded = yjs.createAbsolutePositionFromRelativePosition(relPos, y); return decoded !== null && decoded.type instanceof yjs.XmlText && relPos.item !== null && ((_decoded$type$_item = decoded.type._item) === null || _decoded$type$_item === void 0 ? void 0 : _decoded$type$_item.deleted) === true; }; //#endregion //#region src/lib/utils/positions/migrations/getPositionMigrationStore.ts const getPositionMigrationStore = (fragment) => { if (!fragment.doc) throw new Error("The Yjs fragment must belong to a document"); const key = fragment._item ? `item:${fragment._item.id.client}:${fragment._item.id.clock}` : `root:${yjs.findRootTypeKey(fragment)}`; return fragment.doc.getMap(`__tiptap_position_migrations:${key}`); }; //#endregion //#region src/lib/utils/positions/migrations/getPositionMigrationCandidates.ts const indexes = /* @__PURE__ */ new WeakMap(); const getPositionMigrationCandidates = (store, position) => { var _position$item, _index$clients$get; const identity = (_position$item = position.item) !== null && _position$item !== void 0 ? _position$item : position.type; if (!identity) return []; let boundary; if (!position.item) boundary = position.assoc < 0 ? "start" : "end"; let index = indexes.get(store); if (!index || index.size !== store.size) { const clients = /* @__PURE__ */ new Map(); store.forEach((migration) => migration.transfers.forEach((transfer) => { var _clients$get; const candidates = (_clients$get = clients.get(transfer.source.client)) !== null && _clients$get !== void 0 ? _clients$get : []; candidates.push({ migration, transfer }); clients.set(transfer.source.client, candidates); })); index = { size: store.size, clients }; indexes.set(store, index); } const candidates = ((_index$clients$get = index.clients.get(identity.client)) !== null && _index$clients$get !== void 0 ? _index$clients$get : []).filter(({ transfer }) => transfer.source.boundary === boundary && transfer.source.clock <= identity.clock && identity.clock < transfer.source.clock + transfer.source.length); return candidates.filter((candidate) => !candidates.some((other) => other.migration !== candidate.migration && other.migration.seen.some(([client, clock]) => client === candidate.migration.client && clock > candidate.migration.clock))); }; //#endregion //#region src/lib/utils/positions/migrations/resolvePositionMigration.ts const destination = (candidate, identity, assoc) => { var _target$tname, _target$assoc; const { source, target } = candidate.transfer; if (!target) return null; return new yjs.RelativePosition(target.boundary ? yjs.createID(target.client, target.clock) : target.type, (_target$tname = target.tname) !== null && _target$tname !== void 0 ? _target$tname : null, target.boundary ? null : yjs.createID(target.client, target.clock + identity.clock - source.clock), (_target$assoc = target.assoc) !== null && _target$assoc !== void 0 ? _target$assoc : assoc); }; const commonDestination = (candidates, identity, assoc) => { const next = destination(candidates[0], identity, assoc); if (!next || candidates.some((candidate) => { const other = destination(candidate, identity, assoc); return !other || !yjs.compareRelativePositions(next, other); })) return null; return next; }; const positionIdentity = (position) => { var _position$item; const identity = (_position$item = position.item) !== null && _position$item !== void 0 ? _position$item : position.type; if (!identity) return null; let boundary = position.assoc < 0 ? "start" : "end"; if (position.item) boundary = "item"; return `${identity.client}:${identity.clock}:${boundary}`; }; const resolvePositionMigration = (fragment, position) => { var _current$item; const store = getPositionMigrationStore(fragment); const visited = /* @__PURE__ */ new Set(); let current = position; let identity = (_current$item = current.item) !== null && _current$item !== void 0 ? _current$item : current.type; while (identity) { var _next$item; const key = positionIdentity(current); if (visited.has(key)) return null; visited.add(key); const candidates = getPositionMigrationCandidates(store, current); if (candidates.length === 0) return current; const next = commonDestination(candidates, identity, current.assoc); if (!next) return null; if (positionIdentity(next) === key) return next; current = next; identity = (_next$item = next.item) !== null && _next$item !== void 0 ? _next$item : next.type; } return current; }; //#endregion //#region src/lib/utils/positions/relativePositionToAbsolutePosition.ts /** Resolves a Yjs relative position, returning null when it cannot be mapped. */ const relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) => { var _type$_item; const migrated = resolvePositionMigration(documentType, relPos); if (!migrated) return null; relPos = migrated; const decodedPos = yjs.createAbsolutePositionFromRelativePosition(relPos, y); if (decodedPos === null || decodedPos.type !== documentType && !yjs.isParentOf(documentType, decodedPos.type._item)) return null; let type = decodedPos.type; let pos = 0; if (type.constructor === yjs.XmlText) pos = decodedPos.index; else if (!((_type$_item = type._item) === null || _type$_item === void 0 ? void 0 : _type$_item.deleted)) { const offset = getYContentOffset(type, decodedPos.index, mapping); if (offset === null) return null; pos = offset + 1; } while (type !== documentType && type._item !== null) { var _parent$_item; const parent = getYParent(type._item); if (parent === null) return null; if ((_parent$_item = parent._item) === null || _parent$_item === void 0 ? void 0 : _parent$_item.deleted) { type = parent; continue; } const offset = getParentOffset(parent, type, mapping); if (offset === null) return null; pos += offset + 1; type = parent; } const absPos = pos - 1; if (isMisresolvedTextPosition(y, relPos, absPos)) return null; return absPos; }; //#endregion //#region src/lib/plugins/keys.ts const ySyncPluginKey = new prosemirror_state.PluginKey("y-sync"); const yUndoPluginKey = new prosemirror_state.PluginKey("y-undo"); const yCursorPluginKey = new prosemirror_state.PluginKey("yjs-cursor"); //#endregion //#region src/lib/plugins/setMeta.ts let viewsToUpdate = null; function takePendingMetas() { const pending = viewsToUpdate; viewsToUpdate = null; const entries = []; pending === null || pending === void 0 || pending.forEach((metas, view) => { metas.forEach((value, key) => entries.push({ view, key, value })); }); return entries; } function updateMetas(entries = takePendingMetas(), isRetry = false) { let isFirst = true; while (entries.length > 0) { const { view, key, value } = entries[0]; try { const syncState = ySyncPluginKey.getState(view.state); if (syncState && syncState.binding.prosemirrorView !== null) view.dispatch(view.state.tr.setMeta(key, value)); } catch (error) { if (!(error instanceof RangeError)) throw error; if (isRetry && isFirst) entries.shift(); if (entries.length > 0) (0, lib0_eventloop.timeout)(0, () => updateMetas(entries, true)); return; } isFirst = false; entries.shift(); } } /** Queues plugin metadata and batches repeated updates for the same key. */ function setMeta(view, key, value) { if (viewsToUpdate === null) { viewsToUpdate = /* @__PURE__ */ new Map(); (0, lib0_eventloop.timeout)(0, updateMetas); } let metas = viewsToUpdate.get(view); if (!metas) { metas = /* @__PURE__ */ new Map(); viewsToUpdate.set(view, metas); } metas.set(key, value); } //#endregion //#region src/lib/plugins/yCursorPlugin.ts const defaultAwarenessStateFilter = (currentClientId, userClientId, _user) => currentClientId !== userClientId; /** Creates the default remote cursor element from awareness user details. */ const defaultCursorBuilder = (user) => { const cursor = document.createElement("span"); cursor.classList.add("ProseMirror-yjs-cursor"); cursor.setAttribute("style", `border-color: ${user.color}`); const userDiv = document.createElement("div"); userDiv.setAttribute("style", `background-color: ${user.color}`); userDiv.insertBefore(document.createTextNode(user.name), null); const nonbreakingSpace1 = document.createTextNode("⁠"); const nonbreakingSpace2 = document.createTextNode("⁠"); cursor.insertBefore(nonbreakingSpace1, null); cursor.insertBefore(userDiv, null); cursor.insertBefore(nonbreakingSpace2, null); return cursor; }; /** Creates the default decoration attributes for a remote selection. */ const defaultSelectionBuilder = (user) => { return { style: `background-color: ${user.color}70`, class: "ProseMirror-yjs-selection" }; }; const rxValidColor = /^#[0-9a-fA-F]{6}$/; const hasCursorUser = (user) => typeof user.name === "string" && typeof user.color === "string"; const createDecorations = (state, awareness, awarenessFilter, createCursor, createSelection) => { const ystate = ySyncPluginKey.getState(state); if (ystate == null || ystate.doc == null || ystate.binding == null) return prosemirror_view.DecorationSet.create(state.doc, []); const y = ystate.doc; const decorations = []; if (ystate.snapshot != null || ystate.prevSnapshot != null || ystate.binding.mapping.size === 0) return prosemirror_view.DecorationSet.create(state.doc, []); awareness.getStates().forEach((aw, clientId) => { if (!awarenessFilter(y.clientID, clientId, aw)) return; if (aw && aw.cursor != null) { const user = { ...aw.user }; if (user.color == null) user.color = "#ffa500"; else if (!rxValidColor.test(user.color)) console.warn("A user uses an unsupported color format", user); if (user.name == null) user.name = `User: ${clientId}`; if (!hasCursorUser(user)) return; let anchor = relativePositionToAbsolutePosition(y, ystate.type, yjs.createRelativePositionFromJSON(aw.cursor.anchor), ystate.binding.mapping); let head = relativePositionToAbsolutePosition(y, ystate.type, yjs.createRelativePositionFromJSON(aw.cursor.head), ystate.binding.mapping); if (anchor !== null && head !== null) { const maxsize = lib0_math.max(state.doc.content.size - 1, 0); anchor = lib0_math.min(anchor, maxsize); head = lib0_math.min(head, maxsize); decorations.push(prosemirror_view.Decoration.widget(head, () => createCursor(user, clientId), { key: clientId + "", side: 10 })); const from = lib0_math.min(anchor, head); const to = lib0_math.max(anchor, head); decorations.push(prosemirror_view.Decoration.inline(from, to, createSelection(user, clientId), { inclusiveEnd: true, inclusiveStart: false })); } } }); return prosemirror_view.DecorationSet.create(state.doc, decorations); }; /** Renders remote cursors and publishes the local selection through awareness. */ const yCursorPlugin = (awareness, { awarenessStateFilter = defaultAwarenessStateFilter, cursorBuilder = defaultCursorBuilder, selectionBuilder = defaultSelectionBuilder, getSelection = (state) => state.selection } = {}, cursorStateField = "cursor") => new prosemirror_state.Plugin({ key: yCursorPluginKey, state: { init(_, state) { return createDecorations(state, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder); }, apply(tr, prevState, oldState, newState) { const ystate = ySyncPluginKey.getState(newState); const yCursorState = tr.getMeta(yCursorPluginKey); const isRemoteChange = ystate && ystate.isChangeOrigin; if (tr.docChanged && !isRemoteChange && isStructuralTransaction(tr, oldState.doc)) return prosemirror_view.DecorationSet.empty; if (isRemoteChange || yCursorState && yCursorState.awarenessUpdated) return createDecorations(newState, awareness, awarenessStateFilter, cursorBuilder, selectionBuilder); if (tr.docChanged) return prevState.map(tr.mapping, tr.doc); return prevState; } }, props: { decorations: (state) => { var _yCursorPluginKey$get; return (_yCursorPluginKey$get = yCursorPluginKey.getState(state)) !== null && _yCursorPluginKey$get !== void 0 ? _yCursorPluginKey$get : prosemirror_view.DecorationSet.empty; } }, view: (view) => { const awarenessListener = () => { if (!view.isDestroyed) setMeta(view, yCursorPluginKey, { awarenessUpdated: true }); }; const updateCursorInfo = (editorView) => { const ystate = ySyncPluginKey.getState(editorView.state); if (!ystate || !ystate.binding || ystate.binding.projectionError !== null) return; const current = awareness.getLocalState() || {}; if (editorView.hasFocus()) { const selection = getSelection(editorView.state); const anchor = absolutePositionToRelativePosition(selection.anchor, ystate.type, ystate.binding.mapping); const head = absolutePositionToRelativePosition(selection.head, ystate.type, ystate.binding.mapping); if (current.cursor == null || !yjs.compareRelativePositions(yjs.createRelativePositionFromJSON(current.cursor.anchor), anchor) || !yjs.compareRelativePositions(yjs.createRelativePositionFromJSON(current.cursor.head), head)) awareness.setLocalStateField(cursorStateField, { anchor, head }); } else if (current.cursor != null && relativePositionToAbsolutePosition(ystate.doc, ystate.type, yjs.createRelativePositionFromJSON(current.cursor.anchor), ystate.binding.mapping) !== null) awareness.setLocalStateField(cursorStateField, null); }; const onFocusChange = () => updateCursorInfo(view); awareness.on("change", awarenessListener); view.dom.addEventListener("focusin", onFocusChange); view.dom.addEventListener("focusout", onFocusChange); return { update: updateCursorInfo, destroy: () => { view.dom.removeEventListener("focusin", onFocusChange); view.dom.removeEventListener("focusout", onFocusChange); awareness.off("change", awarenessListener); awareness.setLocalStateField(cursorStateField, null); } }; } }); //#endregion //#region src/lib/plugins/getSyncPluginState.ts function getSyncPluginState(state) { const pluginState = ySyncPluginKey.getState(state); if (!pluginState) throw new Error("The editor requires ySyncPlugin"); return pluginState; } //#endregion //#region src/lib/utils/binding/isVisible.ts /** Reports whether a Yjs item is visible in the current document or a snapshot. */ const isVisible = (item, snapshot) => { var _snapshot$sv$get; return snapshot === void 0 ? !item.deleted : snapshot.sv.has(item.id.client) && ((_snapshot$sv$get = snapshot.sv.get(item.id.client)) !== null && _snapshot$sv$get !== void 0 ? _snapshot$sv$get : 0) > item.id.clock && !yjs.isDeleted(snapshot.ds, item.id); }; //#endregion //#region src/lib/utils/binding/getUserColor.ts const getUserColor = (colorMapping, colors, user) => { if (!colorMapping.has(user)) { if (colorMapping.size < colors.length) { const usedColors = lib0_set.create(); colorMapping.forEach((color) => usedColors.add(color)); colors = colors.filter((color) => !usedColors.has(color)); } colorMapping.set(user, lib0_random.oneOf(colors)); } const color = colorMapping.get(user); if (!color) throw new Error("At least one snapshot color is required"); return color; }; //#endregion //#region src/lib/utils/positions/attrsEqual.ts const attrsEqual = (a, b) => { if (a === b) return true; const aKeys = Object.keys(a); return aKeys.length === Object.keys(b).length && aKeys.every((k) => a[k] === b[k]); }; //#endregion //#region src/lib/utils/positions/hasDistinctiveAttrs.ts const hasDistinctiveAttrs = (node) => { const specAttrs = node.type.spec.attrs || {}; return Object.keys(node.attrs).some((key) => { const spec = specAttrs[key]; return spec == null || !Object.prototype.hasOwnProperty.call(spec, "default") || spec.default !== node.attrs[key]; }); }; //#endregion //#region src/lib/utils/positions/findAbsolutePositionAfterStructuralChange.ts /** Recovers a position in a moved block, returning null when the match is ambiguous. */ const findAbsolutePositionAfterStructuralChange = (oldDoc, newDoc, absPos) => { let pos = 0; let targetIdx = 0; for (; targetIdx < oldDoc.childCount; targetIdx++) { const child = oldDoc.child(targetIdx); if (pos + child.nodeSize > absPos) break; pos += child.nodeSize; } if (targetIdx >= oldDoc.childCount) return null; const targetChild = oldDoc.child(targetIdx); const $oldPos = oldDoc.resolve(absPos); const place = (newBlockStart, newBlock) => { if ($oldPos.depth === 0) { const remapped = newBlockStart + (absPos - pos); const contentStart = newBlockStart + 1; const contentEnd = newBlockStart + newBlock.nodeSize - 1; return Math.max(contentStart, Math.min(remapped, contentEnd)); } if ($oldPos.depth > 1) { const innerPosition = findAbsolutePositionAfterStructuralChange($oldPos.node(1), newBlock, $oldPos.pos - $oldPos.start(1)); return innerPosition === null ? null : newBlockStart + 1 + innerPosition; } if (!newBlock.isTextblock) return null; return newBlockStart + 1 + Math.min($oldPos.parentOffset, newBlock.content.size); }; const findByPredicate = (pred, requireUnique = false) => { let occurrence = 0; for (let i = 0; i <= targetIdx; i++) if (pred(oldDoc.child(i))) occurrence++; let matchCount = 0; let matchStart = -1; let matchBlock = null; let newPos = 0; for (let i = 0; i < newDoc.childCount; i++) { const child = newDoc.child(i); if (pred(child)) { matchCount++; if (matchCount === occurrence) { matchStart = newPos; matchBlock = child; } } newPos += child.nodeSize; } if (matchBlock === null || requireUnique && (occurrence !== 1 || matchCount !== 1)) return null; return place(matchStart, matchBlock); }; const sameTypeAndAttrs = (child) => child.type === targetChild.type && attrsEqual(child.attrs, targetChild.attrs); const oldText = targetChild.textContent; const byAll = findByPredicate((child) => sameTypeAndAttrs(child) && child.textContent === oldText); if (byAll !== null) return byAll; const byText = findByPredicate((child) => child.type === targetChild.type && child.textContent === oldText); if (byText !== null) return byText; if (!targetChild.isTextblock || hasDistinctiveAttrs(targetChild)) { const byAttrs = findByPredicate(sameTypeAndAttrs, true); if (byAttrs !== null) return byAttrs; } return findByPredicate((child) => sameTypeAndAttrs(child) && oldText !== "" && child.textContent !== "" && (oldText.startsWith(child.textContent) || child.textContent.startsWith(oldText)), true); }; //#endregion //#region src/lib/utils/positions/isMisresolvedAfterStructuralChange.ts /** Detects a position that moved into a different block. */ const isMisresolvedAfterStructuralChange = (oldDoc, newDoc, oldAbs, resolvedAbs) => { if (resolvedAbs === null) return false; const $old = oldDoc.resolve(oldAbs); const $new = newDoc.resolve(resolvedAbs); if (!$old.parent.isTextblock) return false; if (!$new.parent.isTextblock) return true; if ($old.parent.textContent !== $new.parent.textContent) return true; if ($old.parentOffset !== 0 && $new.parentOffset === 0) return true; const bothAtStart = $old.parentOffset === 0 && $new.parentOffset === 0; const suspicious = $old.parentOffset !== $new.parentOffset || $old.parent.type !== $new.parent.type || !attrsEqual($old.parent.attrs, $new.parent.attrs); if (bothAtStart || suspicious) { const expected = findAbsolutePositionAfterStructuralChange(oldDoc, newDoc, oldAbs); return expected !== null && expected !== resolvedAbs; } return false; }; //#endregion //#region src/lib/utils/positions/restoreRelativeSelection.ts const recoverSelectionEndpoint = (newDoc, oldDoc, oldAbs, resolved) => { if (oldAbs == null || oldAbs < 0 || oldAbs > oldDoc.content.size) return resolved; if (!(resolved === null || oldAbs > 1 && resolved <= 1 || isMisresolvedAfterStructuralChange(oldDoc, newDoc, oldAbs, resolved))) return resolved; const recovered = findAbsolutePositionAfterStructuralChange(oldDoc, newDoc, oldAbs); return recovered !== null ? recovered : resolved; }; const restoreRelativeSelection = (tr, relSel, binding, oldDoc) => { if (relSel !== null && relSel.anchor !== null && relSel.head !== null) { if (relSel.type === "all") tr.setSelection(new prosemirror_state.AllSelection(tr.doc)); else if (relSel.type === "node") { const anchor = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.anchor, binding.mapping); if (anchor !== null) tr.setSelection(createSafeNodeSelection(tr, anchor)); } else if (relSel.type === "nodeRange") { const anchor = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.anchor, binding.mapping); const head = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.head, binding.mapping); const selection = createSafeNodeRangeSelection(tr, anchor, head, relSel.depth); if (selection !== null) tr.setSelection(selection); } else { let anchor = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.anchor, binding.mapping); let head = relativePositionToAbsolutePosition(binding.doc, binding.type, relSel.head, binding.mapping); if (oldDoc != null) { if (resolvePositionMigration(binding.type, relSel.anchor) === relSel.anchor) anchor = recoverSelectionEndpoint(tr.doc, oldDoc, relSel.absAnchor, anchor); if (resolvePositionMigration(binding.type, relSel.head) === relSel.head) head = recoverSelectionEndpoint(tr.doc, oldDoc, relSel.absHead, head); } if (anchor === null) anchor = head; if (head === null) head = anchor; if (anchor !== null && head !== null) tr.setSelection(prosemirror_state.TextSelection.between(tr.doc.resolve(anchor), tr.doc.resolve(head))); } } }; const createSafeNodeSelection = (tr, pos) => { const $pos = tr.doc.resolve(pos); if ($pos.nodeAfter) return prosemirror_state.NodeSelection.create(tr.doc, pos); else return prosemirror_state.TextSelection.near($pos); }; const createSafeNodeRangeSelection = (tr, anchor, head, depth) => { if (anchor === null || head === null) return null; const clampedAnchor = Math.min(Math.max(anchor, 0), tr.doc.content.size); const clampedHead = Math.min(Math.max(head, 0), tr.doc.content.size); try { const selection = prosemirror_state.Selection.fromJSON(tr.doc, { type: "nodeRange", anchor: clampedAnchor, head: clampedHead, depth }); if (!selection.ranges.length) return prosemirror_state.TextSelection.near(tr.doc.resolve(clampedAnchor)); return selection; } catch { return prosemirror_state.TextSelection.near(tr.doc.resolve(clampedAnchor)); } }; //#endregion //#region src/lib/utils/positions/getRelativeSelection.ts /** Captures the current selection as Yjs relative positions and absolute fallback positions. */ const getRelativeSelection = (pmbinding, state) => { const selection = state.selection; const json = selection.toJSON(); const type = typeof json.type === "string" ? json.type : "text"; return { type, depth: type === "nodeRange" && "depth" in selection && typeof selection.depth === "number" ? selection.depth : void 0, anchor: absolutePositionToRelativePosition(state.selection.anchor, pmbinding.type, pmbinding.mapping), head: absolutePositionToRelativePosition(state.selection.head, pmbinding.type, pmbinding.mapping), absAnchor: state.selection.anchor, absHead: state.selection.head }; }; //#endregion //#region src/lib/utils/binding/projectedMappings.ts const projectedMappings = /* @__PURE__ */ new WeakMap(); //#endregion //#region src/lib/utils/conversion/yattr2markname.ts const hashedMarkNameRegex = /(.*)(--[a-zA-Z0-9+/=]{8})$/; const yattr2markname = (attrName) => { var _hashedMarkNameRegex$, _hashedMarkNameRegex$2; return (_hashedMarkNameRegex$ = (_hashedMarkNameRegex$2 = hashedMarkNameRegex.exec(attrName)) === null || _hashedMarkNameRegex$2 === void 0 ? void 0 : _hashedMarkNameRegex$2[1]) !== null && _hashedMarkNameRegex$ !== void 0 ? _hashedMarkNameRegex$ : attrName; }; //#endregion //#region src/lib/utils/conversion/attributesToMarks.ts const attributesToMarks = (attrs, schema) => { const marks = []; for (const markName in attrs) marks.push(schema.mark(yattr2markname(markName), attrs[markName])); return marks; }; //#endregion //#region src/lib/utils/conversion/createTextNodesFromYText.ts const createTextNodesFromYText = (text, schema, _meta, snapshot, prevSnapshot, computeYChange) => { return text.toDelta(snapshot, prevSnapshot, computeYChange).map((delta) => schema.text(delta.insert, attributesToMarks(delta.attributes, schema))); }; //#endregion //#region src/lib/utils/conversion/createNodeFromYElement.ts const createNodeFromYElement = (el, schema, meta, snapshot, prevSnapshot, computeYChange) => { var _projectedMappings$ge; const cachedNode = meta.mapping.get(el); if (cachedNode !== void 0 && !Array.isArray(cachedNode) && ((_projectedMappings$ge = projectedMappings.get(meta.mapping)) === null || _projectedMappings$ge === void 0 ? void 0 : _projectedMappings$ge.get(el)) === cachedNode) return cachedNode; const children = []; const createChildren = (type) => { if (type instanceof yjs.XmlElement) children.push(createNodeFromYElement(type, schema, meta, snapshot, prevSnapshot, computeYChange)); else if (type instanceof yjs.XmlText) children.push(...createTextNodesFromYText(type, schema, meta, snapshot, prevSnapshot, computeYChange)); else throw new Error("Cannot project a Yjs XML hook"); }; if (snapshot === void 0 || prevSnapshot === void 0) el.toArray().forEach(createChildren); else yjs.typeListToArraySnapshot(el, new yjs.Snapshot(prevSnapshot.ds, snapshot.sv)).forEach(createChildren); const attrs = el.getAttributes(snapshot); if (snapshot !== void 0 && el._item) { if (!isVisible(el._item, snapshot)) attrs.ychange = computeYChange ? computeYChange("removed", el._item.id) : { type: "removed" }; else if (!isVisible(el._item, prevSnapshot)) attrs.ychange = computeYChange ? computeYChange("added", el._item.id) : { type: "added" }; } let node = schema.node(el.nodeName, attrs, children); if (cachedNode !== void 0 && !Array.isArray(cachedNode) && cachedNode.eq(node)) node = cachedNode; meta.mapping.set(el, node); let projectedNodes = projectedMappings.get(meta.mapping); if (projectedNodes === void 0) { projectedNodes = /* @__PURE__ */ new WeakMap(); projectedMappings.set(meta.mapping, projectedNodes); } projectedNodes.set(el, node); return node; }; //#endregion //#region src/lib/utils/conversion/validateDocumentContent.ts const validateDocumentContent = (content, schema) => { if (content.childCount > 0 && !schema.topNodeType.validContent(content)) throw new RangeError("Shared content does not fit the document schema"); }; //#endregion //#region src/lib/utils/conversion/hashOfJSON.ts const foldDigest = (digest) => { const hashBytes = 6; for (let i = hashBytes; i < digest.length; i++) digest[i % hashBytes] = digest[i % hashBytes] ^ digest[i]; return digest.slice(0, hashBytes); }; const hashOfJSON = (json) => lib0_buffer.toBase64(foldDigest(lib0_hash_sha256.digest(lib0_buffer.encodeAny(json)))); //#endregion //#region src/lib/utils/conversion/marksToAttributes.ts const marksToAttributes = (marks, meta) => { const pattrs = {}; marks.forEach((mark) => { if (mark.type.name !== "ychange") { const isOverlapping = lib0_map.setIfUndefined(meta.isOMark, mark.type, () => !mark.type.excludes(mark.type)); pattrs[isOverlapping ? `${mark.type.name}--${hashOfJSON(mark.toJSON())}` : mark.type.name] = mark.attrs; } }); return pattrs; }; //#endregion //#region src/lib/utils/conversion/createTypeFromTextNodes.ts const createTypeFromTextNodes = (nodes, meta) => { const type = new yjs.XmlText(); const delta = nodes.map((node) => { var _node$text; return { insert: (_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : "", attributes: marksToAttributes(node.marks, meta) }; }); type.applyDelta(delta); meta.mapping.set(type, nodes); return type; }; //#endregion //#region src/lib/utils/conversion/normalizePNodeContent.ts const normalizePNodeContent = (pnode) => { const c = pnode.content.content; const res = []; for (let i = 0; i < c.length; i++) { const n = c[i]; if (n.isText) { const textNodes = []; for (let tnode = c[i]; i < c.length && tnode.isText; tnode = c[++i]) textNodes.push(tnode); i--; res.push(textNodes); } else res.push(n); } return res; }; //#endregion //#region src/lib/utils/conversion/createTypeFromTextOrElementNode.ts const createTypeFromTextOrElementNode = (node, meta) => node instanceof Array ? createTypeFromTextNodes(node, meta) : createTypeFromElementNode(node, meta); const createTypeFromElementNode = (node, meta) => { const type = new yjs.XmlElement(node.type.name); for (const key in node.attrs) { const val = node.attrs[key]; if (val !== null && key !== "ychange") type.setAttribute(key, val); } type.insert(0, normalizePNodeContent(node).map((n) => createTypeFromTextOrElementNode(n, meta))); meta.mapping.set(type, node); return type; }; //#endregion //#region src/lib/utils/reconciliation/isObject.ts const isObject = (val) => typeof val === "object" && val !== null; //#endregion //#region src/lib/utils/reconciliation/equalAttrs.ts const equalAttrs = (pattrs, yattrs) => { const keys = Object.keys(pattrs).filter((key) => pattrs[key] !== null); let eq = keys.length === Object.keys(yattrs).filter((key) => yattrs[key] !== null).length; for (let i = 0; i < keys.length && eq; i++) { const key = keys[i]; const l = pattrs[key]; const r = yattrs[key]; eq = key === "ychange" || l === r || isObject(l) && isObject(r) && equalAttrs(l, r); } return eq; }; //#endregion //#region src/lib/utils/reconciliation/equalYTextPText.ts const equalYTextPText = (ytext, ptexts) => { const delta = ytext.toDelta(); return delta.length === ptexts.length && delta.every((d, i) => { var _d$attributes; return d.insert === ptexts[i].text && lib0_object.keys(d.attributes || {}).length === ptexts[i].marks.length && Object.entries((_d$attributes = d.attributes) !== null && _d$attributes !== void 0 ? _d$attributes : {}).every(([yattrname, attr]) => { const markname = yattr2markname(yattrname); const pmark = ptexts[i].marks.find((mark) => mark.type.name === markname); if (!pmark) return false; return equalAttrs(attr, pmark.attrs); }); }); }; //#endregion //#region src/lib/utils/reconciliation/matchNodeName.ts const matchNodeName = (yElement, pNode) => !(pNode instanceof Array) && yElement.nodeName === pNode.type.name; //#endregion //#region src/lib/utils/reconciliation/equalYTypePNode.ts const equalYTypePNode = (ytype, pnode) => { if (ytype instanceof yjs.XmlElement && !(pnode instanceof Array) && matchNodeName(ytype, pnode)) { const normalizedContent = normalizePNodeContent(pnode); return ytype._length === normalizedContent.length && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i])); } return ytype instanceof yjs.XmlText && pnode instanceof Array && equalYTextPText(ytype, pnode); }; //#endregion //#region src/lib/utils/reconciliation/mappedIdentity.ts const mappedIdentity = (mapped, pcontent) => mapped === pcontent || mapped instanceof Array && pcontent instanceof Array && mapped.length === pcontent.length && mapped.every((a, i) => pcontent[i] === a); //#endregion //#region src/lib/utils/reconciliation/findMappedChildAnchors.ts const findMappedChildAnchors = (yChildren, pChildren, meta) => { const pChildIndices = /* @__PURE__ */ new Map(); pChildren.forEach((pChild, index) => { if (!pChildIndices.has(pChild)) pChildIndices.set(pChild, index); }); const candidates = []; yChildren.forEach((yChild, yIndex) => { const mapped = meta.mapping.get(yChild); const pIndex = mapped === void 0 ? void 0 : pChildIndices.get(mapped); if (pIndex !== void 0 && mappedIdentity(mapped, pChildren[pIndex])) candidates.push({ yIndex, pIndex, yChild }); }); if (candidates.length === 0) return []; const predecessors = []; const tails = []; for (let index = 0; index < candidates.length; index++) { let low = 0; let high = tails.length; while (low < high) { const middle = low + high >> 1; if (candidates[tails[middle]].pIndex < candidates[index].pIndex) low = middle + 1; else high = middle; } predecessors[index] = low > 0 ? tails[low - 1] : -1; tails[low] = index; } const anchors = []; for (let index = tails[tails.length - 1]; index !== -1; index = predecessors[index]) anchors.push(candidates[index]); return anchors.reverse(); }; //#endregion //#region src/lib/utils/reconciliation/ytextTrans.ts const ytextTrans = (ytext) => { let str = ""; let n = ytext._start; const nAttrs = {}; while (n !== null) { if (!n.deleted) { if (n.countable && n.content instanceof yjs.ContentString) str += n.content.str; else if (n.content instanceof yjs.ContentFormat) nAttrs[n.content.key] = null; } n = n.right; } return { str, nAttrs }; }; //#endregion //#region src/lib/utils/reconciliation/updateYText.ts const updateYText = (ytext, ptexts, meta) => { meta.mapping.set(ytext, ptexts); const { nAttrs, str } = ytextTrans(ytext); const content = ptexts.map((p) => { var _p$text; return { insert: (_p$text = p.text) !== null && _p$text !== void 0 ? _p$text : "", attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks, meta)) }; }); const { insert, remove, index } = (0, lib0_diff.simpleDiff)(str, content.map((c) => c.insert).join("")); ytext.delete(index, remove); ytext.insert(index, insert); ytext.applyDelta(content.map((c) => ({ retain: c.insert.length, attributes: c.attributes }))); }; //#endregion //#region src/lib/utils/reconciliation/updateMatchingYChild.ts const updateMatchingYChild = (y, yChild, pChild, meta, updateFragment) => { if (yChild instanceof yjs.XmlText && pChild instanceof Array) { if (equalYTextPText(yChild, pChild)) meta.mapping.set(yChild, pChild); else updateYText(yChild, pChild, meta); return true; } if (yChild instanceof yjs.XmlElement && !(pChild instanceof Array) && matchNodeName(yChild, pChild)) { if (equalYTypePNode(yChild, pChild)) meta.mapping.set(yChild, pChild); else updateFragment(y, yChild, pChild, meta); return true; } return false; }; //#endregion //#region src/lib/utils/binding/deleteMapping.ts const deleteMapping = (mapping, type) => { var _projectedMappings$ge; if (!(type instanceof yjs.XmlFragment || type instanceof yjs.Text || type instanceof yjs.XmlHook)) return; mapping.delete(type); (_projectedMappings$ge = projectedMappings.get(mapping)) === null || _projectedMappings$ge === void 0 || _projectedMappings$ge.delete(type); }; //#endregion //#region src/lib/utils/reconciliation/deleteYChildren.ts const deleteYChildren = (yDomFragment, index, length, meta) => { if (length > 0) { yDomFragment.slice(index, index + length).forEach((type) => deleteMapping(meta.mapping, type)); yDomFragment.delete(index, length); } }; //#endregion //#region src/lib/utils/reconciliation/insertPChildren.ts const insertPChildren = (yDomFragment, index, pChildren, start, end, meta) => { if (start < end) { const children = []; for (let childIndex = start; childIndex < end; childIndex++) children.push(createTypeFromTextOrElementNode(pChildren[childIndex], meta)); yDomFragment.insert(index, children); } }; //#endregion //#region src/lib/utils/reconciliation/reconcileUnanchoredChildren.ts const reconcileUnanchoredChildren = (y, yDomFragment, pChildren, yChildren, yStart, yEnd, pStart, pEnd, meta, updateFragment) => { const matchingChildren = lib0_math.min(yEnd - yStart, pEnd - pStart); for (let index = 0; index < matchingChildren; index++) { const yChild = yChildren[yStart + index]; const pChild = pChildren[pStart + index]; if (!updateMatchingYChild(y, yChild, pChild, meta, updateFragment)) { deleteYChildren(yDomFragment, pStart + index, 1, meta); insertPChildren(yDomFragment, pStart + index, pChildren, pStart + index, pStart + index + 1, meta); } } deleteYChildren(yDomFragment, pStart + matchingChildren, yEnd - yStart - matchingChildren, meta); insertPChildren(yDomFragment, pStart + matchingChildren, pChildren, pStart + matchingChildren, pEnd, meta); }; //#endregion //#region src/lib/utils/reconciliation/reconcileMappedChildren.ts const reconcileMappedChildren = (y, yDomFragment, pChildren, yChildren, meta, updateFragment) => { const anchors = findMappedChildAnchors(yChildren, pChildren, meta); if (anchors.length < 2 || !anchors.some((anchor) => anchor.yIndex !== anchor.pIndex) || anchors.some((anchor) => { const pChild = pChildren[anchor.pIndex]; return !(anchor.yChild instanceof yjs.XmlText && pChild instanceof Array || anchor.yChild instanceof yjs.XmlElement && !(pChild instanceof Array)); })) return false; let yIndex = 0; let pIndex = 0; for (const anchor of anchors) { reconcileUnanchoredChildren(y, yDomFragment, pChildren, yChildren, yIndex, anchor.yIndex, pIndex, anchor.pIndex, meta, updateFragment); const pChild = pChildren[anchor.pIndex]; updateMatchingYChild(y, anchor.yChild, pChild, meta, updateFragment); yIndex = anchor.yIndex + 1; pIndex = anchor.pIndex + 1; } reconcileUnanchoredChildren(y, yDomFragment, pChildren, yChildren, yIndex, yChildren.length, pIndex, pChildren.length, meta, updateFragment); return true; }; //#endregion //#region src/lib/utils/reconciliation/computeChildEqualityFactor.ts const computeChildEqualityFactor = (ytype, pnode, meta) => { const yChildren = ytype.toArray(); const pChildren = normalizePNodeContent(pnode); const pChildCnt = pChildren.length; const yChildCnt = yChildren.length; const minCnt = lib0_math.min(yChildCnt, pChildCnt); let left = 0; let right = 0; let foundMappedChild = false; for (; left < minCnt; left++) { const leftY = yChildren[left]; const leftP = pChildren[left]; if (mappedIdentity(meta.mapping.get(leftY), leftP)) foundMappedChild = true; else if (!equalYTypePNode(leftY, leftP)) break; } for (; left + right < minCnt; right++) { const rightY = yChildren[yChildCnt - right - 1]; const rightP = pChildren[pChildCnt - right - 1]; if (mappedIdentity(meta.mapping.get(rightY), rightP)) foundMappedChild = true; else if (!equalYTypePNode(rightY, rightP)) break; } return { equalityFactor: left + right, foundMappedChild }; }; //#endregion //#region src/lib/utils/reconciliation/updateYFragment.ts /** Updates a Yjs XML fragment to match a ProseMirror node while preserving mapped children. */ const updateYFragment = (y, yDomFragment, pNode, meta) => { if (yDomFragment instanceof yjs.XmlElement && yDomFragment.nodeName !== pNode.type.name) throw new Error("node name mismatch!"); meta.mapping.set(yDomFragment, pNode); if (yDomFragment instanceof yjs.XmlElement) { const yDomAttrs = yDomFragment.getAttributes(); const pAttrs = pNode.attrs; for (const key in pAttrs) { if (pAttrs[key] === null) { yDomFragment.removeAttribute(key); continue; } if (yDomAttrs[key] !== pAttrs[key] && key !== "ychange") yDomFragment.setAttribute(key, pAttrs[key]); } for (const key in yDomAttrs) if (pAttrs[key] === void 0) yDomFragment.removeAttribute(key); } const pChildren = normalizePNodeContent(pNode); const pChildCnt = pChildren.length; const yChildren = yDomFragment.toArray(); const yChildCnt = yChildren.length; const minCnt = lib0_math.min(pChildCnt, yChildCnt); let reconciled = false; y.transact(() => { reconciled = reconcileMappedChildren(y, yDomFragment, pChildren, yChildren, meta, updateYFragment); }, ySyncPluginKey); if (reconciled) return; let left = 0; let right = 0; for (; left < minCnt; left++) { const leftY = yChildren[left]; const leftP = pChildren[left]; if (mappedIdentity(meta.mapping.get(leftY), leftP)) continue; if (!equalYTypePNode(leftY, leftP)) break; meta.mapping.set(leftY, leftP); } for (; right + left + 1 < minCnt; right++) { const rightY = yChildren[yChildCnt - right - 1]; const rightP = pChildren[pChildCnt - right - 1]; if (mappedIdentity(meta.mapping.get(rightY), rightP)) continue; if (!equalYTypePNode(rightY, rightP)) break; meta.mapping.set(rightY, rightP); } y.transact(() => { while (yChildCnt - left - right > 0 && pChildCnt - left - right > 0) { const leftY = yChildren[left]; const leftP = pChildren[left]; const rightY = yChildren[yChildCnt - right - 1]; const rightP = pChildren[pChildCnt - right - 1]; if (leftY instanceof yjs.XmlText && leftP instanceof Array) { if (!equalYTextPText(leftY, leftP)) updateYText(leftY, leftP, meta); left += 1; continue; } let updateLeft = leftY instanceof yjs.XmlElement && matchNodeName(leftY, leftP); let updateRight = rightY instanceof yjs.XmlElement && matchNodeName(rightY, rightP); if (updateLeft && updateRight && leftY instanceof yjs.XmlElement && rightY instanceof yjs.XmlElement && !Array.isArray(leftP) && !Array.isArray(rightP)) { const equalityLeft = computeChildEqualityFactor(leftY, leftP, meta); const equalityRight = computeChildEqualityFactor(rightY, rightP, meta); if (equalityLeft.foundMappedChild && !equalityRight.foundMappedChild) updateRight = false; else if (!equalityLeft.foundMappedChild && equalityRight.foundMappedChild) updateLeft = false; else if (equalityLeft.equalityFactor < equalityRight.equalityFactor) updateLeft = false; else updateRight = false; } if (updateLeft && leftY instanceof yjs.XmlElement && !Array.isArray(leftP)) { updateYFragment(y, leftY, leftP, meta); left += 1; continue; } if (updateRight && rightY instanceof yjs.XmlElement && !Array.isArray(rightP)) { updateYFragment(y, rightY, rightP, meta); right += 1; continue; } deleteMapping(meta.mapping, yDomFragment.get(left)); yDomFragment.delete(left, 1); yDomFragment.insert(left, [createTypeFromTextOrElementNode(leftP, meta)]); left += 1; } const yDelLen = yChildCnt - left - right; if (yChildCnt === 1 && pChildCnt === 0 && yChildren[0] instanceof yjs.XmlText) { deleteMapping(meta.mapping, yChildren[0]); yChildren[0].delete(0, yChildren[0].length); } else if (yDelLen > 0) { yDomFragment.slice(left, left + yDelLen).forEach((type) => deleteMapping(meta.mapping, type)); yDomFragment.delete(left, yDelLen); } if (left + right >= pChildCnt) return; const childrenToInsert = []; for (let index = left; index < pChildCnt - right; index++) childrenToInsert.push(createTypeFromTextOrElementNode(pChildren[index], meta)); yDomFragment.insert(left, childrenToInsert); }, ySyncPluginKey); }; //#endregion //#region src/lib/utils/positions/migrations/getTextCharacters.ts const getTextCharacters = (type, position = 0) => { const characters = []; if (!type._item) throw new Error("Position tracking requires integrated text"); for (let item = type._start; item; item = item.right) { if (item.deleted || !item.countable) continue; if (!(item.content instanceof yjs.ContentString)) throw new Error("Position tracking requires string text content"); for (let index = 0; index < item.length; index += 1) characters.push({ client: item.id.client, clock: item.id.clock + index, text: item.content.str[index], type: type._item.id, position: position + characters.length }); } return characters; }; //#endregion //#region src/lib/utils/positions/migrations/syncPositionText.ts const retainedCharacters = (characters, desired, length) => { const retained = [{ current: -1, target: -1 }]; characters.forEach((character, index) => { const target = desired.get(`${character.client}:${character.clock}`); if (target !== void 0 && target > retained[retained.length - 1].target) retained.push({ current: index, target }); }); retained.push({ current: characters.length, target: length }); return retained; }; const syncPositionText = (type, text, position, desired, sources) => { const characters = getTextCharacters(type); const indexes = /* @__PURE__ */ new Map(); for (let index = 0; index < text.length; index += 1) { const character = desired.get(position + index); if (character) indexes.set(`${character.client}:${character.clock}`, index); } const retained = retainedCharacters(characters, indexes, text.length); for (let index = retained.length - 1; index > 0; index -= 1) { const left = retained[index - 1]; const right = retained[index]; const existing = characters.slice(left.current + 1, right.current); const replacement = text.slice(left.target + 1, right.target); if (!existing.some((character) => sources.has(`${character.client}:${character.clock}`)) && existing.map((character) => character.text).join("") === replacement) continue; type.delete(left.current + 1, existing.length); type.insert(left.current + 1, replacement); } return getTextCharacters(type, position); }; //#endregion //#region src/lib/utils/positions/migrations/mergePositionTransfers.ts const adjacentTargets = (left, right) => { if (!left.target || !right.target) return left.target === right.target; return left.target.client === right.target.client && left.target.assoc === right.target.assoc && left.target.boundary === right.target.boundary && left.target.tname === right.target.tname && left.target.clock + left.source.length === right.target.clock && yjs.compareIDs(left.target.type, right.target.type); }; const mergePositionTransfers = (transfers) => { const merged = []; transfers.forEach((transfer) => { const previous = merged[merged.length - 1]; if (previous && previous.source.client === transfer.source.client && previous.source.boundary === transfer.source.boundary && previous.source.clock + previous.source.length === transfer.source.clock && adjacentTargets(previous, transfer)) { previous.source.length += transfer.source.length; return; } merged.push({ ...transfer, source: { ...transfer.source } }); }); return merged; }; //#endregion //#region src/lib/utils/positions/migrations/writePositionMigration.ts const writePositionMigration = (store, transfers) => { const doc = store.doc; if (!doc || transfers.length === 0) return; const clock = yjs.getState(doc.store, doc.clientID); store.set(`${doc.clientID}:${clock}`, { version: 1, client: doc.clientID, clock, seen: [...yjs.decodeStateVector(yjs.encodeStateVector(doc))], transfers: mergePositionTransfers(transfers) }); }; //#endregion //#region src/lib/utils/positions/migrations/getPositionBoundaryTransfers.ts const getPositionBoundaryTransfers = (characters, destinations) => { const types = /* @__PURE__ */ new Map(); characters.forEach((character) => { var _types$get; const key = `${character.type.client}:${character.type.clock}`; const group = (_types$get = types.get(key)) !== null && _types$get !== void 0 ? _types$get : []; group.push(character); types.set(key, group); }); const transfers = []; types.forEach((group) => { const first = group[0]; const last = group[group.length - 1]; const start = first.position === null ? void 0 : destinations.get(first.position); const end = last.position === null ? void 0 : destinations.get(last.position); if (!start || !yjs.compareIDs(first.type, start.type)) transfers.push({ source: { ...first.type, length: 1, boundary: "start" }, target: start ? { ...start, assoc: 0 } : null }); if (!end || !yjs.compareIDs(last.type, end.type)) transfers.push({ source: { ...first.type, length: 1, boundary: "end" }, target: end ? { ...end, assoc: -1 } : null }); }); return transfers; }; //#endregion //#region src/lib/utils/positions/migrations/getPositionNodeTransfers.ts const getPositionNodeTransfers = (nodes, starts, ends, emptyTexts) => { const transfers = []; nodes.forEach((node) => { var _starts$get; const start = node.position === null ? null : (_starts$get = starts.get(node.position)) !== null && _starts$get !== void 0 ? _starts$get : null; if (start && yjs.compareIDs(node.id, start)) return; transfers.push({ source: { ...node.id, length: 1 }, target: start }); }); const types = [...nodes, ...emptyTexts]; for (const boundary of ["start", "end"]) { const destinations = boundary === "start" ? starts : ends; types.forEach((node) => { var _destinations$get; const position = boundary === "start" ? node.position : node.end; const target = position === null ? null : (_destinations$get = destinations.get(position)) !== null && _destinations$get !== void 0 ? _destinations$get : null; if (target && yjs.compareIDs(node.id, target)) return; transfers.push({ source: { ...node.id, length: 1, boundary }, target: target ? { client: target.client, clock: target.clock, type: null, boundary } : null }); }); } return transfers; }; //#endregion //#region src/lib/utils/positions/migrations/syncPositionElement.ts const syncPositionElement = (parent, index, node, position, sources, metadata) => { let element = parent.get(index); if (!(element instanceof yjs.XmlElement) || !element._item) throw new Error("Position migration requires synchronized elements"); const source = sources.get(`${element._item.id.client}:${element._item.id.clock}`); if (source && source.position !== position) { deleteMapping(metadata.mapping, element); parent.delete(index, 1); element = createTypeFromTextOrElementNode(node, metadata); parent.insert(index, [element]); } if (!(element instanceof yjs.XmlElement) || !element._item) throw new Error("Position migration requires integrated elements"); return element; }; //#endregion //#region src/lib/utils/positions/migrations/applyPositionMigration.ts const applyPositionMigration = (fragment, plan, metadata, store) => { const desired = new Map(plan.characters.map((character) => [character.position, character])); const sources = new Set(plan.characters.map((character) => `${character.client}:${character.clock}`)); const targets = /* @__PURE__ */ new Map(); const nodeSources = new Map(plan.nodes.map((node) => [`${node.id.client}:${node.id.clock}`, node])); const starts = /* @__PURE__ */ new Map(); const ends = /* @__PURE__ */ new Map(); const visit = (type, node, position) => { const children = type.toArray(); let offset = position; normalizePNodeContent(node).forEach((child, index) => { var _type$_item$id, _type$_item; const shared = children[index]; if (Array.isArray(child)) { if (!(shared instanceof yjs.XmlText) || !shared._item) throw new Error("Position migration requires integrated text"); const text = child.map((node) => { var _node$text; return (_node$text = node.text) !== null && _node$text !== void 0 ? _node$text : ""; }).join(""); const characters = syncPositionText(shared, text, offset, desired, sources); const typeId = shared._item.id; characters.forEach((character) => targets.set(character.position, { client: character.client, clock: character.clock, type: typeId })); updateYText(shared, child, metadata); offset += text.length; return; } const element = syncPositionElement(type, index, child, offset, nodeSources, metadata); const identity = element._item.id; const target = { client: identity.client, clock: identity.clock, type: (_type$_item$id = (_type$_item = type._item) === null || _type$_item === void 0 ? void 0 : _type$_item.id) !== null && _type$_item$id !== void 0 ? _type$_item$id : null, ...type._item ? {} : { tname: yjs.findRootTypeKey(type) } }; starts.set(offset, target); ends.set(offset + child.nodeSize - 1, target); if (!child.isLeaf) visit(element, child, offset + 1); offset += child.nodeSize; }); }; visit(fragment, plan.doc, 0); const transfers = []; plan.characters.forEach((character) => { var _targets$get; const target = character.position === null ? null : (_targets$get = targets.get(character.position)) !== null && _targets$get !== void 0 ? _targets$get : null; if (target && yjs.compareIDs(character, target) && yjs.compareIDs(character.type, target.type)) return; transfers.push({ source: { client: character.client, clock: character.clock, length: 1 }, target }); }); transfers.push(...getPositionBoundaryTransfers(plan.characters, targets)); transfers.push(...getPositionNodeTransfers(plan.nodes, starts, ends, plan.emptyTexts)); writePositionMigration(store, transfers); }; //#endregion //#region src/lib/utils/binding/replaceChangedChildren.ts const replaceChangedChildren = (tr, content) => { var _step$apply$doc; const oldContent = tr.doc.content; let start = 0; let from = 0; while (start < oldContent.childCount && start < content.childCount && oldContent.child(start) === content.child(start)) { from += oldContent.child(start).nodeSize; start++; } let oldEnd = oldContent.childCount; let newEnd = content.childCount; let suffixSize = 0; while (oldEnd > start && newEnd > start && oldContent.child(oldEnd - 1) === content.child(newEnd - 1)) { suffixSize += oldContent.child(oldEnd - 1).nodeSize; oldEnd--; newEnd--; } if (start === oldEnd && start === newEnd) return tr; const slice = new prosemirror_model.Slice(content.cut(from, content.size - suffixSize), 0, 0); const step = (0, prosemirror_transform.replaceStep)(tr.doc, from, oldContent.size - suffixSize, slice); if (step !== null && ((_step$apply$doc = step.apply(tr.doc).doc) === null || _step$apply$doc === void 0 ? void 0 : _step$apply$doc.content.eq(content))) return tr.step(step); return tr.replace(0, oldContent.size, new prosemirror_model.Slice(content, 0, 0)); }; //#endregion //#region src/lib/utils/binding/ProsemirrorBinding.ts /** Synchronizes one integrated Yjs XML fragment with a ProseMirror editor view. */ var ProsemirrorBinding = class { /** * Returns the last projection error. Shared writes stop until a compatible projection succeeds. * @returns The projection error, or null when shared content can be rendered. */ get projectionError() { return this._projectionError; } constructor(yXmlFragment, mapping = /* @__PURE__ */ new Map()) { this.prosemirrorView = null; this.mux = (0, lib0_mutex.createMutex)(); this.isOMark = /* @__PURE__ */ new Map(); this.beforeTransactionSelection = null; this._domSelectionInView = null; this.syncedDoc = null; this._projectionError = null; this.type = yXmlFragment; this.mapping = mapping; this._observeFunction = this._typeChanged.bind(this); if (yXmlFragment.doc === null) throw new Error("The Yjs fragment must belong to a document"); this.doc = yXmlFragment.doc; this.beforeAllTransactions = () => { if (this.projectionError === null && this.beforeTransactionSelection === null && this.prosemirrorView != null) this.beforeTransactionSelection = getRelativeSelection(this, this.view.state); }; this.afterAllTransactions = () => { this.beforeTransactionSelection = null; }; } get view() { if (!this.prosemirrorView) throw new Error("The binding has no editor view"); return this.prosemirrorView; } get _tr() { return this.view.state.tr.setMeta("addToHistory", false); } _isLocalCursorInView() { if (!this.view.hasFocus()) return false; if (lib0_environment.isBrowser && this._domSelectionInView === null) { lib0_eventloop.timeout(0, () => { this._domSelectionInView = null; }); this._domSelectionInView = this._isDomSelectionInView(); } return this._domSelectionInView; } _isDomSelectionInView() { const root = this.view.root; const selection = "getSelection" in root && typeof root.getSelection === "function" ? root.getSelection() : this.view.dom.ownerDocument.getSelection(); if (selection == null || selection.anchorNode == null || selection.focusNode == null) return false; const range = this.view.dom.ownerDocument.createRange(); range.setStart(selection.anchorNode, selection.anchorOffset); range.setEnd(selection.focusNode, selection.focusOffset); if (range.getClientRects().length === 0) { if (range.startContainer && range.collapsed) range.selectNodeContents(range.startContainer); } const bounding = range.getBoundingClientRect(); const documentElement = lib0_dom.doc.documentElement; return bounding.bottom >= 0 && bounding.right >= 0 && bounding.left <= (window.innerWidth || documentElement.clientWidth || 0) && bounding.top <= (window.innerHeight || documentElement.clientHeight || 0); } renderSnapshot(snapshot, prevSnapshot) { if (!prevSnapshot) prevSnapshot = yjs.createSnapshot(yjs.createDeleteSet(), /* @__PURE__ */ new Map()); this.view.dispatch(this._tr.setMeta(ySyncPluginKey, { snapshot, prevSnapshot })); } createDocumentFragment() { try { const fragmentContent = this.type.toArray().map((type) => { if (!(type instanceof yjs.XmlElement)) throw lib0_error.methodUnimplemented(); return createNodeFromYElement(type, this.view.state.schema, this); }); const content = prosemirror_model.Fragment.from(fragmentContent); validateDocumentContent(content, this.view.state.schema); this._projectionError = null; return content; } catch (error) { this.handleProjectionError(error); return null; } } handleProjectionError(error) { this._projectionError = error instanceof Error ? error : /* @__PURE__ */ new Error("Cannot project shared content"); this.mapping.clear(); projectedMappings.delete(this.mapping); this.beforeTransactionSelection = null; this.view.dispatch(this._tr.setMeta(ySyncPluginKey, { isChangeOrigin: true })); } createDocumentTransaction() { const content = this.createDocumentFragment(); if (content === null) return null; return this._tr.replace(0, this.view.state.doc.content.size, new prosemirror_model.Slice(content, 0, 0)); } unrenderSnapshot() { this.mapping.clear(); projectedMappings.delete(this.mapping); this.mux(() => { const tr = this.createDocumentTransaction(); if (tr === null) return; tr.setMeta(ySyncPluginKey, { snapshot: null, prevSnapshot: null }); this.view.dispatch(tr); }); } _forceRerender() { this.mapping.clear(); projectedMappings.delete(this.mapping); this.mux(() => { const sel = this.beforeTransactionSelection !== null ? null : this.view.state.selection; const tr = this.createDocumentTransaction(); if (tr === null) return; if (sel) { const clampedAnchor = lib0_math.min(lib0_math.max(sel.anchor, 0), tr.doc.content.size); const clampedHead = lib0_math.min(lib0_math.max(sel.head, 0), tr.doc.content.size); tr.setSelection(prosemirror_state.TextSelection.create(tr.doc, clampedAnchor, clampedHead)); } this.view.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, binding: this })); }); } _renderSnapshot(snapshot, prevSnapshot, pluginState) { var _prevSnapshot; let historyDoc = this.doc; let historyType = this.type; if (!snapshot) snapshot = yjs.snapshot(this.doc); if (snapshot instanceof Uint8Array || prevSnapshot instanceof Uint8Array) { if (!(snapshot instanceof Uint8Array) || !(prevSnapshot instanceof Uint8Array)) throw lib0_error.unexpectedCase(); historyDoc = new yjs.Doc({ gc: false }); yjs.applyUpdateV2(historyDoc, prevSnapshot); prevSnapshot = yjs.snapshot(historyDoc); yjs.applyUpdateV2(historyDoc, snapshot); snapshot = yjs.snapshot(historyDoc); if (historyType._item === null) { const rootKey = Array.from(this.doc.share.keys()).find((key) => Object.is(this.doc.share.get(key), historyType)); historyType = historyDoc.getXmlFragment(rootKey); } else { var _historyDoc$store$cli; const historyStructs = (_historyDoc$store$cli = historyDoc.store.clients.get(historyType._item.id.client)) !== null && _historyDoc$store$cli !== void 0 ? _historyDoc$store$cli : []; const item = historyStructs[yjs.findIndexSS(historyStructs, historyType._item.id.clock)]; if (!(item instanceof yjs.Item && item.content instanceof yjs.ContentType && item.content.type instanceof yjs.XmlFragment)) throw lib0_error.unexpectedCase(); historyType = item.content.type; } } const currentSnapshot = snapshot; const previousSnapshot = (_prevSnapshot = prevSnapshot) !== null && _prevSnapshot !== void 0 ? _prevSnapshot : yjs.emptySnapshot; this.mapping.clear(); projectedMappings.delete(this.mapping); this.mux(() => { try { historyDoc.transact((transaction) => { const pud = pluginState.permanentUserData; if (pud) pud.dss.forEach((ds) => { yjs.iterateDeletedStructs(transaction, ds, (_item) => {}); }); const computeYChange = (type, id) => { const user = pud ? type === "added" ? pud.getUserByClientId(id.client) : pud.getUserByDeletedId(id) : null; return { user, type, color: getUserColor(pluginState.colorMapping, pluginState.colors, user) }; }; const fragmentContent = yjs.typeListToArraySnapshot(historyType, new yjs.Snapshot(previousSnapshot.ds, currentSnapshot.sv)).map((t) => { if (!(t instanceof yjs.XmlElement) || !t._item) return null; if (!t._item.deleted || isVisible(t._item, currentSnapshot) || isVisible(t._item, previousSnapshot)) return createNodeFromYElement(t, this.view.state.schema, { mapping: /* @__PURE__ */ new Map(), isOMark: /* @__PURE__ */ new Map() }, currentSnapshot, previousSnapshot, computeYChange); else return null; }).filter((n) => n !== null); const content = prosemirror_model.Fragment.from(fragmentContent); validateDocumentContent(content, this.view.state.schema); const tr = this._tr.replace(0, this.view.state.doc.content.size, new prosemirror_model.Slice(content, 0, 0)); this._projectionError = null; this.view.dispatch(tr.setMeta(ySyncPluginKey, { isChangeOrigin: true })); }, ySyncPluginKey); } catch (error) { this.handleProjectionError(error); } }); } _typeChanged(events, transaction) { if (this.prosemirrorView == null) return; const syncState = getSyncPluginState(this.view.state); if (events.length === 0 || syncState.snapshot != null || syncState.prevSnapshot != null) { this.renderSnapshot(syncState.snapshot, syncState.prevSnapshot); return; } this.mux(() => { const delType = (_, type) => deleteMapping(this.mapping, type); yjs.iterateDeletedStructs(transaction, transaction.deleteSet, (struct) => { if (struct instanceof yjs.Item && struct.content instanceof yjs.ContentType) deleteMapping(this.mapping, struct.content.type); }); transaction.changed.forEach(delType); transaction.changedParentTypes.forEach(delType); const content = this.createDocumentFragment(); if (content === null) return; const oldDoc = this.view.state.doc; let tr = replaceChangedChildren(this._tr, content); restoreRelativeSelection(tr, this.beforeTransactionSelection, this, oldDoc); tr = tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, isUndoRedoOperation: transaction.origin instanceof yjs.UndoManager }); if (this.beforeTransactionSelection !== null && this._isLocalCursorInView()) tr.scrollIntoView(); this.view.dispatch(tr); }); } _prosemirrorChanged(doc, plan = null) { const syncState = getSyncPluginState(this.view.state); if (this.projectionError !== null || syncState.snapshot != null || syncState.prevSnapshot != null) return; this.doc.transact(() => { updateYFragment(this.doc, this.type, doc, this); if ((plan === null || plan === void 0 ? void 0 : plan.doc) === doc) applyPositionMigration(this.type, plan, this, getPositionMigrationStore(this.type)); this.syncedDoc = doc; this.beforeTransactionSelection = getRelativeSelection(this, this.view.state); }, ySyncPluginKey); } initView(prosemirrorView) { if (this.prosemirrorView != null) this.destroy(); this.prosemirrorView = prosemirrorView; this.doc.on("beforeAllTransactions", this.beforeAllTransactions); this.doc.on("afterAllTransactions", this.afterAllTransactions); this.type.observeDeep(this._observeFunction); } destroy() { if (this.prosemirrorView == null) return; this.prosemirrorView = null; this.type.unobserveDeep(this._observeFunction); this.doc.off("beforeAllTransactions", this.beforeAllTransactions); this.doc.off("afterAllTransactions", this.afterAllTransactions); } }; //#endregion //#region src/lib/utils/positions/migrations/getPositionContent.ts const getPositionContent = (fragment, doc) => { const characters = []; const nodes = []; const emptyTexts = []; if (fragment.length === 0) return { characters, nodes, emptyTexts }; const visit = (type, node, position) => { const children = type.toArray(); const content = normalizePNodeContent(node); let offset = position; if (node.content.size === 0) children.forEach((child) => { if (child instanceof yjs.XmlText && child._item) emptyTexts.push({ id: child._item.id, position: position - 1, end: position }); }); content.forEach((child, index) => { const shared = children[index]; if (!Array.isArray(child)) { if (!(shared instanceof yjs.XmlElement) || !shared._item) throw new Error("Position tracking requires synchronized editor content"); nodes.push({ id: shared._item.id, position: offset, end: offset + child.nodeSize - 1 }); if (!child.isLeaf) visit(shared, child, offset + 1); offset += child.nodeSize; return; } if (!(shared instanceof yjs.XmlText)) throw new Error("Position tracking requires synchronized editor text"); const text = getTextCharacters(shared, offset); characters.push(...text); offset += text.length; }); }; visit(fragment, doc, 0); return { characters, nodes, emptyTexts }; }; //#endregion //#region src/lib/utils/positions/migrations/isNodeMarkupChange.ts const isNodeMarkupChange = (step, before, after, position) => { const json = step.toJSON(); if (json.stepType !== "replace" && json.stepType !== "replaceAround") return false; if (position !== json.from && position !== json.to - 1) return false; const source = before.nodeAt(json.from); const target = after.nodeAt(json.from); if (!source || !target) return false; if (source.nodeSize !== json.to - json.from) return false; return changedMarkup(source, target); }; const changedMarkup = (source, target) => source.type === target.type && source.content.eq(target.content) && !source.sameMarkup(target); //#endregion //#region src/lib/utils/positions/migrations/mapPositionMigration.ts const isTextReplacement = (step, doc) => { var _json$slice; const json = step.toJSON(); const slice = (_json$slice = json.slice) !== null && _json$slice !== void 0 ? _json$slice : { content: [] }; if (json.stepType !== "replace" || slice.openStart > 0 || slice.openEnd > 0) return false; const from = doc.resolve(json.from); const to = doc.resolve(json.to); return slice.content.every((node) => node.type === "text") && from.parent.isTextblock && from.sameParent(to); }; const indexNodes = (doc) => { const nodes = /* @__PURE__ */ new Map(); doc.descendants((node, position) => { var _nodes$get; const positions = (_nodes$get = nodes.get(node)) !== null && _nodes$get !== void 0 ? _nodes$get : []; positions.push(position); nodes.set(node, positions); }); return nodes; }; const findMovedNode = (node, offset, step, indexes) => { var _indexes$step$get; if (((_indexes$step$get = indexes[step].get(node)) === null || _indexes$step$get === void 0 ? void 0 : _indexes$step$get.length) !== 1) return null; for (let targetStep = step + 1; targetStep < indexes.length; targetStep += 1) { const target = indexes[targetStep].get(node); if ((target === null || target === void 0 ? void 0 : target.length) === 1) return { position: target[0] + offset, step: targetStep }; } return null; }; const findMovedPosition = (position, step, docs, indexes) => { const original = docs[step].resolve(position); const candidates = []; const child = docs[step].nodeAt(position); if (child && !child.isText) candidates.push({ node: child, offset: 0 }); for (let depth = original.depth; depth > 0; depth -= 1) candidates.push({ node: original.node(depth), offset: position - original.before(depth) }); for (const candidate of candidates) { const moved = findMovedNode(candidate.node, candidate.offset, step, indexes); if (moved) return moved; } return null; }; const mapCharacterPosition = (position, transaction, docs, indexes) => { for (let step = 0; step < transaction.mapping.maps.length; step += 1) { const mapping = transaction.mapping.maps[step]; const start = mapping.mapResult(position, 1); const end = mapping.mapResult(position + 1, -1); if (!start.deleted && !end.deleted && end.pos === start.pos + 1) { position = start.pos; continue; } if (isNodeMarkupChange(transaction.steps[step], docs[step], docs[step + 1], position)) continue; const moved = findMovedPosition(position, step, docs, indexes); if (!moved) return null; position = moved.position; step = moved.step - 1; } return position; }; const mapPositionMigration = (transaction, binding, previous) => { const pending = (previous === null || previous === void 0 ? void 0 : previous.doc) === transaction.before && previous.doc !== binding.syncedDoc ? previous : null; if (!transaction.docChanged || !binding.prosemirrorView || !pending && transaction.steps.every((step, index) => isTextReplacement(step, transaction.docs[index]))) return pending; const source = pending !== null && pending !== void 0 ? pending : getPositionContent(binding.type, transaction.before); const docs = [...transaction.docs, transaction.doc]; const indexes = docs.map(indexNodes); const characters = source.characters.map((character) => ({ ...character, position: character.position === null ? null : mapCharacterPosition(character.position, transaction, docs, indexes) })); const mapNode = (node) => ({ ...node, position: node.position === null ? null : mapCharacterPosition(node.position, transaction, docs, indexes), end: node.end === null ? null : mapCharacterPosition(node.end, transaction, docs, indexes) }); return { doc: transaction.doc, characters, nodes: source.nodes.map(mapNode), emptyTexts: source.emptyTexts.map(mapNode) }; }; //#endregion //#region src/lib/plugins/ySyncPlugin.ts const defaultColors = [{ light: "#ecd44433", dark: "#ecd444" }]; /** Binds a Yjs XML fragment to the editor document. The fragment must belong to a Yjs document. */ const ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping = /* @__PURE__ */ new Map(), permanentUserData = null, onFirstRender = () => {}, mapping } = {}) => { let initialContentChanged = false; const binding = new ProsemirrorBinding(yXmlFragment, mapping); return new prosemirror_state.Plugin({ filterTransaction: (transaction) => binding.projectionError === null || !transaction.docChanged, props: { editable: (state) => { const syncState = getSyncPluginState(state); return binding.projectionError === null && syncState.snapshot == null && syncState.prevSnapshot == null; } }, key: ySyncPluginKey, state: { init: (_initargs, _state) => { return { type: yXmlFragment, doc: binding.doc, binding, snapshot: null, prevSnapshot: null, isChangeOrigin: false, isUndoRedoOperation: false, addToHistory: true, colors, colorMapping, permanentUserData, positionMigration: null }; }, apply: (tr, pluginState) => { var _pluginState$position; const change = tr.getMeta(ySyncPluginKey); if (change !== void 0) pluginState = Object.assign({}, pluginState, change); if (tr.docChanged) pluginState.addToHistory = tr.getMeta("addToHistory") !== false; pluginState.isChangeOrigin = change !== void 0 && !!change.isChangeOrigin; pluginState.isUndoRedoOperation = change !== void 0 && !!change.isChangeOrigin && !!change.isUndoRedoOperation; pluginState = { ...pluginState, positionMigration: binding.projectionError || pluginState.isChangeOrigin || pluginState.snapshot || pluginState.prevSnapshot || (change === null || change === void 0 ? void 0 : change.snapshot) !== void 0 || (change === null || change === void 0 ? void 0 : change.prevSnapshot) !== void 0 ? null : mapPositionMigration(tr, binding, (_pluginState$position = pluginState.positionMigration) !== null && _pluginState$position !== void 0 ? _pluginState$position : null) }; if (binding.prosemirrorView === null || change === void 0 || change.snapshot == null && change.prevSnapshot == null) return pluginState; lib0_eventloop.timeout(0, () => { if (binding.prosemirrorView == null) return; if (change.restore == null) { binding._renderSnapshot(change.snapshot, change.prevSnapshot, pluginState); return; } binding._renderSnapshot(change.snapshot, change.snapshot, pluginState); if (binding.projectionError !== null) return; binding.mux(() => { const activeView = binding.prosemirrorView; if (!activeView) return; activeView.dispatch(binding._tr.setMeta(ySyncPluginKey, { restore: false, snapshot: null, prevSnapshot: null, isChangeOrigin: true })); binding._prosemirrorChanged(activeView.state.doc); }); }); return pluginState; } }, view: (view) => { binding.initView(view); if (mapping == null) binding._forceRerender(); onFirstRender(); return { update: () => { const pluginState = getSyncPluginState(view.state); if (binding.projectionError !== null) return; if (pluginState.snapshot == null && pluginState.prevSnapshot == null) { const emptyDocument = view.state.doc.type.createAndFill(); if (!emptyDocument) throw new Error("The schema cannot create an empty document"); if (initialContentChanged || view.state.doc.content.findDiffStart(emptyDocument.content) !== null) { initialContentChanged = true; if (pluginState.addToHistory === false && !pluginState.isChangeOrigin) { const yUndoPluginState = yUndoPluginKey.getState(view.state); const um = yUndoPluginState && yUndoPluginState.undoManager; if (um) um.stopCapturing(); } binding.mux(() => { pluginState.doc.transact((tr) => { var _pluginState$position2; tr.meta.set("addToHistory", pluginState.addToHistory); binding._prosemirrorChanged(view.state.doc, (_pluginState$position2 = pluginState.positionMigration) !== null && _pluginState$position2 !== void 0 ? _pluginState$position2 : null); }, ySyncPluginKey); }); } } }, destroy: () => { binding.destroy(); } }; } }); }; //#endregion //#region src/lib/plugins/getUndoPluginState.ts function getUndoPluginState(state) { const pluginState = yUndoPluginKey.getState(state); if (!pluginState) throw new Error("The editor requires yUndoPlugin"); return pluginState; } //#endregion //#region src/lib/utils/positions/migrations/getLivePositionTargets.ts const getLivePositionTargets = (fragment) => { const targets = /* @__PURE__ */ new Map(); const visit = (type) => { type.toArray().forEach((child) => { if (child instanceof yjs.XmlElement) { var _type$_item$id, _type$_item; const identity = child._item.id; targets.set(`${identity.client}:${identity.clock}`, { client: identity.client, clock: identity.clock, type: (_type$_item$id = (_type$_item = type._item) === null || _type$_item === void 0 ? void 0 : _type$_item.id) !== null && _type$_item$id !== void 0 ? _type$_item$id : null, ...type._item ? {} : { tname: yjs.findRootTypeKey(type) } }); visit(child); return; } if (!(child instanceof yjs.XmlText) || !child._item) return; const typeId = child._item.id; getTextCharacters(child).forEach((character) => { targets.set(`${character.client}:${character.clock}`, { client: character.client, clock: character.clock, type: typeId }); }); }); }; visit(fragment); return targets; }; //#endregion //#region src/lib/utils/positions/migrations/restorePositionHistory.ts const restoreTransfers = (fragment, store) => { const doc = fragment.doc; if (!doc) return; const live = getLivePositionTargets(fragment); const identities = /* @__PURE__ */ new Map(); const connections = /* @__PURE__ */ new Map(); const connect = (source, target) => { var _connections$get, _connections$get2; const from = `${source.client}:${source.clock}`; const to = `${target.client}:${target.clock}`; identities.set(from, source); identities.set(to, target); const outgoing = (_connections$get = connections.get(from)) !== null && _connections$get !== void 0 ? _connections$get : /* @__PURE__ */ new Set(); const incoming = (_connections$get2 = connections.get(to)) !== null && _connections$get2 !== void 0 ? _connections$get2 : /* @__PURE__ */ new Set(); outgoing.add(to); incoming.add(from); connections.set(from, outgoing); connections.set(to, incoming); }; store.forEach((migration) => migration.transfers.forEach(({ source, target }) => { if (!target || source.boundary) return; for (let offset = 0; offset < source.length; offset += 1) connect(yjs.createID(source.client, source.clock + offset), yjs.createID(target.client, target.clock + offset)); })); doc.store.clients.forEach((structs) => structs.forEach((item) => { if (!(item instanceof yjs.Item) || !item.redone) return; if (!(item.content instanceof yjs.ContentString) && !(item.content instanceof yjs.ContentType)) return; for (let offset = 0; offset < item.length; offset += 1) connect(yjs.createID(item.id.client, item.id.clock + offset), yjs.createID(item.redone.client, item.redone.clock + offset)); })); const transfers = []; const visited = /* @__PURE__ */ new Set(); connections.forEach((_, start) => { if (visited.has(start)) return; const pending = [start]; const component = []; const targets = []; while (pending.length > 0) { var _connections$get3; const key = pending.pop(); if (visited.has(key)) continue; visited.add(key); component.push(key); const target = live.get(key); if (target) targets.push(target); pending.push(...(_connections$get3 = connections.get(key)) !== null && _connections$get3 !== void 0 ? _connections$get3 : []); } if (targets.length !== 1) return; component.forEach((key) => { if (live.has(key)) return; const source = identities.get(key); transfers.push({ source: { client: source.client, clock: source.clock, length: 1 }, target: targets[0] }); const item = yjs.getItem(doc.store, source); if (item.content instanceof yjs.ContentType && item.content.type instanceof yjs.XmlElement) for (const boundary of ["start", "end"]) transfers.push({ source: { client: source.client, clock: source.clock, length: 1, boundary }, target: { client: targets[0].client, clock: targets[0].clock, type: null, boundary } }); }); }); writePositionMigration(store, transfers); }; const restorePositionHistory = (fragment, manager, action) => { const store = getPositionMigrationStore(fragment); if (store.size === 0) return manager[action](); const flag = action === "undo" ? "undoing" : "redoing"; let result = null; try { manager.doc.transact(() => { result = manager[action](); manager[flag] = true; if (result) restoreTransfers(fragment, store); }, manager); } finally { manager[flag] = false; } return result; }; //#endregion //#region src/lib/plugins/yUndoPlugin.ts const restoreHistory = (state, direction) => { const syncState = getSyncPluginState(state); if (syncState.binding.projectionError !== null || syncState.snapshot != null || syncState.prevSnapshot != null) return false; const undoManager = getUndoPluginState(state).undoManager; if (undoManager != null) { restorePositionHistory(syncState.type, undoManager, direction); return true; } }; /** Undoes a local change through the editor's Yjs undo manager. */ const undo = (state) => restoreHistory(state, "undo"); /** Redoes a local change through the editor's Yjs undo manager. */ const redo = (state) => restoreHistory(state, "redo"); const defaultProtectedNodes = /* @__PURE__ */ new Set(["paragraph"]); /** Keeps protected structural nodes when an undo operation would delete them. */ const defaultDeleteFilter = (item, protectedNodes) => !(item instanceof yjs.Item) || !(item.content instanceof yjs.ContentType) || !(item.content.type instanceof yjs.Text || item.content.type instanceof yjs.XmlElement && protectedNodes.has(item.content.type.nodeName)) || item.content.type._length === 0; /** Tracks local Yjs changes for undo and redo while preserving remote changes. */ const yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) => new prosemirror_state.Plugin({ key: yUndoPluginKey, state: { init: (_, state) => { const syncState = getSyncPluginState(state); const destroyListeners = /* @__PURE__ */ new Set(); let manager = undoManager; if (!manager) { var _syncState$doc$_obser; const before = new Set(syncState.doc._observers.get("destroy")); manager = new yjs.UndoManager(syncState.type, { trackedOrigins: /* @__PURE__ */ new Set([ySyncPluginKey, ...trackedOrigins]), deleteFilter: (item) => defaultDeleteFilter(item, protectedNodes), captureTransaction: (transaction) => transaction.meta.get("addToHistory") !== false }); ((_syncState$doc$_obser = syncState.doc._observers.get("destroy")) !== null && _syncState$doc$_obser !== void 0 ? _syncState$doc$_obser : /* @__PURE__ */ new Set()).forEach((listener) => { if (!before.has(listener)) destroyListeners.add(listener); }); } return { undoManager: manager, prevSel: null, hasUndoOps: manager.undoStack.length > 0, hasRedoOps: manager.redoStack.length > 0, destroyListeners }; }, apply: (_, value, oldState, state) => { const binding = getSyncPluginState(state).binding; return { ...value, prevSel: binding ? getRelativeSelection(binding, oldState) : value.prevSel, hasUndoOps: value.undoManager.undoStack.length > 0, hasRedoOps: value.undoManager.redoStack.length > 0 }; } }, view: (view) => { const syncState = getSyncPluginState(view.state); const { undoManager: manager, destroyListeners } = getUndoPluginState(view.state); manager.on("stack-item-added", ({ stackItem }) => { const binding = syncState.binding; if (binding) stackItem.meta.set(binding, getUndoPluginState(view.state).prevSel); }); manager.on("stack-item-popped", ({ stackItem }) => { const binding = syncState.binding; if (binding) binding.beforeTransactionSelection = stackItem.meta.get(binding) || binding.beforeTransactionSelection; }); return { destroy: () => { manager.destroy(); destroyListeners.forEach((listener) => manager.doc.off("destroy", listener)); destroyListeners.clear(); } }; } }); //#endregion //#region src/lib/utils/conversion/prosemirrorToYXmlFragment.ts /** Converts a ProseMirror document into a new or existing Yjs XML fragment. */ function prosemirrorToYXmlFragment(doc, xmlFragment) { var _type$doc; const type = xmlFragment || new yjs.XmlFragment(); const ydoc = (_type$doc = type.doc) !== null && _type$doc !== void 0 ? _type$doc : { transact: (callback) => callback() }; updateYFragment(ydoc, type, doc, { mapping: /* @__PURE__ */ new Map(), isOMark: /* @__PURE__ */ new Map() }); return type; } //#endregion //#region src/lib/utils/conversion/prosemirrorToYDoc.ts /** Creates a Yjs document from a ProseMirror document. */ function prosemirrorToYDoc(doc, xmlFragment = "prosemirror") { const ydoc = new yjs.Doc(); const type = ydoc.getXmlFragment(xmlFragment); if (!type.doc) return ydoc; prosemirrorToYXmlFragment(doc, type); return type.doc; } //#endregion //#region src/lib/utils/conversion/prosemirrorJSONToYDoc.ts /** Creates a Yjs document from ProseMirror JSON using the supplied schema. */ function prosemirrorJSONToYDoc(schema, state, xmlFragment = "prosemirror") { return prosemirrorToYDoc(prosemirror_model.Node.fromJSON(schema, state), xmlFragment); } //#endregion //#region src/lib/utils/conversion/yXmlFragmentToProsemirrorJSON.ts /** Reads ProseMirror JSON from a Yjs XML fragment. */ function yXmlFragmentToProsemirrorJSON(xmlFragment) { const items = xmlFragment.toArray(); const serialize = (item) => { let response; if (item instanceof yjs.XmlText) response = item.toDelta().map((d) => { const text = { type: "text", text: d.insert }; const attributes = d.attributes; if (attributes) text.marks = Object.keys(attributes).map((type_) => { const attrs = attributes[type_]; const mark = { type: yattr2markname(type_) }; if (Object.keys(attrs)) mark.attrs = attrs; return mark; }); return text; }); else if (item instanceof yjs.XmlElement) { response = { type: item.nodeName }; const attrs = item.getAttributes(); if (Object.keys(attrs).length) response.attrs = attrs; const children = item.toArray(); if (children.length) response.content = children.map(serialize).flat(); } else lib0_error.unexpectedCase(); return response; }; return { type: "doc", content: items.map(serialize) }; } //#endregion //#region src/lib/utils/conversion/yDocToProsemirrorJSON.ts /** Reads ProseMirror JSON from the named Yjs XML fragment. */ function yDocToProsemirrorJSON(ydoc, xmlFragment = "prosemirror") { return yXmlFragmentToProsemirrorJSON(ydoc.getXmlFragment(xmlFragment)); } //#endregion //#region src/lib/utils/conversion/yDocToProsemirror.ts /** Creates a ProseMirror document from the named Yjs XML fragment. */ function yDocToProsemirror(schema, ydoc) { const state = yDocToProsemirrorJSON(ydoc); return prosemirror_model.Node.fromJSON(schema, state); } //#endregion //#region src/lib/utils/conversion/prosemirrorJSONToYXmlFragment.ts /** Converts ProseMirror JSON into a new or existing Yjs XML fragment. */ function prosemirrorJSONToYXmlFragment(schema, state, xmlFragment) { return prosemirrorToYXmlFragment(prosemirror_model.Node.fromJSON(schema, state), xmlFragment); } //#endregion //#region src/lib/utils/conversion/yXmlFragmentToProsemirror.ts /** Creates a ProseMirror document from a Yjs XML fragment. */ function yXmlFragmentToProsemirror(schema, xmlFragment) { const state = yXmlFragmentToProsemirrorJSON(xmlFragment); return prosemirror_model.Node.fromJSON(schema, state); } //#endregion //#region src/lib/utils/conversion/createEmptyMeta.ts const createEmptyMeta = () => ({ mapping: /* @__PURE__ */ new Map(), isOMark: /* @__PURE__ */ new Map() }); //#endregion //#region src/lib/utils/conversion/yXmlFragmentToProseMirrorFragment.ts /** Converts Yjs XML content to a ProseMirror fragment using the supplied schema. */ const yXmlFragmentToProseMirrorFragment = (yXmlFragment, schema) => { const fragmentContent = yXmlFragment.toArray().map((t) => { if (!(t instanceof yjs.XmlElement)) throw lib0_error.methodUnimplemented(); return createNodeFromYElement(t, schema, createEmptyMeta()); }); return prosemirror_model.Fragment.fromArray(fragmentContent); }; //#endregion //#region src/lib/utils/conversion/yXmlFragmentToProseMirrorRootNode.ts /** Converts Yjs XML content to the schema's top-level document node. */ const yXmlFragmentToProseMirrorRootNode = (yXmlFragment, schema) => { const content = yXmlFragmentToProseMirrorFragment(yXmlFragment, schema); validateDocumentContent(content, schema); return schema.topNodeType.createChecked(null, content); }; //#endregion //#region src/lib/utils/conversion/initProseMirrorDoc.ts /** Creates the initial ProseMirror document and its Yjs node mapping. */ const initProseMirrorDoc = (yXmlFragment, schema) => { const meta = createEmptyMeta(); const fragmentContent = yXmlFragment.toArray().map((t) => { if (!(t instanceof yjs.XmlElement)) throw lib0_error.methodUnimplemented(); return createNodeFromYElement(t, schema, meta); }); const content = prosemirror_model.Fragment.fromArray(fragmentContent); validateDocumentContent(content, schema); return { doc: schema.topNodeType.createChecked(null, content), meta, mapping: meta.mapping }; }; //#endregion exports.ProsemirrorBinding = ProsemirrorBinding; exports.absolutePositionToRelativePosition = absolutePositionToRelativePosition; exports.createDecorations = createDecorations; exports.defaultAwarenessStateFilter = defaultAwarenessStateFilter; exports.defaultCursorBuilder = defaultCursorBuilder; exports.defaultDeleteFilter = defaultDeleteFilter; exports.defaultProtectedNodes = defaultProtectedNodes; exports.defaultSelectionBuilder = defaultSelectionBuilder; exports.findAbsolutePositionAfterStructuralChange = findAbsolutePositionAfterStructuralChange; exports.getRelativeSelection = getRelativeSelection; exports.initProseMirrorDoc = initProseMirrorDoc; exports.isMisresolvedAfterStructuralChange = isMisresolvedAfterStructuralChange; exports.isMisresolvedTextPosition = isMisresolvedTextPosition; exports.isStructuralTransaction = isStructuralTransaction; exports.isVisible = isVisible; exports.prosemirrorJSONToYDoc = prosemirrorJSONToYDoc; exports.prosemirrorJSONToYXmlFragment = prosemirrorJSONToYXmlFragment; exports.prosemirrorToYDoc = prosemirrorToYDoc; exports.prosemirrorToYXmlFragment = prosemirrorToYXmlFragment; exports.redo = redo; exports.relativePositionToAbsolutePosition = relativePositionToAbsolutePosition; exports.setMeta = setMeta; exports.undo = undo; exports.updateYFragment = updateYFragment; exports.yCursorPlugin = yCursorPlugin; exports.yCursorPluginKey = yCursorPluginKey; exports.yDocToProsemirror = yDocToProsemirror; exports.yDocToProsemirrorJSON = yDocToProsemirrorJSON; exports.ySyncPlugin = ySyncPlugin; exports.ySyncPluginKey = ySyncPluginKey; exports.yUndoPlugin = yUndoPlugin; exports.yUndoPluginKey = yUndoPluginKey; exports.yXmlFragmentToProseMirrorFragment = yXmlFragmentToProseMirrorFragment; exports.yXmlFragmentToProseMirrorRootNode = yXmlFragmentToProseMirrorRootNode; exports.yXmlFragmentToProsemirror = yXmlFragmentToProsemirror; exports.yXmlFragmentToProsemirrorJSON = yXmlFragmentToProsemirrorJSON; //# sourceMappingURL=index.cjs.map