UNPKG

alinea

Version:

[![npm](https://img.shields.io/npm/v/alinea.svg)](https://npmjs.org/package/alinea) [![install size](https://packagephobia.com/badge?p=alinea)](https://packagephobia.com/result?p=alinea)

1,502 lines (1,491 loc) 47.5 kB
import { EditorContent, FloatingMenu, NodeViewWrapper, ReactNodeViewRenderer } from "../../chunks/chunk-OBYSELPT.js"; import "../../chunks/chunk-I5C4WAC4.js"; import { Extension, Fragment, Node, Plugin, PluginKey, Slice, TextSelection, mergeAttributes } from "../../chunks/chunk-MDIOFKJQ.js"; import { ContentFormat, ContentString, ContentType, Item, RelativePosition, Snapshot, UndoManager, YText, YXmlElement, YXmlText, create, createAbsolutePositionFromRelativePosition, createDeleteSet, createID, createRelativePositionFromTypeIndex, createSnapshot, doc, findRootTypeKey, isBrowser, isDeleted, isParentOf, iterateDeletedStructs, keys, oneOf, snapshot, timeout, typeListToArraySnapshot } from "../../chunks/chunk-OYP4EJOA.js"; import { methodUnimplemented, min, unexpectedCase } from "../../chunks/chunk-O6EXLFU2.js"; import "../../chunks/chunk-U5RRZUYZ.js"; // node_modules/lib0/mutex.js var createMutex = () => { let token = true; return (f, g) => { if (token) { token = false; try { f(); } finally { token = true; } } else if (g !== void 0) { g(); } }; }; // node_modules/lib0/diff.js var highSurrogateRegex = /[\uD800-\uDBFF]/; var lowSurrogateRegex = /[\uDC00-\uDFFF]/; var simpleDiffString = (a, b) => { let left = 0; let right = 0; while (left < a.length && left < b.length && a[left] === b[left]) { left++; } if (left > 0 && highSurrogateRegex.test(a[left - 1])) left--; while (right + left < a.length && right + left < b.length && a[a.length - right - 1] === b[b.length - right - 1]) { right++; } if (right > 0 && lowSurrogateRegex.test(a[a.length - right])) right--; return { index: left, remove: a.length - left - right, insert: b.slice(left, b.length - right) }; }; var simpleDiff = simpleDiffString; // node_modules/y-prosemirror/src/plugins/keys.js var ySyncPluginKey = new PluginKey("y-sync"); var yUndoPluginKey = new PluginKey("y-undo"); var yCursorPluginKey = new PluginKey("yjs-cursor"); // node_modules/y-prosemirror/src/plugins/sync-plugin.js var isVisible = (item, snapshot2) => snapshot2 === void 0 ? !item.deleted : snapshot2.sv.has(item.id.client) && /** @type {number} */ snapshot2.sv.get(item.id.client) > item.id.clock && !isDeleted(snapshot2.ds, item.id); var defaultColors = [{ light: "#ecd44433", dark: "#ecd444" }]; var getUserColor = (colorMapping, colors, user) => { if (!colorMapping.has(user)) { if (colorMapping.size < colors.length) { const usedColors = create(); colorMapping.forEach((color) => usedColors.add(color)); colors = colors.filter((color) => !usedColors.has(color)); } colorMapping.set(user, oneOf(colors)); } return ( /** @type {ColorDef} */ colorMapping.get(user) ); }; var ySyncPlugin = (yXmlFragment, { colors = defaultColors, colorMapping = /* @__PURE__ */ new Map(), permanentUserData = null, onFirstRender = () => { } } = {}) => { let changedInitialContent = false; let rerenderTimeout; const plugin = new Plugin({ props: { editable: (state) => { const syncState = ySyncPluginKey.getState(state); return syncState.snapshot == null && syncState.prevSnapshot == null; } }, key: ySyncPluginKey, state: { /** * @returns {any} */ init: (_initargs, _state) => { return { type: yXmlFragment, doc: yXmlFragment.doc, binding: null, snapshot: null, prevSnapshot: null, isChangeOrigin: false, isUndoRedoOperation: false, addToHistory: true, colors, colorMapping, permanentUserData }; }, apply: (tr, pluginState) => { const change = tr.getMeta(ySyncPluginKey); if (change !== void 0) { pluginState = Object.assign({}, pluginState); for (const key in change) { pluginState[key] = change[key]; } } pluginState.addToHistory = tr.getMeta("addToHistory") !== false; pluginState.isChangeOrigin = change !== void 0 && !!change.isChangeOrigin; pluginState.isUndoRedoOperation = change !== void 0 && !!change.isChangeOrigin && !!change.isUndoRedoOperation; if (pluginState.binding !== null) { if (change !== void 0 && (change.snapshot != null || change.prevSnapshot != null)) { timeout(0, () => { if (pluginState.binding == null || pluginState.binding.isDestroyed) { return; } if (change.restore == null) { pluginState.binding._renderSnapshot( change.snapshot, change.prevSnapshot, pluginState ); } else { pluginState.binding._renderSnapshot( change.snapshot, change.snapshot, pluginState ); delete pluginState.restore; delete pluginState.snapshot; delete pluginState.prevSnapshot; pluginState.binding.mux(() => { pluginState.binding._prosemirrorChanged( pluginState.binding.prosemirrorView.state.doc ); }); } }); } } return pluginState; } }, view: (view) => { const binding = new ProsemirrorBinding(yXmlFragment, view); if (rerenderTimeout != null) { rerenderTimeout.destroy(); } rerenderTimeout = timeout(0, () => { binding._forceRerender(); view.dispatch(view.state.tr.setMeta(ySyncPluginKey, { binding })); onFirstRender(); }); return { update: () => { const pluginState = plugin.getState(view.state); if (pluginState.snapshot == null && pluginState.prevSnapshot == null) { if (changedInitialContent || view.state.doc.content.findDiffStart( view.state.doc.type.createAndFill().content ) !== null) { changedInitialContent = 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) => { tr.meta.set("addToHistory", pluginState.addToHistory); binding._prosemirrorChanged(view.state.doc); }, ySyncPluginKey); }); } } }, destroy: () => { rerenderTimeout.destroy(); binding.destroy(); } }; } }); return plugin; }; var restoreRelativeSelection = (tr, relSel, binding) => { if (relSel !== null && relSel.anchor !== null && relSel.head !== null) { const anchor = relativePositionToAbsolutePosition( binding.doc, binding.type, relSel.anchor, binding.mapping ); const head = relativePositionToAbsolutePosition( binding.doc, binding.type, relSel.head, binding.mapping ); if (anchor !== null && head !== null) { tr = tr.setSelection(TextSelection.create(tr.doc, anchor, head)); } } }; var getRelativeSelection = (pmbinding, state) => ({ anchor: absolutePositionToRelativePosition( state.selection.anchor, pmbinding.type, pmbinding.mapping ), head: absolutePositionToRelativePosition( state.selection.head, pmbinding.type, pmbinding.mapping ) }); var ProsemirrorBinding = class { /** * @param {Y.XmlFragment} yXmlFragment The bind source * @param {any} prosemirrorView The target binding */ constructor(yXmlFragment, prosemirrorView) { this.type = yXmlFragment; this.prosemirrorView = prosemirrorView; this.mux = createMutex(); this.isDestroyed = false; this.mapping = /* @__PURE__ */ new Map(); this._observeFunction = this._typeChanged.bind(this); this.doc = yXmlFragment.doc; this.beforeTransactionSelection = null; this.beforeAllTransactions = () => { if (this.beforeTransactionSelection === null) { this.beforeTransactionSelection = getRelativeSelection( this, prosemirrorView.state ); } }; this.afterAllTransactions = () => { this.beforeTransactionSelection = null; }; this.doc.on("beforeAllTransactions", this.beforeAllTransactions); this.doc.on("afterAllTransactions", this.afterAllTransactions); yXmlFragment.observeDeep(this._observeFunction); this._domSelectionInView = null; } /** * Create a transaction for changing the prosemirror state. * * @returns */ get _tr() { return this.prosemirrorView.state.tr.setMeta("addToHistory", false); } _isLocalCursorInView() { if (!this.prosemirrorView.hasFocus()) return false; if (isBrowser && this._domSelectionInView === null) { timeout(0, () => { this._domSelectionInView = null; }); this._domSelectionInView = this._isDomSelectionInView(); } return this._domSelectionInView; } _isDomSelectionInView() { const selection = this.prosemirrorView._root.getSelection(); const range = this.prosemirrorView._root.createRange(); range.setStart(selection.anchorNode, selection.anchorOffset); range.setEnd(selection.focusNode, selection.focusOffset); const rects = range.getClientRects(); if (rects.length === 0) { if (range.startContainer && range.collapsed) { range.selectNodeContents(range.startContainer); } } const bounding = range.getBoundingClientRect(); const documentElement = doc.documentElement; return bounding.bottom >= 0 && bounding.right >= 0 && bounding.left <= (window.innerWidth || documentElement.clientWidth || 0) && bounding.top <= (window.innerHeight || documentElement.clientHeight || 0); } /** * @param {Y.Snapshot} snapshot * @param {Y.Snapshot} prevSnapshot */ renderSnapshot(snapshot2, prevSnapshot) { if (!prevSnapshot) { prevSnapshot = createSnapshot(createDeleteSet(), /* @__PURE__ */ new Map()); } this.prosemirrorView.dispatch( this._tr.setMeta(ySyncPluginKey, { snapshot: snapshot2, prevSnapshot }) ); } unrenderSnapshot() { this.mapping = /* @__PURE__ */ new Map(); this.mux(() => { const fragmentContent = this.type.toArray().map( (t) => createNodeFromYElement( /** @type {Y.XmlElement} */ t, this.prosemirrorView.state.schema, this.mapping ) ).filter((n) => n !== null); const tr = this._tr.replace( 0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0) ); tr.setMeta(ySyncPluginKey, { snapshot: null, prevSnapshot: null }); this.prosemirrorView.dispatch(tr); }); } _forceRerender() { this.mapping = /* @__PURE__ */ new Map(); this.mux(() => { const fragmentContent = this.type.toArray().map( (t) => createNodeFromYElement( /** @type {Y.XmlElement} */ t, this.prosemirrorView.state.schema, this.mapping ) ).filter((n) => n !== null); const tr = this._tr.replace( 0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0) ); this.prosemirrorView.dispatch( tr.setMeta(ySyncPluginKey, { isChangeOrigin: true }) ); }); } /** * @param {Y.Snapshot} snapshot * @param {Y.Snapshot} prevSnapshot * @param {Object} pluginState */ _renderSnapshot(snapshot2, prevSnapshot, pluginState) { if (!snapshot2) { snapshot2 = snapshot(this.doc); } this.mapping = /* @__PURE__ */ new Map(); this.mux(() => { this.doc.transact((transaction) => { const pud = pluginState.permanentUserData; if (pud) { pud.dss.forEach((ds) => { iterateDeletedStructs(transaction, ds, (_item) => { }); }); } const computeYChange = (type, id) => { const user = type === "added" ? pud.getUserByClientId(id.client) : pud.getUserByDeletedId(id); return { user, type, color: getUserColor( pluginState.colorMapping, pluginState.colors, user ) }; }; const fragmentContent = typeListToArraySnapshot( this.type, new Snapshot(prevSnapshot.ds, snapshot2.sv) ).map((t) => { if (!t._item.deleted || isVisible(t._item, snapshot2) || isVisible(t._item, prevSnapshot)) { return createNodeFromYElement( t, this.prosemirrorView.state.schema, /* @__PURE__ */ new Map(), snapshot2, prevSnapshot, computeYChange ); } else { return null; } }).filter((n) => n !== null); const tr = this._tr.replace( 0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0) ); this.prosemirrorView.dispatch( tr.setMeta(ySyncPluginKey, { isChangeOrigin: true }) ); }, ySyncPluginKey); }); } /** * @param {Array<Y.YEvent<any>>} events * @param {Y.Transaction} transaction */ _typeChanged(events, transaction) { const syncState = ySyncPluginKey.getState(this.prosemirrorView.state); if (events.length === 0 || syncState.snapshot != null || syncState.prevSnapshot != null) { this.renderSnapshot(syncState.snapshot, syncState.prevSnapshot); return; } this.mux(() => { const delType = (_, type) => this.mapping.delete(type); iterateDeletedStructs( transaction, transaction.deleteSet, (struct) => { if (struct.constructor === Item) { const type = ( /** @type {Y.ContentType} */ /** @type {Y.Item} */ struct.content.type ); type && this.mapping.delete(type); } } ); transaction.changed.forEach(delType); transaction.changedParentTypes.forEach(delType); const fragmentContent = this.type.toArray().map( (t) => createNodeIfNotExists( /** @type {Y.XmlElement | Y.XmlHook} */ t, this.prosemirrorView.state.schema, this.mapping ) ).filter((n) => n !== null); let tr = this._tr.replace( 0, this.prosemirrorView.state.doc.content.size, new Slice(Fragment.from(fragmentContent), 0, 0) ); restoreRelativeSelection(tr, this.beforeTransactionSelection, this); tr = tr.setMeta(ySyncPluginKey, { isChangeOrigin: true, isUndoRedoOperation: transaction.origin instanceof UndoManager }); if (this.beforeTransactionSelection !== null && this._isLocalCursorInView()) { tr.scrollIntoView(); } this.prosemirrorView.dispatch(tr); }); } _prosemirrorChanged(doc2) { this.doc.transact(() => { updateYFragment(this.doc, this.type, doc2, this.mapping); this.beforeTransactionSelection = getRelativeSelection( this, this.prosemirrorView.state ); }, ySyncPluginKey); } destroy() { this.isDestroyed = true; this.type.unobserveDeep(this._observeFunction); this.doc.off("beforeAllTransactions", this.beforeAllTransactions); this.doc.off("afterAllTransactions", this.afterAllTransactions); } }; var createNodeIfNotExists = (el, schema, mapping, snapshot2, prevSnapshot, computeYChange) => { const node = ( /** @type {PModel.Node} */ mapping.get(el) ); if (node === void 0) { if (el instanceof YXmlElement) { return createNodeFromYElement( el, schema, mapping, snapshot2, prevSnapshot, computeYChange ); } else { throw methodUnimplemented(); } } return node; }; var createNodeFromYElement = (el, schema, mapping, snapshot2, prevSnapshot, computeYChange) => { const children = []; const createChildren = (type) => { if (type.constructor === YXmlElement) { const n = createNodeIfNotExists( type, schema, mapping, snapshot2, prevSnapshot, computeYChange ); if (n !== null) { children.push(n); } } else { const ns = createTextNodesFromYText( type, schema, mapping, snapshot2, prevSnapshot, computeYChange ); if (ns !== null) { ns.forEach((textchild) => { if (textchild !== null) { children.push(textchild); } }); } } }; if (snapshot2 === void 0 || prevSnapshot === void 0) { el.toArray().forEach(createChildren); } else { typeListToArraySnapshot(el, new Snapshot(prevSnapshot.ds, snapshot2.sv)).forEach(createChildren); } try { const attrs = el.getAttributes(snapshot2); if (snapshot2 !== void 0) { if (!isVisible( /** @type {Y.Item} */ el._item, snapshot2 )) { attrs.ychange = computeYChange ? computeYChange( "removed", /** @type {Y.Item} */ el._item.id ) : { type: "removed" }; } else if (!isVisible( /** @type {Y.Item} */ el._item, prevSnapshot )) { attrs.ychange = computeYChange ? computeYChange( "added", /** @type {Y.Item} */ el._item.id ) : { type: "added" }; } } const node = schema.node(el.nodeName, attrs, children); mapping.set(el, node); return node; } catch (e) { el.doc.transact((transaction) => { el._item.delete(transaction); }, ySyncPluginKey); mapping.delete(el); return null; } }; var createTextNodesFromYText = (text, schema, _mapping, snapshot2, prevSnapshot, computeYChange) => { const nodes = []; const deltas = text.toDelta(snapshot2, prevSnapshot, computeYChange); try { for (let i = 0; i < deltas.length; i++) { const delta = deltas[i]; const marks = []; for (const markName in delta.attributes) { marks.push(schema.mark(markName, delta.attributes[markName])); } nodes.push(schema.text(delta.insert, marks)); } } catch (e) { text.doc.transact((transaction) => { text._item.delete(transaction); }, ySyncPluginKey); return null; } return nodes; }; var createTypeFromTextNodes = (nodes, mapping) => { const type = new YXmlText(); const delta = nodes.map((node) => ({ // @ts-ignore insert: node.text, attributes: marksToAttributes(node.marks) })); type.applyDelta(delta); mapping.set(type, nodes); return type; }; var createTypeFromElementNode = (node, mapping) => { const type = new YXmlElement(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, mapping) ) ); mapping.set(type, node); return type; }; var createTypeFromTextOrElementNode = (node, mapping) => node instanceof Array ? createTypeFromTextNodes(node, mapping) : createTypeFromElementNode(node, mapping); var isObject = (val) => typeof val === "object" && val !== null; var equalAttrs = (pattrs, yattrs) => { const keys2 = Object.keys(pattrs).filter((key) => pattrs[key] !== null); let eq = keys2.length === Object.keys(yattrs).filter((key) => yattrs[key] !== null).length; for (let i = 0; i < keys2.length && eq; i++) { const key = keys2[i]; const l = pattrs[key]; const r = yattrs[key]; eq = key === "ychange" || l === r || isObject(l) && isObject(r) && equalAttrs(l, r); } return eq; }; var 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; }; var equalYTextPText = (ytext, ptexts) => { const delta = ytext.toDelta(); return delta.length === ptexts.length && delta.every( (d, i) => d.insert === /** @type {any} */ ptexts[i].text && keys(d.attributes || {}).length === ptexts[i].marks.length && ptexts[i].marks.every( (mark) => equalAttrs(d.attributes[mark.type.name] || {}, mark.attrs) ) ); }; var equalYTypePNode = (ytype, pnode) => { if (ytype instanceof YXmlElement && !(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 YXmlText && pnode instanceof Array && equalYTextPText(ytype, pnode); }; var mappedIdentity = (mapped, pcontent) => mapped === pcontent || mapped instanceof Array && pcontent instanceof Array && mapped.length === pcontent.length && mapped.every( (a, i) => pcontent[i] === a ); var computeChildEqualityFactor = (ytype, pnode, mapping) => { const yChildren = ytype.toArray(); const pChildren = normalizePNodeContent(pnode); const pChildCnt = pChildren.length; const yChildCnt = yChildren.length; const minCnt = 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(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(mapping.get(rightY), rightP)) { foundMappedChild = true; } else if (!equalYTypePNode(rightY, rightP)) { break; } } return { equalityFactor: left + right, foundMappedChild }; }; var ytextTrans = (ytext) => { let str = ""; let n = ytext._start; const nAttrs = {}; while (n !== null) { if (!n.deleted) { if (n.countable && n.content instanceof ContentString) { str += n.content.str; } else if (n.content instanceof ContentFormat) { nAttrs[n.content.key] = null; } } n = n.right; } return { str, nAttrs }; }; var updateYText = (ytext, ptexts, mapping) => { mapping.set(ytext, ptexts); const { nAttrs, str } = ytextTrans(ytext); const content = ptexts.map((p) => ({ insert: ( /** @type {any} */ p.text ), attributes: Object.assign({}, nAttrs, marksToAttributes(p.marks)) })); const { insert, remove, index } = 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 })) ); }; var marksToAttributes = (marks) => { const pattrs = {}; marks.forEach((mark) => { if (mark.type.name !== "ychange") { pattrs[mark.type.name] = mark.attrs; } }); return pattrs; }; var updateYFragment = (y, yDomFragment, pNode, mapping) => { if (yDomFragment instanceof YXmlElement && yDomFragment.nodeName !== pNode.type.name) { throw new Error("node name mismatch!"); } mapping.set(yDomFragment, pNode); if (yDomFragment instanceof YXmlElement) { const yDomAttrs = yDomFragment.getAttributes(); const pAttrs = pNode.attrs; for (const key in pAttrs) { if (pAttrs[key] !== null) { if (yDomAttrs[key] !== pAttrs[key] && key !== "ychange") { yDomFragment.setAttribute(key, pAttrs[key]); } } else { yDomFragment.removeAttribute(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 = min(pChildCnt, yChildCnt); let left = 0; let right = 0; for (; left < minCnt; left++) { const leftY = yChildren[left]; const leftP = pChildren[left]; if (!mappedIdentity(mapping.get(leftY), leftP)) { if (equalYTypePNode(leftY, leftP)) { mapping.set(leftY, leftP); } else { break; } } } for (; right + left + 1 < minCnt; right++) { const rightY = yChildren[yChildCnt - right - 1]; const rightP = pChildren[pChildCnt - right - 1]; if (!mappedIdentity(mapping.get(rightY), rightP)) { if (equalYTypePNode(rightY, rightP)) { mapping.set(rightY, rightP); } else { break; } } } 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 YXmlText && leftP instanceof Array) { if (!equalYTextPText(leftY, leftP)) { updateYText(leftY, leftP, mapping); } left += 1; } else { let updateLeft = leftY instanceof YXmlElement && matchNodeName(leftY, leftP); let updateRight = rightY instanceof YXmlElement && matchNodeName(rightY, rightP); if (updateLeft && updateRight) { const equalityLeft = computeChildEqualityFactor( /** @type {Y.XmlElement} */ leftY, /** @type {PModel.Node} */ leftP, mapping ); const equalityRight = computeChildEqualityFactor( /** @type {Y.XmlElement} */ rightY, /** @type {PModel.Node} */ rightP, mapping ); 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) { updateYFragment( y, /** @type {Y.XmlFragment} */ leftY, /** @type {PModel.Node} */ leftP, mapping ); left += 1; } else if (updateRight) { updateYFragment( y, /** @type {Y.XmlFragment} */ rightY, /** @type {PModel.Node} */ rightP, mapping ); right += 1; } else { mapping.delete(yDomFragment.get(left)); yDomFragment.delete(left, 1); yDomFragment.insert(left, [ createTypeFromTextOrElementNode(leftP, mapping) ]); left += 1; } } } const yDelLen = yChildCnt - left - right; if (yChildCnt === 1 && pChildCnt === 0 && yChildren[0] instanceof YXmlText) { mapping.delete(yChildren[0]); yChildren[0].delete(0, yChildren[0].length); } else if (yDelLen > 0) { yDomFragment.slice(left, left + yDelLen).forEach((type) => mapping.delete(type)); yDomFragment.delete(left, yDelLen); } if (left + right < pChildCnt) { const ins = []; for (let i = left; i < pChildCnt - right; i++) { ins.push(createTypeFromTextOrElementNode(pChildren[i], mapping)); } yDomFragment.insert(left, ins); } }, ySyncPluginKey); }; var matchNodeName = (yElement, pNode) => !(pNode instanceof Array) && yElement.nodeName === pNode.type.name; // node_modules/y-prosemirror/src/lib.js var absolutePositionToRelativePosition = (pos, type, mapping) => { if (pos === 0) { return createRelativePositionFromTypeIndex(type, 0); } let n = type._first === null ? null : ( /** @type {Y.ContentType} */ type._first.content.type ); while (n !== null && type !== n) { if (n instanceof YXmlText) { if (n._length >= pos) { return createRelativePositionFromTypeIndex(n, pos); } else { pos -= n._length; } if (n._item !== null && n._item.next !== null) { n = /** @type {Y.ContentType} */ n._item.next.content.type; } else { do { n = n._item === null ? null : n._item.parent; pos--; } while (n !== type && n !== null && n._item !== null && n._item.next === null); if (n !== null && n !== type) { n = n._item === null ? null : ( /** @type {Y.ContentType} */ /** @type Y.Item */ n._item.next.content.type ); } } } else { const pNodeSize = ( /** @type {any} */ (mapping.get(n) || { nodeSize: 0 }).nodeSize ); if (n._first !== null && pos < pNodeSize) { n = /** @type {Y.ContentType} */ n._first.content.type; pos--; } else { if (pos === 1 && n._length === 0 && pNodeSize > 1) { return new RelativePosition(n._item === null ? null : n._item.id, n._item === null ? findRootTypeKey(n) : null, null); } pos -= pNodeSize; if (n._item !== null && n._item.next !== null) { n = /** @type {Y.ContentType} */ n._item.next.content.type; } else { if (pos === 0) { n = n._item === null ? n : n._item.parent; return new RelativePosition(n._item === null ? null : n._item.id, n._item === null ? findRootTypeKey(n) : null, null); } do { n = /** @type {Y.Item} */ n._item.parent; pos--; } while (n !== type && /** @type {Y.Item} */ n._item.next === null); if (n !== type) { n = /** @type {Y.ContentType} */ /** @type {Y.Item} */ /** @type {Y.Item} */ n._item.next.content.type; } } } } if (n === null) { throw unexpectedCase(); } if (pos === 0 && n.constructor !== YXmlText && n !== type) { return createRelativePosition(n._item.parent, n._item); } } return createRelativePositionFromTypeIndex(type, type._length); }; var createRelativePosition = (type, item) => { let typeid = null; let tname = null; if (type._item === null) { tname = findRootTypeKey(type); } else { typeid = createID(type._item.id.client, type._item.id.clock); } return new RelativePosition(typeid, tname, item.id); }; var relativePositionToAbsolutePosition = (y, documentType, relPos, mapping) => { const decodedPos = createAbsolutePositionFromRelativePosition(relPos, y); if (decodedPos === null || decodedPos.type !== documentType && !isParentOf(documentType, decodedPos.type._item)) { return null; } let type = decodedPos.type; let pos = 0; if (type.constructor === YXmlText) { pos = decodedPos.index; } else if (type._item === null || !type._item.deleted) { let n = type._first; let i = 0; while (i < type._length && i < decodedPos.index && n !== null) { if (!n.deleted) { const t = ( /** @type {Y.ContentType} */ n.content.type ); i++; if (t instanceof YXmlText) { pos += t._length; } else { pos += /** @type {any} */ mapping.get(t).nodeSize; } } n = /** @type {Y.Item} */ n.right; } pos += 1; } while (type !== documentType && type._item !== null) { const parent = type._item.parent; if (parent._item === null || !parent._item.deleted) { pos += 1; let n = ( /** @type {Y.AbstractType} */ parent._first ); while (n !== null) { const contentType = ( /** @type {Y.ContentType} */ n.content.type ); if (contentType === type) { break; } if (!n.deleted) { if (contentType instanceof YXmlText) { pos += contentType._length; } else { pos += /** @type {any} */ mapping.get(contentType).nodeSize; } } n = n.right; } } type = /** @type {Y.AbstractType} */ parent; } return pos - 1; }; // node_modules/y-prosemirror/src/plugins/undo-plugin.js var undo = (state) => { const undoManager = yUndoPluginKey.getState(state).undoManager; if (undoManager != null) { undoManager.undo(); return true; } }; var redo = (state) => { const undoManager = yUndoPluginKey.getState(state).undoManager; if (undoManager != null) { undoManager.redo(); return true; } }; var defaultProtectedNodes = /* @__PURE__ */ new Set(["paragraph"]); var defaultDeleteFilter = (item, protectedNodes) => !(item instanceof Item) || !(item.content instanceof ContentType) || !(item.content.type instanceof YText || item.content.type instanceof YXmlElement && protectedNodes.has(item.content.type.nodeName)) || item.content.type._length === 0; var yUndoPlugin = ({ protectedNodes = defaultProtectedNodes, trackedOrigins = [], undoManager = null } = {}) => new Plugin({ key: yUndoPluginKey, state: { init: (initargs, state) => { const ystate = ySyncPluginKey.getState(state); const _undoManager = undoManager || new UndoManager(ystate.type, { trackedOrigins: new Set([ySyncPluginKey].concat(trackedOrigins)), deleteFilter: (item) => defaultDeleteFilter(item, protectedNodes), captureTransaction: (tr) => tr.meta.get("addToHistory") !== false }); return { undoManager: _undoManager, prevSel: null, hasUndoOps: _undoManager.undoStack.length > 0, hasRedoOps: _undoManager.redoStack.length > 0 }; }, /** * @returns {any} */ apply: (tr, val, oldState, state) => { const binding = ySyncPluginKey.getState(state).binding; const undoManager2 = val.undoManager; const hasUndoOps = undoManager2.undoStack.length > 0; const hasRedoOps = undoManager2.redoStack.length > 0; if (binding) { return { undoManager: undoManager2, prevSel: getRelativeSelection(binding, oldState), hasUndoOps, hasRedoOps }; } else { if (hasUndoOps !== val.hasUndoOps || hasRedoOps !== val.hasRedoOps) { return Object.assign({}, val, { hasUndoOps: undoManager2.undoStack.length > 0, hasRedoOps: undoManager2.redoStack.length > 0 }); } else { return val; } } } }, view: (view) => { const ystate = ySyncPluginKey.getState(view.state); const undoManager2 = yUndoPluginKey.getState(view.state).undoManager; undoManager2.on("stack-item-added", ({ stackItem }) => { const binding = ystate.binding; if (binding) { stackItem.meta.set(binding, yUndoPluginKey.getState(view.state).prevSel); } }); undoManager2.on("stack-item-popped", ({ stackItem }) => { const binding = ystate.binding; if (binding) { binding.beforeTransactionSelection = stackItem.meta.get(binding) || binding.beforeTransactionSelection; } }); return { destroy: () => { undoManager2.destroy(); } }; } }); // node_modules/@tiptap/extension-collaboration/dist/index.js var Collaboration = Extension.create({ name: "collaboration", priority: 1e3, addOptions() { return { document: null, field: "default", fragment: null }; }, onCreate() { if (this.editor.extensionManager.extensions.find((extension) => extension.name === "history")) { console.warn('[tiptap warn]: "@tiptap/extension-collaboration" comes with its own history support and is not compatible with "@tiptap/extension-history".'); } }, addCommands() { return { undo: () => ({ tr, state, dispatch }) => { tr.setMeta("preventDispatch", true); const undoManager = yUndoPluginKey.getState(state).undoManager; if (undoManager.undoStack.length === 0) { return false; } if (!dispatch) { return true; } return undo(state); }, redo: () => ({ tr, state, dispatch }) => { tr.setMeta("preventDispatch", true); const undoManager = yUndoPluginKey.getState(state).undoManager; if (undoManager.redoStack.length === 0) { return false; } if (!dispatch) { return true; } return redo(state); } }; }, addKeyboardShortcuts() { return { "Mod-z": () => this.editor.commands.undo(), "Mod-y": () => this.editor.commands.redo(), "Shift-Mod-z": () => this.editor.commands.redo() }; }, addProseMirrorPlugins() { const fragment = this.options.fragment ? this.options.fragment : this.options.document.getXmlFragment(this.options.field); const yUndoPluginInstance = yUndoPlugin(); const originalUndoPluginView = yUndoPluginInstance.spec.view; yUndoPluginInstance.spec.view = (view) => { const { undoManager } = yUndoPluginKey.getState(view.state); if (undoManager.restore) { undoManager.restore(); undoManager.restore = () => { }; } const viewRet = originalUndoPluginView(view); return { destroy: () => { const hasUndoManSelf = undoManager.trackedOrigins.has(undoManager); const observers = undoManager._observers; undoManager.restore = () => { if (hasUndoManSelf) { undoManager.trackedOrigins.add(undoManager); } undoManager.doc.on("afterTransaction", undoManager.afterTransactionHandler); undoManager._observers = observers; }; viewRet.destroy(); } }; }; return [ySyncPlugin(fragment), yUndoPluginInstance]; } }); // src/input/richtext/RichTextField.browser.tsx import { createId, Field, Type } from "alinea/core"; import { entries } from "alinea/core/util/Objects"; import { FormRow } from "alinea/dashboard/atoms/FormAtoms"; import { InputForm } from "alinea/dashboard/editor/InputForm"; import { useField, useFieldMutator } from "alinea/dashboard/editor/UseField"; import { IconButton } from "alinea/dashboard/view/IconButton"; import { InputLabel } from "alinea/dashboard/view/InputLabel"; import { fromModule, HStack, Icon, px, TextLabel } from "alinea/ui"; import { DropdownMenu } from "alinea/ui/DropdownMenu"; import IcRoundAddCircle from "alinea/ui/icons/IcRoundAddCircle"; import { IcRoundClose } from "alinea/ui/icons/IcRoundClose"; import { IcRoundDragHandle } from "alinea/ui/icons/IcRoundDragHandle"; import { IcRoundNotes } from "alinea/ui/icons/IcRoundNotes"; import { Sink } from "alinea/ui/Sink"; import { createContext, useCallback, useContext, useMemo, useRef, useState } from "react"; import { useEditor } from "./hook/UseEditor.js"; import { PickTextLink, usePickTextLink } from "./PickTextLink.js"; import { richText as createRichText } from "./RichTextField.js"; // src/input/richtext/RichTextField.module.scss var RichTextField_module_default = { "hyphenate": "alinea-RichTextField-hyphenate", "link": "alinea-RichTextField-link", "p": "alinea-RichTextField-p", "h5": "alinea-RichTextField-h5", "h4": "alinea-RichTextField-h4", "h3": "alinea-RichTextField-h3", "h2": "alinea-RichTextField-h2", "h1": "alinea-RichTextField-h1", "is-flat": "alinea-RichTextField-is-flat", "isFlat": "alinea-RichTextField-is-flat", "monospace": "alinea-RichTextField-monospace", "small": "alinea-RichTextField-small", "root-editor": "alinea-RichTextField-editor", "rootEditor": "alinea-RichTextField-editor", "is-focus": "alinea-RichTextField-is-focus", "isFocus": "alinea-RichTextField-is-focus", "is-readonly": "alinea-RichTextField-is-readonly", "isReadonly": "alinea-RichTextField-is-readonly", "insert-trigger": "alinea-RichTextField-insert-trigger", "insertTrigger": "alinea-RichTextField-insert-trigger" }; // src/input/richtext/RichTextField.browser.tsx import { RichTextKit } from "./RichTextKit.js"; import { RichTextToolbar } from "./RichTextToolbar.js"; export * from "./RichTextField.js"; import { Fragment as Fragment2, jsx, jsxs } from "react/jsx-runtime"; var richText = Field.provideView(RichTextInput, createRichText); var styles = fromModule(RichTextField_module_default); var IsNested = createContext(false); function typeExtension(field, name, type) { function View({ node, deleteNode }) { const { id } = node.attrs; const meta = Type.meta(type); return /* @__PURE__ */ jsx(FormRow, { field, type, rowId: id, children: /* @__PURE__ */ jsx(NodeViewWrapper, { children: /* @__PURE__ */ jsxs(Sink.Root, { style: { margin: `${px(18)} 0` }, children: [ /* @__PURE__ */ jsxs(Sink.Header, { children: [ /* @__PURE__ */ jsx(Sink.Options, { children: /* @__PURE__ */ jsx( IconButton, { icon: meta.icon || IcRoundDragHandle, "data-drag-handle": true, style: { cursor: "grab" } } ) }), /* @__PURE__ */ jsx(Sink.Title, { children: /* @__PURE__ */ jsx(TextLabel, { label: Type.label(type) }) }), /* @__PURE__ */ jsx(Sink.Options, { children: /* @__PURE__ */ jsx(IconButton, { icon: IcRoundClose, onClick: deleteNode }) }) ] }), /* @__PURE__ */ jsx(Sink.Content, { children: /* @__PURE__ */ jsx(IsNested.Provider, { value: true, children: /* @__PURE__ */ jsx(InputForm, { type }) }) }) ] }) }) }); } return Node.create({ name, group: "block", atom: true, draggable: true, parseHTML() { return [{ tag: name }]; }, renderHTML({ HTMLAttributes }) { return [name, mergeAttributes(HTMLAttributes)]; }, addNodeView() { return ReactNodeViewRenderer(View); }, addAttributes() { return { id: { default: null } }; } }); } function schemaToExtensions(field, schema) { if (!schema) return []; return entries(schema).map(([name, type]) => { return typeExtension(field, name, type); }); } function InsertMenu({ editor, schema, onInsert }) { const id = createId(); if (!schema) return null; const blocks = entries(schema).map(([key, type]) => { return /* @__PURE__ */ jsx( DropdownMenu.Item, { onClick: () => { onInsert(id, key); editor.chain().focus().insertContent({ type: key, attrs: { id } }).run(); }, children: /* @__PURE__ */ jsxs(HStack, { center: true, gap: 8, children: [ /* @__PURE__ */ jsx(Icon, { icon: Type.meta(type).icon ?? IcRoundAddCircle }), /* @__PURE__ */ jsx(TextLabel, { label: Type.label(type) }) ] }) }, key ); }); return /* @__PURE__ */ jsx( FloatingMenu, { editor, tippyOptions: { zIndex: 1, maxWidth: "none" }, children: /* @__PURE__ */ jsxs(DropdownMenu.Root, { bottom: true, children: [ /* @__PURE__ */ jsxs(DropdownMenu.Trigger, { className: styles.insert.trigger(), children: [ /* @__PURE__ */ jsx(Icon, { icon: IcRoundAddCircle }), /* @__PURE__ */ jsx("span", { children: "Insert block" }) ] }), /* @__PURE__ */ jsx(DropdownMenu.Items, { children: blocks }) ] }) } ); } function RichTextEditor({ field }) { const { value, mutator, label, options } = useField(field); const { readOnly, fragment, insert } = mutator; const picker = usePickTextLink(); const { optional, inline, help, width, schema } = options; const [focus, setFocus] = useState(false); const toolbarRef = useRef(null); const containerRef = useRef(null); const focusToggle = useCallback( function focusToggle2(target) { const element = target || document.activeElement; const editorElement = () => containerRef.current?.querySelector( `${styles.root.editor.toSelector()} > .ProseMirror` ); const isFocused = toolbarRef.current?.contains(element) || element === editorElement() || false; setFocus(isFocused); }, [setFocus, containerRef, toolbarRef] ); const extensions = [ Collaboration.configure({ fragment }), RichTextKit, ...schemaToExtensions(field, schema) ]; const isNested = useContext(IsNested); const content = useMemo( () => ({ type: "doc", content: value.map((node) => { if (node.type === "text") return node; const { type, ...attrs } = node; if (schema?.[type]) return { type, attrs: { id: node.id } }; return { type, content: "content" in node ? node.content : void 0, attrs }; }) }), [] ); const onFocus = useCallback( ({ event }) => focusToggle(event.currentTarget), [focusToggle] ); const onBlur = useCallback( ({ event }) => focusToggle(event.relatedTarget), [focusToggle] ); const editor = useEditor( { content: isNested ? void 0 : content, onFocus, onBlur, extensions, editable: !options.readOnly && !readOnly }, [] ); if (!editor) return null; return /* @__PURE__ */ jsxs(Fragment2, { children: [ focus && /* @__PURE__ */ jsx( RichTextToolbar, { ref: toolbarRef, editor, focusToggle, pickLink: picker.pickLink } ), /* @__PURE__ */ jsx(PickTextLink, { picker }), /* @__PURE__ */ jsxs( InputLabel, { label, help, optional, inline, width, focused: focus, icon: IcRoundNotes, empty: editor.isEmpty, ref: containerRef, children: [ /* @__PURE__ */ jsx(InsertMenu, { editor, schema, onInsert: insert }), /* @__PURE__ */ jsx( EditorContent, { className: styles.root.editor({ focus, readonly: options.readOnly }), editor } ) ] } ) ] }); } function RichTextInput({ field }) { const { fragment } = useFieldMutator(field); const key = useMemo(createId, [fragment]); return /* @__PURE__ */ jsx(RichTextEditor, { field }, key); } export { RichTextInput, richText };