@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
1 lines • 318 kB
Source Map (JSON)
{"version":3,"file":"extensions-CSB9ZvuL.cjs","names":[],"sources":["../src/api/exporters/html/util/serializeBlocksExternalHTML.ts","../src/api/exporters/html/externalHTMLExporter.ts","../src/api/getBlocksChangedByTransaction.ts","../src/extensions/BlockChange/BlockChange.ts","../src/extensions/DropCursor/utils.ts","../src/extensions/DropCursor/DropCursor.ts","../src/extensions/History/History.ts","../src/extensions/InlineContentBoundaryEdit/InlineContentBoundaryEdit.ts","../src/extensions/LinkToolbar/LinkToolbar.ts","../src/extensions/LinkToolbar/protocols.ts","../src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.ts","../src/extensions/Placeholder/Placeholder.ts","../src/extensions/PositionMapping/PositionMapping.ts","../src/extensions/PreviousBlockType/PreviousBlockType.ts","../src/extensions/getDraggableBlockFromElement.ts","../src/api/exporters/markdown/htmlToMarkdown.ts","../src/api/exporters/markdown/markdownExporter.ts","../src/api/nodeConversions/fragmentToBlocks.ts","../src/extensions/SideMenu/MultipleNodeSelection.ts","../src/extensions/SideMenu/dragging.ts","../src/extensions/SideMenu/SideMenu.ts","../src/extensions/SourceBlockWithPreview/SourceBlockWithPreview.ts","../src/extensions/SourceInlineContentWithPreview/SourceInlineContentWithPreview.ts","../src/extensions/SyntaxHighlighting/shiki.ts","../src/extensions/SyntaxHighlighting/SyntaxHighlighting.ts","../src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts","../src/extensions/TableHandles/TableHandles.ts","../src/extensions/TrailingNode/TrailingNode.ts","../src/extensions/Versioning/Versioning.ts","../src/extensions/Versioning/inMemoryVersioning.ts"],"sourcesContent":["import { DOMSerializer, Fragment, Node } from \"prosemirror-model\";\n\nimport { PartialBlock } from \"../../../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../../../editor/BlockNoteEditor.js\";\nimport {\n BlockImplementation,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../../../schema/index.js\";\nimport { UnreachableCaseError } from \"../../../../util/typescript.js\";\nimport {\n inlineContentToNodes,\n tableContentToNodes,\n} from \"../../../nodeConversions/blockToNode.js\";\nimport { nodeToCustomInlineContent } from \"../../../nodeConversions/nodeToBlock.js\";\n\n/**\n * Placeholder character inserted into empty inline-content blocks when\n * exporting to external HTML. An empty block serializes to an element with no\n * children (e.g. `<p></p>`), which is dropped when the HTML is parsed back into\n * blocks. Filling it with this character keeps the block alive through the\n * round trip, and the parser strips the character again so the block ends up\n * empty (see `HTMLToBlocks`).\n *\n * The Unicode object replacement character (U+FFFC) is used because it's a\n * reserved placeholder that users don't type, so it can be safely removed on\n * import without discarding legitimate content (unlike a non-breaking space).\n */\nexport const EMPTY_BLOCK_PLACEHOLDER = \"\";\n\nfunction addAttributesAndRemoveClasses(element: HTMLElement) {\n // Removes all BlockNote specific class names.\n const className =\n Array.from(element.classList).filter(\n (className) => !className.startsWith(\"bn-\"),\n ) || [];\n\n if (className.length > 0) {\n element.className = className.join(\" \");\n } else {\n element.removeAttribute(\"class\");\n }\n}\n\nexport function serializeInlineContentExternalHTML<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n editor: BlockNoteEditor<any, I, S>,\n blockContent: PartialBlock<BSchema, I, S>[\"content\"],\n serializer: DOMSerializer,\n options?: { document?: Document; blockType?: string },\n) {\n let nodes: Node[];\n\n // TODO: reuse function from nodeconversions?\n if (!blockContent) {\n throw new Error(\"blockContent is required\");\n } else if (typeof blockContent === \"string\") {\n nodes = inlineContentToNodes(\n [blockContent],\n editor.pmSchema,\n options?.blockType,\n );\n } else if (Array.isArray(blockContent)) {\n nodes = inlineContentToNodes(\n blockContent,\n editor.pmSchema,\n options?.blockType,\n );\n } else if (blockContent.type === \"tableContent\") {\n nodes = tableContentToNodes(blockContent, editor.pmSchema);\n } else {\n throw new UnreachableCaseError(blockContent.type);\n }\n\n // Check if any of the nodes are custom inline content with toExternalHTML\n const doc = options?.document ?? document;\n const fragment = doc.createDocumentFragment();\n\n for (const node of nodes) {\n // Check if this is a custom inline content node with toExternalHTML\n if (\n node.type.name !== \"text\" &&\n editor.schema.inlineContentSchema[node.type.name]\n ) {\n const inlineContentImplementation =\n editor.schema.inlineContentSpecs[node.type.name].implementation;\n\n if (inlineContentImplementation) {\n // Convert the node to inline content format\n const inlineContent = nodeToCustomInlineContent(\n node,\n editor.schema.inlineContentSchema,\n editor.schema.styleSchema,\n );\n\n // Use the custom toExternalHTML method or fallback to `render`\n const output = inlineContentImplementation.toExternalHTML\n ? inlineContentImplementation.toExternalHTML(\n inlineContent as any,\n editor as any,\n )\n : inlineContentImplementation.render.call(\n {\n renderType: \"dom\",\n props: undefined,\n },\n inlineContent as any,\n () => {\n // No-op\n },\n editor as any,\n );\n\n if (output) {\n fragment.appendChild(output.dom);\n\n // If contentDOM exists, render the inline content into it\n if (output.contentDOM) {\n const contentFragment = serializer.serializeFragment(\n node.content,\n options,\n );\n output.contentDOM.dataset.editable = \"\";\n output.contentDOM.appendChild(contentFragment);\n }\n continue;\n }\n }\n } else if (node.type.name === \"text\") {\n // We serialize text nodes manually as we need to serialize the styles/\n // marks using `styleSpec.implementation.render`. When left up to\n // ProseMirror, it'll use `toDOM` which is incorrect.\n let dom: globalThis.Node | Text = document.createTextNode(\n node.textContent,\n );\n // Reverse the order of marks to maintain the correct priority.\n for (const mark of node.marks.toReversed()) {\n if (mark.type.name in editor.schema.styleSpecs) {\n const newDom = (\n editor.schema.styleSpecs[mark.type.name].implementation\n .toExternalHTML ??\n editor.schema.styleSpecs[mark.type.name].implementation.render\n )(mark.attrs[\"stringValue\"], editor);\n newDom.contentDOM!.appendChild(dom);\n dom = newDom.dom;\n } else {\n const domOutputSpec = mark.type.spec.toDOM!(mark, true);\n const newDom = DOMSerializer.renderSpec(document, domOutputSpec);\n newDom.contentDOM!.appendChild(dom);\n dom = newDom.dom;\n }\n }\n\n fragment.appendChild(dom);\n } else {\n // Fall back to default serialization for this node\n const nodeFragment = serializer.serializeFragment(\n Fragment.from([node]),\n options,\n );\n fragment.appendChild(nodeFragment);\n }\n }\n\n if (\n fragment.childNodes.length === 1 &&\n fragment.firstChild?.nodeType === 1 /* Node.ELEMENT_NODE */\n ) {\n addAttributesAndRemoveClasses(fragment.firstChild as HTMLElement);\n }\n\n return fragment;\n}\n\n/**\n * TODO: there's still quite some logic that handles getting and filtering properties,\n * we should make sure the `toExternalHTML` methods of default blocks actually handle this,\n * instead of the serializer.\n */\nfunction serializeBlock<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n fragment: DocumentFragment,\n editor: BlockNoteEditor<BSchema, I, S>,\n block: PartialBlock<BSchema, I, S>,\n serializer: DOMSerializer,\n orderedListItemBlockTypes: Set<string>,\n unorderedListItemBlockTypes: Set<string>,\n nestingLevel: number,\n options?: { document?: Document },\n) {\n const doc = options?.document ?? document;\n const BC_NODE = editor.pmSchema.nodes[\"blockContainer\"];\n\n // set default props in case we were passed a partial block\n const props = block.props || {};\n for (const [name, spec] of Object.entries(\n editor.schema.blockSchema[block.type as any].propSchema,\n )) {\n if (!(name in props) && spec.default !== undefined) {\n (props as any)[name] = spec.default;\n }\n }\n\n const bc = BC_NODE.spec?.toDOM?.(\n BC_NODE.create({\n id: block.id,\n ...props,\n }),\n ) as {\n dom: HTMLElement;\n contentDOM?: HTMLElement;\n };\n\n // the container node is just used as a workaround to get some block-level attributes.\n // we should change toExternalHTML so that this is not necessary\n const attrs = Array.from(bc.dom.attributes);\n\n const blockImplementation = editor.blockImplementations[block.type as any]\n .implementation as BlockImplementation;\n const ret =\n blockImplementation.toExternalHTML?.call(\n {},\n { ...block, props } as any,\n editor as any,\n {\n nestingLevel,\n },\n ) ||\n blockImplementation.render.call(\n {},\n { ...block, props } as any,\n editor as any,\n );\n\n const elementFragment = doc.createDocumentFragment();\n\n if ((ret.dom as HTMLElement).classList.contains(\"bn-block-content\")) {\n const blockContentDataAttributes = [\n ...attrs,\n ...Array.from((ret.dom as HTMLElement).attributes),\n ].filter(\n (attr) =>\n attr.name.startsWith(\"data\") &&\n attr.name !== \"data-content-type\" &&\n attr.name !== \"data-file-block\" &&\n attr.name !== \"data-node-view-wrapper\" &&\n attr.name !== \"data-node-type\" &&\n attr.name !== \"data-id\" &&\n attr.name !== \"data-editable\",\n );\n\n // ret.dom = ret.dom.firstChild! as any;\n for (const attr of blockContentDataAttributes) {\n (ret.dom.firstChild! as HTMLElement).setAttribute(attr.name, attr.value);\n }\n\n addAttributesAndRemoveClasses(ret.dom.firstChild! as HTMLElement);\n if (nestingLevel > 0) {\n (ret.dom.firstChild! as HTMLElement).setAttribute(\n \"data-nesting-level\",\n nestingLevel.toString(),\n );\n }\n elementFragment.append(...Array.from(ret.dom.childNodes));\n } else {\n elementFragment.append(ret.dom);\n if (nestingLevel > 0) {\n (ret.dom as HTMLElement).setAttribute(\n \"data-nesting-level\",\n nestingLevel.toString(),\n );\n }\n }\n\n if (ret.contentDOM) {\n if (block.content) {\n const ic = serializeInlineContentExternalHTML(\n editor,\n block.content as any, // TODO\n serializer,\n { ...options, blockType: block.type },\n );\n\n ret.contentDOM.appendChild(ic);\n }\n\n // Blocks with empty inline content (e.g. an empty paragraph) serialize to\n // an element with no children (e.g. `<p></p>`), which is ignored when the\n // HTML is parsed back into blocks. To make these blocks survive such a\n // round trip, we fill their content with a placeholder character that the\n // parser strips out again (see `EMPTY_BLOCK_PLACEHOLDER`).\n //\n // Only applies to blocks that hold inline content: containers (columns,\n // tables) fill their `contentDOM` with child blocks later on, and code\n // blocks would turn the placeholder into literal content.\n const blockNodeType = editor.pmSchema.nodes[block.type as any];\n if (\n blockNodeType?.inlineContent &&\n !blockNodeType.spec.code &&\n ret.contentDOM.childNodes.length === 0\n ) {\n ret.contentDOM.appendChild(doc.createTextNode(EMPTY_BLOCK_PLACEHOLDER));\n }\n }\n\n let listType = undefined;\n if (orderedListItemBlockTypes.has(block.type!)) {\n listType = \"OL\";\n } else if (unorderedListItemBlockTypes.has(block.type!)) {\n listType = \"UL\";\n }\n\n if (listType) {\n if (fragment.lastChild?.nodeName !== listType) {\n const list = doc.createElement(listType);\n\n if (\n listType === \"OL\" &&\n \"start\" in props &&\n props.start &&\n props?.start !== 1\n ) {\n // eslint-disable-next-line @typescript-eslint/no-base-to-string\n list.setAttribute(\"start\", String(props.start));\n }\n fragment.append(list);\n }\n fragment.lastChild!.appendChild(elementFragment);\n } else {\n fragment.append(elementFragment);\n }\n\n if (block.children && block.children.length > 0) {\n const childFragment = doc.createDocumentFragment();\n serializeBlocksToFragment(\n childFragment,\n editor,\n block.children,\n serializer,\n orderedListItemBlockTypes,\n unorderedListItemBlockTypes,\n nestingLevel + 1,\n options,\n );\n if (\n fragment.lastChild?.nodeName === \"UL\" ||\n fragment.lastChild?.nodeName === \"OL\"\n ) {\n // add nested lists to the last list item\n while (\n childFragment.firstChild?.nodeName === \"UL\" ||\n childFragment.firstChild?.nodeName === \"OL\"\n ) {\n fragment.lastChild!.lastChild!.appendChild(childFragment.firstChild!);\n }\n }\n\n if (\"childrenDOM\" in ret && ret.childrenDOM) {\n // block specifies where children should go (e.g. toggle blocks\n // place children inside <details>)\n ret.childrenDOM.append(childFragment);\n } else if (\n editor.pmSchema.nodes[block.type as any].isInGroup(\"blockContent\")\n ) {\n // default \"blockContainer\" style blocks are flattened (no \"nested block\" support) for externalHTML, so append the child fragment to the outer fragment\n fragment.append(childFragment);\n } else {\n // for columns / column lists, do use nesting\n ret.contentDOM?.append(childFragment);\n }\n }\n}\n\nconst serializeBlocksToFragment = <\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n fragment: DocumentFragment,\n editor: BlockNoteEditor<BSchema, I, S>,\n blocks: PartialBlock<BSchema, I, S>[],\n serializer: DOMSerializer,\n orderedListItemBlockTypes: Set<string>,\n unorderedListItemBlockTypes: Set<string>,\n nestingLevel = 0,\n options?: { document?: Document },\n) => {\n for (const block of blocks) {\n serializeBlock(\n fragment,\n editor,\n block,\n serializer,\n orderedListItemBlockTypes,\n unorderedListItemBlockTypes,\n nestingLevel,\n options,\n );\n }\n};\n\nexport const serializeBlocksExternalHTML = <\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n editor: BlockNoteEditor<BSchema, I, S>,\n blocks: PartialBlock<BSchema, I, S>[],\n serializer: DOMSerializer,\n orderedListItemBlockTypes: Set<string>,\n unorderedListItemBlockTypes: Set<string>,\n options?: { document?: Document },\n) => {\n const doc = options?.document ?? document;\n const fragment = doc.createDocumentFragment();\n\n serializeBlocksToFragment(\n fragment,\n editor,\n blocks,\n serializer,\n orderedListItemBlockTypes,\n unorderedListItemBlockTypes,\n 0,\n options,\n );\n return fragment;\n};\n","import { DOMSerializer, Schema } from \"prosemirror-model\";\n\nimport { PartialBlock } from \"../../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport {\n BlockSchema,\n InlineContent,\n InlineContentSchema,\n StyleSchema,\n} from \"../../../schema/index.js\";\nimport {\n serializeBlocksExternalHTML,\n serializeInlineContentExternalHTML,\n} from \"./util/serializeBlocksExternalHTML.js\";\n\n// Used to export BlockNote blocks and ProseMirror nodes to HTML for use outside\n// the editor. Blocks are exported using the `toExternalHTML` method in their\n// `blockSpec`, or `toInternalHTML` if `toExternalHTML` is not defined.\n//\n// The HTML created by this serializer is different to what's rendered by the\n// editor to the DOM. This also means that data is likely to be lost when\n// converting back to original blocks. The differences in the output HTML are:\n// 1. It doesn't include the `blockGroup` and `blockContainer` wrappers meaning\n// that nesting is not preserved for non-list-item blocks.\n// 2. `li` items in the output HTML are wrapped in `ul` or `ol` elements.\n// 3. While nesting for list items is preserved, other types of blocks nested\n// inside a list are un-nested and a new list is created after them.\n// 4. The HTML is wrapped in a single `div` element.\n\n// Needs to be sync because it's used in drag handler event (SideMenuPlugin)\nexport const createExternalHTMLExporter = <\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n schema: Schema,\n editor: BlockNoteEditor<BSchema, I, S>,\n) => {\n const serializer = DOMSerializer.fromSchema(schema);\n\n return {\n exportBlocks: (\n blocks: PartialBlock<BSchema, I, S>[],\n options: { document?: Document },\n ) => {\n const html = serializeBlocksExternalHTML(\n editor,\n blocks,\n serializer,\n new Set<string>([\"numberedListItem\"]),\n new Set<string>([\"bulletListItem\", \"checkListItem\", \"toggleListItem\"]),\n options,\n );\n const div = document.createElement(\"div\");\n div.append(html);\n return div.innerHTML;\n },\n\n exportInlineContent: (\n inlineContent: InlineContent<I, S>[],\n options: { document?: Document },\n ) => {\n const domFragment = serializeInlineContentExternalHTML(\n editor,\n inlineContent as any,\n serializer,\n options,\n );\n\n const parent = document.createElement(\"div\");\n parent.append(domFragment.cloneNode(true));\n\n return parent.innerHTML;\n },\n };\n};\n","import { combineTransactionSteps } from \"@tiptap/core\";\nimport deepEqual from \"fast-deep-equal\";\nimport type { Node } from \"prosemirror-model\";\nimport type { Transaction } from \"prosemirror-state\";\nimport {\n Block,\n DefaultBlockSchema,\n DefaultInlineContentSchema,\n DefaultStyleSchema,\n} from \"../blocks/defaultBlocks.js\";\nimport type { BlockSchema } from \"../schema/index.js\";\nimport type { InlineContentSchema } from \"../schema/inlineContent/types.js\";\nimport type { StyleSchema } from \"../schema/styles/types.js\";\nimport { getNodeId } from \"./getBlockInfoFromPos.js\";\nimport { nodeToBlock } from \"./nodeConversions/nodeToBlock.js\";\nimport { isNodeBlock } from \"./nodeUtil.js\";\n\n/**\n * Change detection utilities for BlockNote.\n *\n * High-level algorithm used by getBlocksChangedByTransaction:\n * 1) Merge appended transactions into one document change.\n * 2) Collect a snapshot of blocks before and after (flat map by id, and per-parent child order).\n * 3) Emit inserts and deletes by diffing ids between snapshots.\n * 4) For ids present in both snapshots:\n * - If parentId changed, emit a move\n * - Else if block changed (ignoring children), emit an update\n * 5) Finally, detect same-parent sibling reorders by comparing child order per parent.\n * We use an inlined O(n log n) LIS inside detectReorderedChildren to keep a\n * longest already-ordered subsequence and mark only the remaining items as moved.\n */\n/**\n * Gets the parent block of a node, if it has one.\n */\nfunction getParentBlockId(doc: Node, pos: number): string | undefined {\n if (pos === 0) {\n return undefined;\n }\n const resolvedPos = doc.resolve(pos);\n for (let i = resolvedPos.depth; i > 0; i--) {\n const parent = resolvedPos.node(i);\n if (isNodeBlock(parent)) {\n return getNodeId(parent, doc);\n }\n }\n return undefined;\n}\n\n/**\n * This attributes the changes to a specific source.\n */\nexport type BlockChangeSource =\n | { type: \"local\" }\n | { type: \"paste\" }\n | { type: \"drop\" }\n | { type: \"undo\" | \"redo\" | \"undo-redo\" }\n | { type: \"yjs-remote\" };\n\nexport type BlocksChanged<\n BSchema extends BlockSchema = DefaultBlockSchema,\n ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n SSchema extends StyleSchema = DefaultStyleSchema,\n> = Array<\n {\n /**\n * The affected block.\n */\n block: Block<BSchema, ISchema, SSchema>;\n /**\n * The source of the change.\n */\n source: BlockChangeSource;\n } & (\n | {\n type: \"insert\" | \"delete\";\n /**\n * Insert and delete changes don't have a previous block.\n */\n prevBlock: undefined;\n }\n | {\n type: \"update\";\n /**\n * The previous block.\n */\n prevBlock: Block<BSchema, ISchema, SSchema>;\n }\n | {\n type: \"move\";\n /**\n * The affected block.\n */\n block: Block<BSchema, ISchema, SSchema>;\n /**\n * The block before the move.\n */\n prevBlock: Block<BSchema, ISchema, SSchema>;\n /**\n * The previous parent block (if it existed).\n */\n prevParent?: Block<BSchema, ISchema, SSchema>;\n /**\n * The current parent block (if it exists).\n */\n currentParent?: Block<BSchema, ISchema, SSchema>;\n }\n )\n>;\n\nfunction determineChangeSource(transaction: Transaction): BlockChangeSource {\n if (transaction.getMeta(\"paste\")) {\n return { type: \"paste\" };\n }\n if (transaction.getMeta(\"uiEvent\") === \"drop\") {\n return { type: \"drop\" };\n }\n if (transaction.getMeta(\"history$\")) {\n return {\n type: transaction.getMeta(\"history$\").redo ? \"redo\" : \"undo\",\n };\n }\n if (transaction.getMeta(\"y-sync$\")) {\n if (transaction.getMeta(\"y-sync$\").isUndoRedoOperation) {\n return { type: \"undo-redo\" };\n }\n return { type: \"yjs-remote\" };\n }\n return { type: \"local\" };\n}\n\ntype BlockSnapshot<\n BSchema extends BlockSchema,\n ISchema extends InlineContentSchema,\n SSchema extends StyleSchema,\n> = {\n byId: Record<\n string,\n {\n block: Block<BSchema, ISchema, SSchema>;\n parentId: string | undefined;\n }\n >;\n childrenByParent: Record<string, string[]>;\n};\n\n/**\n * Collects a snapshot of blocks and per-parent child order in a single traversal.\n * Uses \"__root__\" to represent the root level where parentId is undefined.\n */\nfunction collectSnapshot<\n BSchema extends BlockSchema,\n ISchema extends InlineContentSchema,\n SSchema extends StyleSchema,\n>(doc: Node): BlockSnapshot<BSchema, ISchema, SSchema> {\n const ROOT_KEY = \"__root__\";\n const byId: Record<\n string,\n {\n block: Block<BSchema, ISchema, SSchema>;\n parentId: string | undefined;\n }\n > = {};\n const childrenByParent: Record<string, string[]> = {};\n doc.descendants((node, pos) => {\n if (!isNodeBlock(node)) {\n return true;\n }\n const parentId = getParentBlockId(doc, pos);\n const key = parentId ?? ROOT_KEY;\n if (!childrenByParent[key]) {\n childrenByParent[key] = [];\n }\n const block = nodeToBlock(node, doc);\n const nodeId = getNodeId(node, doc);\n byId[nodeId] = { block, parentId };\n childrenByParent[key].push(nodeId);\n return true;\n });\n return { byId, childrenByParent };\n}\n\n/**\n * Determines which child ids have been reordered (moved) within the same parent.\n * Uses LIS to keep the longest ordered subsequence and marks the rest as moved.\n */\nfunction detectReorderedChildren(\n prevOrder: string[] | undefined,\n nextOrder: string[] | undefined,\n): Set<string> {\n const moved = new Set<string>();\n if (!prevOrder || !nextOrder) {\n return moved;\n }\n // Consider only ids present in both orders (ignore inserts/deletes handled elsewhere)\n const prevIds = new Set(prevOrder);\n const commonNext: string[] = nextOrder.filter((id) => prevIds.has(id));\n const commonPrev: string[] = prevOrder.filter((id) =>\n commonNext.includes(id),\n );\n\n if (commonPrev.length <= 1 || commonNext.length <= 1) {\n return moved;\n }\n\n // Map ids to their index in previous order\n const indexInPrev: Record<string, number> = {};\n for (let i = 0; i < commonPrev.length; i++) {\n indexInPrev[commonPrev[i]] = i;\n }\n\n // Build sequence of indices representing next order in terms of previous indices\n const sequence: number[] = commonNext.map((id) => indexInPrev[id]);\n\n // Inline O(n log n) LIS with reconstruction.\n // Why LIS? We want the smallest set of siblings to label as \"moved\".\n // Keeping the longest subsequence that is already in order achieves this,\n // so only items outside the LIS are reported as moves.\n const n = sequence.length;\n const tailsValues: number[] = [];\n const tailsEndsAtIndex: number[] = [];\n const previousIndexInLis: number[] = new Array(n).fill(-1);\n\n const lowerBound = (arr: number[], target: number): number => {\n let lo = 0;\n let hi = arr.length;\n while (lo < hi) {\n const mid = (lo + hi) >>> 1;\n if (arr[mid] < target) {\n lo = mid + 1;\n } else {\n hi = mid;\n }\n }\n return lo;\n };\n\n for (let i = 0; i < n; i++) {\n const value = sequence[i];\n const pos = lowerBound(tailsValues, value);\n if (pos > 0) {\n previousIndexInLis[i] = tailsEndsAtIndex[pos - 1];\n }\n if (pos === tailsValues.length) {\n tailsValues.push(value);\n tailsEndsAtIndex.push(i);\n } else {\n tailsValues[pos] = value;\n tailsEndsAtIndex[pos] = i;\n }\n }\n\n const lisIndexSet = new Set<number>();\n let k = tailsEndsAtIndex[tailsEndsAtIndex.length - 1] ?? -1;\n while (k !== -1) {\n lisIndexSet.add(k);\n k = previousIndexInLis[k];\n }\n\n // Items not part of LIS are considered moved\n for (let i = 0; i < commonNext.length; i++) {\n if (!lisIndexSet.has(i)) {\n moved.add(commonNext[i]);\n }\n }\n return moved;\n}\n\n/**\n * Get the blocks that were changed by a transaction.\n */\nexport function getBlocksChangedByTransaction<\n BSchema extends BlockSchema = DefaultBlockSchema,\n ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n SSchema extends StyleSchema = DefaultStyleSchema,\n>(\n transaction: Transaction,\n appendedTransactions: Transaction[] = [],\n): BlocksChanged<BSchema, ISchema, SSchema> {\n const source = determineChangeSource(transaction);\n const combinedTransaction = combineTransactionSteps(transaction.before, [\n transaction,\n ...appendedTransactions,\n ]);\n\n const prevSnap = collectSnapshot<BSchema, ISchema, SSchema>(\n combinedTransaction.before,\n );\n const nextSnap = collectSnapshot<BSchema, ISchema, SSchema>(\n combinedTransaction.doc,\n );\n\n const changes: BlocksChanged<BSchema, ISchema, SSchema> = [];\n const changedIds = new Set<string>();\n\n // Handle inserted blocks\n Object.keys(nextSnap.byId)\n .filter((id) => !(id in prevSnap.byId))\n .forEach((id) => {\n changes.push({\n type: \"insert\",\n block: nextSnap.byId[id].block,\n source,\n prevBlock: undefined,\n });\n changedIds.add(id);\n });\n\n // Handle deleted blocks\n Object.keys(prevSnap.byId)\n .filter((id) => !(id in nextSnap.byId))\n .forEach((id) => {\n changes.push({\n type: \"delete\",\n block: prevSnap.byId[id].block,\n source,\n prevBlock: undefined,\n });\n changedIds.add(id);\n });\n\n // Handle updated, moved to different parent, indented, outdented blocks\n Object.keys(nextSnap.byId)\n .filter((id) => id in prevSnap.byId)\n .forEach((id) => {\n const prev = prevSnap.byId[id];\n const next = nextSnap.byId[id];\n const isParentDifferent = prev.parentId !== next.parentId;\n\n if (isParentDifferent) {\n changes.push({\n type: \"move\",\n block: next.block,\n prevBlock: prev.block,\n source,\n prevParent: prev.parentId\n ? prevSnap.byId[prev.parentId]?.block\n : undefined,\n currentParent: next.parentId\n ? nextSnap.byId[next.parentId]?.block\n : undefined,\n });\n changedIds.add(id);\n } else if (\n // Compare blocks while ignoring children to avoid reporting a parent\n // update when only descendants changed.\n !deepEqual(\n { ...prev.block, children: undefined } as any,\n { ...next.block, children: undefined } as any,\n )\n ) {\n changes.push({\n type: \"update\",\n block: next.block,\n prevBlock: prev.block,\n source,\n });\n changedIds.add(id);\n }\n });\n\n // Handle sibling reorders (parent unchanged but relative order changed)\n const prevOrderByParent = prevSnap.childrenByParent;\n const nextOrderByParent = nextSnap.childrenByParent;\n\n // Use a special key for root-level siblings\n const ROOT_KEY = \"__root__\";\n const parents = new Set<string>([\n ...Object.keys(prevOrderByParent),\n ...Object.keys(nextOrderByParent),\n ]);\n\n const addedMoveForId = new Set<string>();\n\n parents.forEach((parentKey) => {\n const movedWithinParent = detectReorderedChildren(\n prevOrderByParent[parentKey],\n nextOrderByParent[parentKey],\n );\n if (movedWithinParent.size === 0) {\n return;\n }\n movedWithinParent.forEach((id) => {\n // Only consider ids that exist in both snapshots and whose parent truly did not change\n const prev = prevSnap.byId[id];\n const next = nextSnap.byId[id];\n if (!prev || !next) {\n return;\n }\n if (prev.parentId !== next.parentId) {\n return;\n }\n // Skip if already accounted for by insert/delete/update/parent move\n if (changedIds.has(id)) {\n return;\n }\n // Verify we're addressing the right parent bucket\n const bucketKey = prev.parentId ?? ROOT_KEY;\n if (bucketKey !== parentKey) {\n return;\n }\n if (addedMoveForId.has(id)) {\n return;\n }\n addedMoveForId.add(id);\n changes.push({\n type: \"move\",\n block: next.block,\n prevBlock: prev.block,\n source,\n prevParent: prev.parentId\n ? prevSnap.byId[prev.parentId]?.block\n : undefined,\n currentParent: next.parentId\n ? nextSnap.byId[next.parentId]?.block\n : undefined,\n });\n changedIds.add(id);\n });\n });\n\n return changes;\n}\n","import { Plugin, PluginKey, Transaction } from \"prosemirror-state\";\nimport {\n BlocksChanged,\n getBlocksChangedByTransaction,\n} from \"../../api/getBlocksChangedByTransaction.js\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\n/**\n * This plugin can filter transactions before they are applied to the editor, but with a higher-level API than `filterTransaction` from prosemirror.\n */\nexport const BlockChangeExtension = createExtension(() => {\n const beforeChangeCallbacks: ((context: {\n getChanges: () => BlocksChanged<any, any, any>;\n tr: Transaction;\n }) => boolean | void)[] = [];\n return {\n key: \"blockChange\",\n prosemirrorPlugins: [\n new Plugin({\n key: new PluginKey(\"blockChange\"),\n filterTransaction: (tr) => {\n let changes:\n | ReturnType<typeof getBlocksChangedByTransaction<any, any, any>>\n | undefined = undefined;\n\n return beforeChangeCallbacks.reduce((acc, cb) => {\n if (acc === false) {\n // We only care that we hit a `false` result, so we can stop iterating.\n return acc;\n }\n return (\n cb({\n getChanges() {\n if (changes) {\n return changes;\n }\n changes = getBlocksChangedByTransaction<any, any, any>(tr);\n return changes;\n },\n tr,\n }) !== false\n );\n }, true);\n },\n }),\n ],\n\n /**\n * Subscribe to the block change events.\n */\n subscribe(\n callback: (context: {\n getChanges: () => BlocksChanged<any, any, any>;\n tr: Transaction;\n }) => boolean | void,\n ) {\n beforeChangeCallbacks.push(callback);\n\n return () => {\n beforeChangeCallbacks.splice(\n beforeChangeCallbacks.indexOf(callback),\n 1,\n );\n };\n },\n } as const;\n});\n","import type { EditorView } from \"prosemirror-view\";\n\n/**\n * The orientation of the drop cursor.\n */\nexport type DropCursorOrientation =\n | \"inline\" // Vertical line within text\n | \"block-horizontal\" // Horizontal line between blocks\n | \"block-vertical-left\" // Vertical line on left edge of block\n | \"block-vertical-right\"; // Vertical line on right edge of block\n\n/**\n * The position and orientation of the drop cursor.\n */\nexport type DropCursorPosition = {\n pos: number; // Document position\n orientation: DropCursorOrientation;\n};\n/**\n * Bounding rectangle in viewport coordinates (e.g. from getBoundingClientRect).\n */\nexport type Rect = {\n left: number;\n right: number;\n top: number;\n bottom: number;\n};\n\n/**\n * Returns true if the element or any ancestor has the given CSS class.\n * Used to skip drop cursor for elements marked with the exclusion class (e.g. drag handles).\n */\nexport function hasExclusionClassname(\n element: Element | null,\n exclude: string,\n): boolean {\n if (!element || !exclude) {\n return false;\n }\n return !!element.closest(`.${exclude}`);\n}\n\n/**\n * Computes the viewport rect for a block-level drop cursor (horizontal line between blocks\n * or vertical line on left/right edge). Returns null for inline positions or when no DOM node exists.\n */\nexport function getBlockDropRect(\n view: EditorView,\n cursorPos: DropCursorPosition,\n width: number,\n scaleX: number,\n scaleY: number,\n): Rect | null {\n const $pos = view.state.doc.resolve(cursorPos.pos);\n const isBlock = !$pos.parent.inlineContent;\n\n if (!isBlock || cursorPos.orientation === \"inline\") {\n return null;\n }\n\n const before = $pos.nodeBefore;\n\n const after = $pos.nodeAfter;\n\n if (!before && !after) {\n return null;\n }\n\n const isVertical =\n cursorPos.orientation === \"block-vertical-left\" ||\n cursorPos.orientation === \"block-vertical-right\";\n // For vertical cursors, position is at the node position, for horizontal cursors, position is at the node before position\n const nodePos = isVertical\n ? cursorPos.pos\n : cursorPos.pos - (before ? before.nodeSize : 0);\n\n const node = view.nodeDOM(nodePos) as HTMLElement | null;\n if (!node) {\n return null;\n }\n\n const nodeRect = node.getBoundingClientRect();\n\n if (isVertical) {\n const halfWidth = (width / 2) * scaleX;\n const left =\n cursorPos.orientation === \"block-vertical-left\"\n ? nodeRect.left\n : nodeRect.right;\n\n return {\n left: left - halfWidth,\n right: left + halfWidth,\n top: nodeRect.top,\n bottom: nodeRect.bottom,\n };\n }\n\n let top = before ? nodeRect.bottom : nodeRect.top;\n if (before && after) {\n top =\n (top +\n (view.nodeDOM(cursorPos.pos) as HTMLElement).getBoundingClientRect()\n .top) /\n 2;\n }\n const halfHeight = (width / 2) * scaleY;\n\n return {\n left: nodeRect.left,\n right: nodeRect.right,\n top: top - halfHeight,\n bottom: top + halfHeight,\n };\n}\n\n/**\n * Computes the viewport rect for an inline drop cursor (vertical line within text).\n */\nexport function getInlineDropRect(\n view: EditorView,\n cursorPos: DropCursorPosition,\n width: number,\n scaleX: number,\n): Rect {\n const coords = view.coordsAtPos(cursorPos.pos);\n const halfWidth = (width / 2) * scaleX;\n\n return {\n left: coords.left - halfWidth,\n right: coords.left + halfWidth,\n top: coords.top,\n bottom: coords.bottom,\n };\n}\n\n/**\n * Applies orientation-specific CSS classes to the drop cursor element so it can be\n * styled correctly (e.g. horizontal vs vertical line, inline vs block).\n */\nexport function applyOrientationClasses(\n el: HTMLElement,\n orientation: DropCursorOrientation,\n) {\n el.classList.toggle(\n \"prosemirror-dropcursor-inline\",\n orientation === \"inline\",\n );\n el.classList.toggle(\n \"prosemirror-dropcursor-block-horizontal\",\n orientation === \"block-horizontal\",\n );\n el.classList.toggle(\n \"prosemirror-dropcursor-block-vertical-left\",\n orientation === \"block-vertical-left\",\n );\n el.classList.toggle(\n \"prosemirror-dropcursor-block-vertical-right\",\n orientation === \"block-vertical-right\",\n );\n el.classList.toggle(\n \"prosemirror-dropcursor-block\",\n orientation === \"block-horizontal\",\n );\n el.classList.toggle(\n \"prosemirror-dropcursor-vertical\",\n orientation === \"block-vertical-left\" ||\n orientation === \"block-vertical-right\",\n );\n}\n\n/**\n * Returns the offset of the parent element for converting viewport coordinates to\n * parent-relative coordinates. Handles document.body and static positioning.\n */\nexport function getParentOffsets(parent: HTMLElement | null) {\n if (\n !parent ||\n (parent === document.body && getComputedStyle(parent).position === \"static\")\n ) {\n return {\n parentLeft: -window.pageXOffset,\n parentTop: -window.pageYOffset,\n };\n }\n\n const parentRect = parent.getBoundingClientRect();\n const parentScaleX = parentRect.width / parent.offsetWidth;\n const parentScaleY = parentRect.height / parent.offsetHeight;\n\n return {\n parentLeft: parentRect.left - parent.scrollLeft * parentScaleX,\n parentTop: parentRect.top - parent.scrollTop * parentScaleY,\n };\n}\n","import { dropPoint } from \"prosemirror-transform\";\nimport type { EditorView } from \"prosemirror-view\";\nimport {\n applyOrientationClasses,\n getBlockDropRect,\n getInlineDropRect,\n getParentOffsets,\n hasExclusionClassname,\n type DropCursorPosition,\n} from \"./utils.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const DRAG_EXCLUSION_CLASSNAME = \"bn-drag-exclude\";\n\n/**\n * Context passed to the computeDropPosition hook.\n */\nexport interface ComputeDropPositionContext {\n editor: BlockNoteEditor<any, any, any>;\n event: DragEvent;\n view: EditorView;\n defaultPosition: DropCursorPosition | null;\n}\n\n/**\n * Hooks for customizing drop cursor behavior.\n */\nexport interface DropCursorHooks {\n /**\n * Compute cursor position and orientation.\n * Return null to prevent dropping (no cursor shown).\n */\n computeDropPosition?: (\n context: ComputeDropPositionContext,\n ) => DropCursorPosition | null;\n}\n\n/**\n * Options for the DropCursor extension.\n */\nexport interface DropCursorOptions {\n width?: number; // Cursor width in pixels (default: 5)\n color?: string | false; // Cursor color (default: \"#ddeeff\")\n exclude?: string; // CSS class for exclusion (default: \"bn-drag-exclude\")\n hooks?: DropCursorHooks; // Optional behavior hooks\n}\n\n/**\n * Drop cursor visualization based on prosemirror-dropcursor:\n * https://github.com/ProseMirror/prosemirror-dropcursor/blob/master/src/dropcursor.ts\n *\n * Refactored to use BlockNote extension pattern with mount callback and AbortSignal\n * for lifecycle management instead of ProseMirror PluginView.\n */\nexport const DropCursorExtension = createExtension<\n any,\n {\n dropCursor?: DropCursorOptions;\n }\n>(({ editor, options }) => {\n // State\n let cursorPos: DropCursorPosition | null = null;\n let element: HTMLElement | null = null;\n let timeout = -1;\n let dragSourceElement: Element | null = null;\n\n const config = {\n width: options.dropCursor?.width ?? 5,\n color: options.dropCursor?.color ?? \"#ddeeff\",\n exclude: options.dropCursor?.exclude ?? DRAG_EXCLUSION_CLASSNAME,\n hooks: options.dropCursor?.hooks,\n } as const;\n\n // Helper functions\n const setCursor = (pos: DropCursorPosition | null) => {\n if (\n pos?.pos === cursorPos?.pos &&\n pos?.orientation === cursorPos?.orientation\n ) {\n return;\n }\n cursorPos = pos;\n\n if (pos == null) {\n if (element && element.parentNode) {\n element.parentNode.removeChild(element);\n }\n element = null;\n } else {\n updateOverlay();\n }\n };\n\n const updateOverlay = () => {\n if (!cursorPos) {\n return;\n }\n\n const view = editor.prosemirrorView;\n const editorDOM = view.dom;\n const editorRect = editorDOM.getBoundingClientRect();\n const scaleX = editorRect.width / editorDOM.offsetWidth;\n const scaleY = editorRect.height / editorDOM.offsetHeight;\n\n const blockRect = getBlockDropRect(\n view,\n cursorPos,\n config.width,\n scaleX,\n scaleY,\n );\n const rect =\n blockRect ?? getInlineDropRect(view, cursorPos, config.width, scaleX);\n\n const parent = view.dom.offsetParent as HTMLElement;\n if (!element) {\n element = parent.appendChild(document.createElement(\"div\"));\n element.style.cssText =\n \"position: absolute; z-index: 50; pointer-events: none;\";\n if (config.color) {\n element.style.backgroundColor = config.color;\n }\n }\n\n applyOrientationClasses(element, cursorPos.orientation);\n\n const { parentLeft, parentTop } = getParentOffsets(parent);\n\n element.style.left = (rect.left - parentLeft) / scaleX + \"px\";\n element.style.top = (rect.top - parentTop) / scaleY + \"px\";\n element.style.width = (rect.right - rect.left) / scaleX + \"px\";\n element.style.height = (rect.bottom - rect.top) / scaleY + \"px\";\n };\n\n const scheduleRemoval = (ms: number) => {\n clearTimeout(timeout);\n timeout = window.setTimeout(() => setCursor(null), ms);\n };\n\n // Event handlers\n const onDragStart = (event: Event) => {\n const e = event as DragEvent;\n dragSourceElement = e.target instanceof Element ? e.target : null;\n };\n\n const onDragOver = (event: Event) => {\n const e = event as DragEvent;\n\n // Check if drag source has exclusion classname\n if (\n dragSourceElement &&\n hasExclusionClassname(dragSourceElement, config.exclude)\n ) {\n return;\n }\n\n // Check if drop target has exclusion classname\n if (\n e.target instanceof Element &&\n hasExclusionClassname(e.target, config.exclude)\n ) {\n return;\n }\n\n const view = editor.prosemirrorView;\n if (!view.editable) {\n return;\n }\n\n const pos = view.posAtCoords({\n left: e.clientX,\n top: e.clientY,\n });\n\n const node = pos && pos.inside >= 0 && view.state.doc.nodeAt(pos.inside);\n const disableDropCursor = node && (node.type.spec as any).disableDropCursor;\n const disabled =\n typeof disableDropCursor === \"function\"\n ? disableDropCursor(view, pos, e)\n : disableDropCursor;\n\n if (pos && !disabled) {\n let target = pos.pos;\n if (view.dragging && view.dragging.slice) {\n const point = dropPoint(view.state.doc, target, view.dragging.slice);\n if (point != null) {\n target = point;\n }\n }\n\n // Compute default position\n const $pos = view.state.doc.resolve(target);\n const isBlock = !$pos.parent.inlineContent;\n const defaultPosition: DropCursorPosition = {\n pos: target,\n orientation: isBlock ? \"block-horizontal\" : \"inline\",\n };\n\n // Allow hook to override position\n let finalPosition = defaultPosition;\n if (config.hooks?.computeDropPosition) {\n const hookResult = config.hooks.computeDropPosition({\n editor,\n event: e,\n view,\n defaultPosition,\n });\n if (hookResult === null) {\n // Hook returned null - don't show cursor\n setCursor(null);\n return;\n }\n finalPosition = hookResult;\n }\n\n setCursor(finalPosition);\n scheduleRemoval(5000);\n }\n };\n\n const onDragLeave = (event: Event) => {\n const e = event as DragEvent;\n if (\n !(e.relatedTarget instanceof Node) ||\n !editor.prosemirrorView.dom.contains(e.relatedTarget)\n ) {\n setCursor(null);\n }\n };\n\n const onDrop = () => {\n scheduleRemoval(20);\n };\n\n const onDragEnd = () => {\n scheduleRemoval(20);\n dragSourceElement = null;\n };\n\n return {\n key: \"dropCursor\",\n mount({ signal, dom, root }) {\n // Track drag source at document level\n root.addEventListener(\"dragstart\", onDragStart, {\n capture: true,\n signal,\n });\n\n // Handle drag events on the editor\n dom.addEventListener(\"dragover\", onDragOver, { signal });\n dom.addEventListener(\"dragleave\", onDragLeave, { signal });\n dom.addEventListener(\"drop\", onDrop, { signal });\n dom.addEventListener(\"dragend\", onDragEnd, { signal });\n\n // Clean up on unmount\n signal.addEventListener(\"abort\", () => {\n clearTimeout(timeout);\n setCursor(null);\n });\n },\n } as const;\n});\n","import { history, redo, undo } from \"@tiptap/pm/history\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const HistoryExtension = createExtension(() => {\n return {\n key: \"history\",\n prosemirrorPlugins: [history()],\n undoCommand: undo,\n redoCommand: redo,\n } as const;\n});\n","import { Plugin, PluginKey, Selection, TextSelection } from \"prosemirror-state\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nconst PLUGIN_KEY = new PluginKey(\"inline-content-boundary-edit\");\n\n// Whether a Backspace/Delete at `selection` would remove the entire content\n// range `[content.from, content.to)` of an inline content node.\nfunction emptiesInlineContent(\n selection: Selection,\n key: string,\n content: { from: number; to: number },\n) {\n if (!selection.empty) {\n return selection.from <= content.from && selection.to >= content.to;\n }\n\n const isSingleChar = content.to - content.from === 1;\n\n return key === \"Backspace\"\n ? isSingleChar && selection.from === content.to\n : isSingleChar && selection.from === content.from;\n}\n\n// Fixes editing at the boundary of an empty custom inline content node (i.e. an\n// inline node with editable content, like a mention or inline math).\n//\n// An empty inline node can't hold a text cursor, so ProseMirror can't reconcile\n// edits across the empty boundary from the DOM: typing into an empty node\n// inserts text next to it rather than inside, and deleting the last character\n// leaves an un-reconcilable empty node that corrupts/freezes the editor. Both\n// boundary edits are handled here via transactions so the caret stays inside\n// the node, which is kept alive and editable in its empty state.\n//\n// The cursor is inside such a node exactly when its directly-enclosing node is\n// inline (`inline: true` in the spec) - regular text blocks aren't inline, and\n// atomic inline content can't hold a cursor - so the handling applies to any\n// inline content type without needing to know it by name.\nexport const InlineContentBoundaryEditExtension = createExtension(\n () =>\n ({\n key: \"inlineContentBoundaryEdit\",\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n props: {\n handleKeyDown: (view, event) => {\n if (!view.editable) {\n return false;\n }\n\n const isTypedChar =\n event.key.length === 1 && !event.ctrlKey && !event.metaKey;\n\n if (\n !isTypedChar &&\n event.key !== \"Backspace\" &&\n event.key !== \"Delete\"\n ) {\n return false;\n }\n\n const { selection } = view.state;\n const node = selection.$from.node();\n if (!node.type.spec.inline) {\n return false;\n }\n\n const pos = selection.$from.before();\n const contentFrom = pos + 1;\n const contentTo = pos + 1 + node.content.size;\n\n // Empty content: redirect the typed character into the node.\n if (isTypedChar && node.content.size === 0) {\n const tr = view.state.tr.insert(\n contentFrom,\n view.state.schema.text(event.key),\n );\n tr.setSelection(\n TextSelection.create(tr.doc, contentFrom + event.key.length),\n );\n view.dispatch(tr);\n\n return true;\n }\n\n // Backspace/Delete that would empty the content: delete it all in\n // one transaction, keeping the now-empty node (and the caret\n // inside it) so it stays editable.\n if (\n node.content.size > 0 &&\n emptiesInlineContent(selection, event.key, {\n from: contentFrom,\n to: contentTo,\n })\n ) {\n const tr = view.state.tr.delete(contentFrom, contentTo);\n tr.setSelection(TextSelection.create(tr.doc, contentFrom));\n view.dispatch(tr);\n\n return true;\n }\n\n return false;\n },\n },\n }),\n ],\n }) as const,\n);\n","import { posToDOMRect } from \"@tiptap/core\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const LinkToolbarExtension = createExtension(({ editor }) => {\n function getLinkElementAtPos(pos: number) {\n let currentNode = editor.prosemirrorView.nodeDOM(pos);\n while (currentNode && currentNode.parentElement) {\n if (currentNode.nodeName === \"A\") {\n return currentNode as HTMLAnchorElement;\n }\n currentNode = currentNode.parentElement;\n }\n return null;\n }\n\n function getLinkAtPos(pos: number) {\n const linkData = editor.getLinkMarkAtPos(pos);\n if (!linkData) {\n return undefined;\n }\n\n return {\n range: { from: linkData.from, to: linkData.to },\n // Expose mark-like attrs for backward compat with React LinkToolbarController\n mark: { attrs: { href: linkData.href } },\n get text() {\n return linkData.text;\n },\n get position() {\n return posToDOMRect(\n editor.prosemirrorView,\n linkData.from,\n linkData.to,\n ).toJSON() as DOMRect;\n },\n };\n }\n\n function getLinkAtSelection() {\n return editor.transact((tr) => {\n if (!tr.selection.empty) {\n return undefined;\n }\n return getLinkAtPos(tr.selection.anchor);\n });\n }\n\n return {\n key: \"linkToolbar\",\n\n getLinkAtSelection,\n getLinkElementAtPos,\n getMarkAtPos(pos: number, _markType: string) {\n return getLinkAtPos(pos);\n },\n\n getLinkAtElement(element: HTMLElement) {\n return editor.transact(() => {\n const posAtElement = editor.prosemirrorView.posAtDOM(element, 0) + 1;\n return getLinkAtPos(posAtElement);\n });\n },\n\n editLink(\n url: string,\n text: string,\n position = editor.transact((tr) => tr.selection.anchor),\n ) {\n editor.editLink(url, text, position);\n },\n\n deleteLink(position = editor.transact((tr) => tr.selection.anchor)) {\n editor.deleteLink(position);\n },\n } as const;\n});\n","export const VALID_LINK_PROTOCOLS = [\n \"http\",\n \"https\",\n \"ftp\",\n \"ftps\",\n \"mailto\",\n \"tel\",\n \"callto\",\n \"sms\",\n \"cid\",\n \"xmpp\",\n];\nexport const DEFAULT_LINK_PROTOCOL = \"https\";\n","import { Plugin, PluginKey, TextSelection } from \"prosemirror-state\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nconst PLUGIN_KEY = new PluginKey(\"node-selection-keyboard\");\n// By default, typing with a node selection active will cause ProseMirror to\n// replace the node with one that contains editable content. This plugin blocks\n// this behaviour without also blocking things like keyboard shortcuts:\n//\n// - Lets through key presses that do not include alphanumeric characters. This\n// includes things like backspace/delete/home/end/etc.\n// - Lets through any key presses that include ctrl/meta keys. These will be\n// shortcuts of some kind like ctrl+C/mod+C.\n// - Special case for Enter key which creates a new paragraph block below and\n// sets the selection to it. This is just to bring the UX closer to Notion\n//\n// While a more elegant solution would probably process transactions instead of\n// keystrokes, this brings us most of the way to Notion's UX without much added\n// complexity.\nexport const NodeSelectionKeyboardExtension = createExtension(\n () =>\n ({\n key: \"nodeSelectionKeyboard\",\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n props: {\n handleKeyDown: (view, event) => {\n // Checks for node selection\n if (\"node\" in view.state.selection) {\n // Checks if key press uses ctrl/meta modifier\n if (event.ctrlKey || event.metaKey) {\n return false;\n }\n // Checks if key press is alphanumeric\n if (event.key.length === 1) {\n event.preventDefault();\n\n return true;\n }\n // Checks if key press is Enter\n if (\n event.key === \"Enter\" &&\n !event.isComposing &&\n !event.shiftKey &&\n !event.altKey &&\n !event.ctrlKey &&\n !event.metaKey\n ) {\n const tr = view.state.tr;\n view.dispatch(\n tr\n .insert(\n view.state.tr.selection.$to.after(),\n view.state.schema.nodes[\"paragraph\"].createChecked(),\n )\n .setSelection(\n new TextSelection(\n tr.doc.resolve(\n view.state.tr.selection.$to.after() + 1,\n ),\n ),\n ),\n );\n\n return true;\n }\n }\n\n return false;\n },\n },\n }),\n ],\n }) as const,\n);\n","import { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { uuidv4 } from \"lib0/random\";\n\nimport {\n createExtension,\n ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { BlockNoteEditorOptions } from \"../../editor/BlockNoteEditor.js\";\n\nconst PLUGIN_KEY = new PluginKey(`blocknote-placeholder`);\n\nexport const PlaceholderExtension = createExtension(\n ({\n editor,\n options,\n }: ExtensionOptions<\n Pick<BlockNoteEditorOptions<any, any, any>, \"placeholders\">\n >) => {\n const placeholders = options.placeholders;\n return {\n key: \"placeholder\",\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n view: (view) => {\n const uniqueEditorSelector = `placeholder-selector-${uuidv4()}`;\n view.dom.classList.add(uniqueEditorSelector);\n const styleEl = document.createElement(\"style\");\n\n const nonce = editor._tiptapEditor.options.injectNonce;\n if (nonce) {\n styleEl.setAttribute(\"nonce\", nonce);\n }\n\n if (view.root instanceof window.ShadowRoot) {\n view.root.append(styleEl);\n } else {\n view.root.head.appendChild(styleEl);\n }\n\n const styleSheet = styleEl.sheet!;\n\n const getSelector = (additionalSelectors = \"\") =>\n `.${uniqueEditorSelector} .bn-block-content${additionalSelectors}:has(.ProseMirror-trailingBreak:only-child):after`;\n\n try {\n // FIXME: the names \"default\" and \"emptyDocument\" are hardcoded\n const {\n default: defaultPlaceholder,\n emptyDocument: emptyPlaceholder,\n ...rest\n } = placeholders || {};\n\n // add block specific placeholders\n for (const [blockType, placeholder] of Object.entries(rest)) {\n const blockTypeSelector = `[data-content-type=\"${blockType}\"]`;\n\n styleSheet.insertRule(\n `${getSelector(blockTypeSelector)} { content: ${JSON.stringify(\n placeholder,\n )}; }`,\n );\n }\n\n const onlyBlockSelector = `[data-is-only-empty-block]`;\n const mustBeFocusedSelector = `[data-is-empty-and-focused]`;\n\n // placeholder for when there's only one empty block\n styleSheet.insertRule(\n `${getSelector(onlyBlockSelector)} { content: ${JSON.stringify(\n emptyPlaceholder,\n )}; }`,\n );\n\n // placeholder for default blocks, only when the cursor is in the block (mustBeFocused)\n styleSheet.insertRule(\n `${getSelector(mustBeFocusedSelector)} { content: ${JSON.stringify(\n defaultPlaceholder,\n )}; }`,\n );\n } catch (e) {\n // eslint-disable-next-line no-console\n console.warn(\n `Failed to insert placeholder CSS rule - this is likely due to the browser not supporting certain CSS pseudo-element selectors (:has, :only-child:, or :before)`,\n e,\n );\n }\n\n return {\n destroy: () => {\n if (view.root instanceof window.ShadowRoot) {\n view.root.removeChild(styleEl);\n } else {\n view.root.head.removeChild(styleEl);\n }\n },\n };\n },\n props: {\n decorations: (state) => {\n const { doc, selection } = state;\n\n if (!editor.isEditable) {\n return;\n }\n\n if (!selection.empty) {\n return;\n }\n\n // Don't show placeholder when the cursor is inside a code block\n if (selection.$from.parent.type.spec.code) {\n return;\n }\n\n const decs = [];\n\n // decoration for when there's only one empty block\n // positions are hardcoded for now\n if (state.doc.content.size === 6) {\n decs.push(\n Decoration.node(2, 4, {\n \"data-is-only-empty-block\": \"true\",\n }),\n );\n }\n\n const $pos = selection.$anchor;\n const node = $pos.parent;\n\n if (node.content.size === 0) {\n const before = $pos.before();\n\n decs.push(\n Decoration.node(before, before + node.nodeSize, {\n \"data-is-empty-and-focused\": \"true\",\n }),\n );\n }\n\n return DecorationSet.create(doc, decs);\n },\n },\n }),\n ],\n } as const;\n },\n);\n","import { Mapping } from \"prosemirror-transform\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const PositionMappingExtension = createExtension(({ editor }) => {\n /**\n * The mapping object which holds the position mapping across changes.\n */\n let mapping = new Mapping();\n /**\n * The number of live `mapPosition` closures.\n */\n let numInstances = 0;\n\n function reset() {\n mapping = new Mapping();\n numInstances = 0;\n }\n\n // FinalizationRegistry is kept as a non-deterministic fallback for\n // individual closure cleanup during the editor's lifetime.\n const registry =\n typeof FinalizationRegistry !== \"undefined\"\n ? new FinalizationRegistry(() => {\n numInstances--;\n if (numInstances === 0) {\n reset();\n }\n })\n : null;\n\n editor.on(\"create\", () => {\n editor._tiptapEditor.on(\"transaction\", ({ transaction }) => {\n if (numInstances === 0) {\n return;\n }\n mapping.appendMapping(transaction.mapping);\n });\n\n // Deterministic cleanup: when the editor is destroyed, reset state so\n // mapping.maps does not grow unbounded across editor lifecycles.\n editor._tiptapEditor.on(\"destroy\", () => {\n reset();\n });\n });\n\n return {\n key: \"positionMapping\",\n mapPosition: (position: number, side: \"left\" | \"right\" = \"left\") => {\n numInstances++;\n const trackedMapLength = mapping.maps.length;\n\n const getMappedPosition = () => {\n return (\n mapping\n // Only read the history of the mapping that we care about\n .slice(trackedMapLength)\n .map(position, side === \"left\" ? -1 : 1)\n );\n };\n\n if (registry) {\n registry.register(getMappedPosition, undefined);\n }\n\n return getMappedPosition;\n },\n } as const;\n});\n","import { findChildrenInRange } from \"@tiptap/core\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport { getNodeId } from \"../../api/getBlockInfoFromPos.js\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nconst PLUGIN_KEY = new PluginKey(`previous-blocks`);\n\nconst nodeAttributes: Record<string, string> = {\n // Numbered List Items\n index: \"index\",\n // Headings\n level: \"level\",\n // All Blocks\n type: \"type\",\n depth: \"depth\",\n \"depth-change\": \"depth-change\",\n};\n\n/**\n * This plugin tracks transformation of Block node attributes, so we can support CSS transitions.\n *\n * Problem it solves: ProseMirror recreates the DOM when transactions happen. So when a transaction changes a Node attribute,\n * it results in a completely new DOM element. This means CSS transitions don't work.\n *\n * Solution: When attributes change on a node, this plugin sets a data-* attribute with the \"previous\" value. This way we can still use CSS transitions. (See block.module.css)\n */\nexport const PreviousBlockTypeExtension = createExtension(() => {\n let timeout: ReturnType<typeof setTimeout>;\n return {\n key: \"previousBlockType\",\n prosemirrorPlugins: [\n new Plugin({\n key: PLUGIN_KEY,\n view(_editorView) {\n return {\n update: async (view, _prevState) => {\n if (this.key?.getState(view.state).updatedBlocks.size > 0) {\n // use setTimeout 0 to clear the decorations so that at least\n // for one DOM-render the decorations have been applied\n timeout = setTimeout(() => {\n view.dispatch(\n view.state.tr.setMeta(PLUGIN_KEY, { clearUpdate: true }),\n );\n }, 0);\n }\n },\n destroy: () => {\n if (timeout) {\n clearTimeout(timeout);\n }\n },\n };\n },\n state: {\n init() {\n return {\n // Block attributes, by block ID, from just before the previous transaction.\n prevTransactionOldBlockAttrs: {} as any,\n // Block attributes, by block ID, from just before the current transaction.\n currentTransactionOldBlockAttrs: {} as any,\n // Set of IDs of blocks whose attributes changed from the current transaction.\n updatedBlocks: new Set<string>(),\n };\n },\n\n apply(transaction, prev, oldState, newState) {\n prev.currentTransactionOldBlockAttrs = {};\n prev.updatedBlocks.clear();\n\n if (!transaction.docChanged) {\n return prev;\n }\n\n // Only check nodes affected by the transaction, not the entire document.\n // changedRange() is O(steps) unlike tiptap's getChangedRanges which is O(steps²).\n const newRange = transaction.changedRange();\n if (!newRange) {\n return prev;\n }\n\n // Map the new-doc range back to old-doc coordinates\n const invertedMapping = transaction.mapping.invert();\n const oldRange = {\n from: invertedMapping.map(newRange.from, -1),\n to: invertedMapping.map(newRange.to, 1),\n };\n\n const currentTransactionOriginalOldBlockAttrs = {} as any;\n\n const oldNodes = findChildrenInRange(\n oldState.doc,\n oldRange,\n (node) => node.attrs.id,\n );\n const oldNodesById = new Map(\n oldNodes.map((node) => [\n getNodeId(node.node, oldState.doc),\n node,\n ]),\n );\n const newNodes = findChildrenInRange(\n newState.doc,\n newRange,\n (node) => node.attrs.id,\n );\n\n for (const node of newNodes) {\n const nodeId = getNodeId(node.node, newState.doc);\n const oldNode = oldNodesById.get(nodeId);\n\n const oldContentNode = oldNode?.node.firstChild;\n const newContentNode = node.node.firstChild;\n\n if (oldNode && oldContentNode && newContentNode) {\n const newAttrs = {\n index: newContentNode.attrs.index,\n level: newContentNode.attrs.level,\n type: newContentNode.type.name,\n depth: newState.doc.resolve(node.pos).depth,\n };\n\n const oldAttrs = {\n index: oldContentNode.attrs.index,\n level: oldContentNode.attrs.level,\n type: oldContentNode.type.name,\n depth: oldState.doc.resolve(oldNode.pos).depth,\n };\n\n currentTransactionOriginalOldBlockAttrs[nodeId] = oldAttrs;\n\n prev.currentTransactionOldBlockAttrs[nodeId] = oldAttrs;\n\n if (\n oldAttrs.index !== newAttrs.index ||\n oldAttrs.level !== newAttrs.level ||\n oldAttrs.type !== newAttrs.type ||\n oldAttrs.depth !== newAttrs.depth\n ) {\n (oldAttrs as any)[\"depth-change\"] =\n oldAttrs.depth - newAttrs.depth;\n\n prev.updatedBlocks.add(nodeId);\n }\n }\n }\n\n prev.prevTransactionOldBlockAttrs =\n currentTransactionOriginalOldBlockAttrs;\n\n return prev;\n },\n },\n props: {\n decorations(state) {\n const pluginState = (this as Plugin).getState(state);\n if (pluginState.updatedBlocks.size === 0) {\n return undefined;\n }\n\n const decorations: Decoration[] = [];\n\n state.doc.descendants((node, pos) => {\n if (!node.attrs.id) {\n return;\n }\n\n const id = getNodeId(node, state.doc);\n\n if (!pluginState.updatedBlocks.has(id)) {\n return;\n }\n\n const prevAttrs = pluginState.currentTransactionOldBlockAttrs[id];\n const decorationAttrs: any = {};\n\n for (const [nodeAttr, val] of Object.entries(prevAttrs)) {\n decorationAttrs[\"data-prev-\" + nodeAttributes[nodeAttr]] =\n val || \"none\";\n }\n\n decorations.push(\n Decoration.node(pos, pos + node.nodeSize, {\n ...decorationAttrs,\n }),\n );\n });\n\n return DecorationSet.create(state.doc, decorations);\n },\n },\n }),\n ],\n } as const;\n});\n","import { EditorView } from \"prosemirror-view\";\n\nexport function getDraggableBlockFromElement(\n element: Element,\n view: EditorView,\n) {\n while (\n element &&\n element.parentElement &&\n element.parentElement !== view.dom &&\n element.getAttribute?.(\"data-node-type\") !== \"blockContainer\"\n ) {\n element = element.parentElement;\n }\n if (element.getAttribute?.(\"data-node-type\") !== \"blockContainer\") {\n return undefined;\n }\n return { node: element as HTMLElement, id: element.getAttribute(\"data-id\")! };\n}\n","/**\n * Custom HTML-to-Markdown serializer for BlockNote.\n * Replaces the unified/rehype-remark pipeline with a direct DOM-based implementation.\n *\n * Input: HTML string from createExternalHTMLExporter\n * Output: GFM-compatible markdown string\n */\n\n/**\n * Convert an HTML string (from BlockNote's external HTML exporter) to markdown.\n */\nexport function htmlToMarkdown(html: string): string {\n // Use a temporary element to parse HTML. This works in both browser and\n // server (JSDOM) environments, unlike `new DOMParser()` which may not be\n // globally available in Node.js.\n const container = document.createElement(\"div\");\n container.innerHTML = html;\n const result = serializeChildren(container, {\n indent: \"\",\n inListItem: false,\n });\n return result.trim() + \"\\n\";\n}\n\ninterface SerializeContext {\n indent: string; // current indentation prefix for list nesting\n // True when the current node is being serialized as continuation content\n // of a parent list item. Used to suppress trailing blank lines that would\n // otherwise turn the parent list into a \"loose\" list.\n inListItem: boolean;\n}\n\n// ─── Main Serializer ─────────────────────────────────────────────────────────\n\nfunction serializeChildren(node: Node, ctx: SerializeContext): string {\n let result = \"\";\n const children = Array.from(node.childNodes);\n\n for (let i = 0; i < children.length; i++) {\n const child = children[i];\n result += serializeNode(child, ctx);\n }\n\n return result;\n}\n\nfunction serializeNode(node: Node, ctx: SerializeContext): string {\n if (node.nodeType === 3 /* Node.TEXT_NODE */) {\n return node.textContent || \"\";\n }\n\n if (node.nodeType !== 1 /* Node.ELEMENT_NODE */) {\n return \"\";\n }\n\n const el = node as HTMLElement;\n const tag = el.tagName.toLowerCase();\n\n switch (tag) {\n case \"p\":\n return serializeParagraph(el, ctx);\n case \"h1\":\n case \"h2\":\n case \"h3\":\n case \"h4\":\n case \"h5\":\n case \"h6\":\n return serializeHeading(el, ctx);\n case \"blockquote\":\n return serializeBlockquote(el, ctx);\n case \"pre\":\n return serializeCodeBlock(el, ctx);\n case \"ul\":\n return serializeUnorderedList(el, ctx);\n case \"ol\":\n return serializeOrderedList(el, ctx);\n case \"table\":\n return serializeTable(el, ctx);\n case \"hr\":\n return ctx.indent + \"***\\n\\n\";\n case \"math\":\n return serializeMathBlock(el, ctx);\n case \"img\":\n return serializeImage(el, ctx);\n case \"video\":\n return serializeVideo(el, ctx);\n case \"audio\":\n return serializeAudio(el, ctx);\n case \"embed\":\n return serializeEmbed(el, ctx);\n case \"figure\":\n return serializeFigure(el, ctx);\n case \"a\":\n // Block-level link (file block)\n return serializeBlockLink(el, ctx);\n case \"details\":\n return serializeDetails(el, ctx);\n case \"div\":\n // Page break or generic container — serialize children\n return serializeChildren(el, ctx);\n case \"br\":\n return \"\";\n default:\n return serializeChildren(el, ctx);\n }\n}\n\n// ─── Block Serializers ───────────────────────────────────────────────────────\n\nfunction serializeParagraph(el: HTMLElement, ctx: SerializeContext): string {\n const content = serializeInlineContent(el);\n // Trim leading/trailing hard breaks (matching remark behavior)\n const trimmed = trimHardBreaks(content);\n if (ctx.inListItem) {\n return trimmed;\n }\n return ctx.indent + trimmed + \"\\n\\n\";\n}\n\nfunction serializeHeading(el: HTMLElement, ctx: SerializeContext): string {\n const level = parseInt(el.tagName[1], 10);\n const prefix = \"#\".repeat(level) + \" \";\n const content = serializeInlineContent(el);\n return ctx.indent + prefix + content + \"\\n\\n\";\n}\n\nfunction serializeBlockquote(el: HTMLElement, ctx: SerializeContext): string {\n // Check if blockquote contains block-level elements (like <p>)\n const blockChildren = Array.from(el.children).filter((child) => {\n const tag = child.tagName.toLowerCase();\n return [\"p\", \"ul\", \"ol\", \"pre\", \"blockquote\", \"table\", \"hr\"].includes(tag);\n });\n\n let content: string;\n if (blockChildren.length > 0) {\n // Has block-level children — serialize each\n const parts: string[] = [];\n for (const child of blockChildren) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"p\") {\n parts.push(serializeInlineContent(child as HTMLElement));\n } else {\n const innerCtx: SerializeContext = { indent: \"\", inListItem: false };\n parts.push(serializeNode(child, innerCtx).trim());\n }\n }\n content = parts.join(\"\\n\\n\");\n } else {\n // No block-level children — treat entire content as inline\n content = serializeInlineContent(el);\n }\n\n const lines = content.split(\"\\n\");\n return lines.map((line) => ctx.indent + \"> \" + line).join(\"\\n\") + \"\\n\\n\";\n}\n\nfunction serializeCodeBlock(el: HTMLElement, ctx: SerializeContext): string {\n const codeEl = el.querySelector(\"code\");\n if (!codeEl) {\n return \"\";\n }\n\n const language =\n codeEl.getAttribute(\"data-language\") ||\n extractLanguageFromClass(codeEl.className) ||\n \"\";\n\n // Extract code content, handling <br> elements as newlines\n const code = extractCodeContent(codeEl);\n\n // Use a fence longer than the longest backtick run in the code\n const longestRun = Math.max(\n 0,\n ...(code.match(/`+/g) ?? []).map((run) => run.length),\n );\n const fence = \"`\".repeat(Math.max(3, longestRun + 1));\n\n // For empty code blocks, don't add a newline between the fences\n if (!code) {\n return ctx.indent + fence + language + \"\\n\" + ctx.indent + fence + \"\\n\\n\";\n }\n\n // Every (non-blank) line carries the indent - inside a list item or\n // blockquote, an unindented line would end the parent container. Blank\n // lines stay blank: they don't terminate an indented fence, and indenting\n // them would add trailing whitespace.\n const lines = [\n fence + language,\n ...(code.endsWith(\"\\n\") ? code.slice(0, -1) : code).split(\"\\n\"),\n fence,\n ];\n return (\n lines.map((line) => (line ? ctx.indent + line : line)).join(\"\\n\") + \"\\n\\n\"\n );\n}\n\n// The LaTeX source of a MathML element, taken from the annotation KaTeX\n// embeds in its output (also what external HTML parsing reads). Falls back to\n// the element's text content for MathML from other sources.\nfunction extractMathLatexSource(el: Element): string {\n const annotation = el.querySelector(\n 'annotation[encoding=\"application/x-tex\"]',\n );\n return (annotation?.textContent ?? el.textContent ?? \"\").trim();\n}\n\nfunction serializeMathBlock(el: HTMLElement, ctx: SerializeContext): string {\n const latex = extractMathLatexSource(el);\n // Every (non-blank) line carries the indent - inside a list item or\n // blockquote, an unindented line would end the parent container.\n return (\n [\"$$\", ...latex.split(\"\\n\"), \"$$\"]\n .map((line) => (line ? ctx.indent + line : line))\n .join(\"\\n\") + \"\\n\\n\"\n );\n}\n\nfunction extractCodeContent(el: Element): string {\n let result = \"\";\n for (const child of Array.from(el.childNodes)) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n result += child.textContent || \"\";\n } else if (child.nodeType === 1 /* Node.ELEMENT_NODE */) {\n const tag = (child as HTMLElement).tagName.toLowerCase();\n if (tag === \"br\") {\n result += \"\\n\";\n } else {\n result += extractCodeContent(child as Element);\n }\n }\n }\n return result;\n}\n\nfunction extractLanguageFromClass(className: string): string {\n const match = className.match(/language-(\\S+)/);\n return match ? match[1] : \"\";\n}\n\nfunction serializeUnorderedList(\n el: HTMLElement,\n ctx: SerializeContext,\n): string {\n let result = \"\";\n const items = Array.from(el.children).filter(\n (child) => child.tagName.toLowerCase() === \"li\",\n );\n\n for (const item of items) {\n result += serializeListItem(item as HTMLElement, \"bullet\", ctx);\n }\n\n // Trailing blank line separates the list from the next block. Skip when\n // this list is nested inside another list item — adding it would convert\n // the parent list into a \"loose\" list (or break tightness).\n if (!ctx.inListItem) {\n result += \"\\n\";\n }\n return result;\n}\n\nfunction serializeOrderedList(el: HTMLElement, ctx: SerializeContext): string {\n let result = \"\";\n const items = Array.from(el.children).filter(\n (child) => child.tagName.toLowerCase() === \"li\",\n );\n const startNum = parseInt(el.getAttribute(\"start\") || \"1\", 10);\n\n for (let i = 0; i < items.length; i++) {\n const num = startNum + i;\n result += serializeListItem(items[i] as HTMLElement, \"ordered\", ctx, num);\n }\n\n if (!ctx.inListItem) {\n result += \"\\n\";\n }\n return result;\n}\n\nfunction serializeListItem(\n el: HTMLElement,\n listType: \"bullet\" | \"ordered\",\n ctx: SerializeContext,\n num?: number,\n): string {\n // Check for checkbox (task list) - direct children only\n let checkbox: HTMLInputElement | null = null;\n let details: HTMLElement | null = null;\n\n for (const child of Array.from(el.children)) {\n const tag = child.tagName.toLowerCase();\n if (tag === \"input\" && (child as HTMLInputElement).type === \"checkbox\") {\n checkbox = child as HTMLInputElement;\n }\n if (tag === \"details\") {\n details = child as HTMLElement;\n }\n }\n\n let marker: string;\n let markerWidth: number;\n\n if (checkbox) {\n const state = checkbox.checked ? \"[x]\" : \"[ ]\";\n marker = `* ${state} `;\n // For child indentation, use bullet width (2), not full checkbox marker width\n markerWidth = 2;\n } else if (listType === \"ordered\") {\n marker = `${num}. `;\n markerWidth = marker.length;\n } else {\n marker = \"* \";\n markerWidth = 2;\n }\n\n // Collect the item's inline content\n let inlineContent: string;\n let firstContentEl: Element | null;\n\n if (details) {\n // Toggle item: get content from summary\n const summary = details.querySelector(\"summary\");\n const summaryP = summary?.querySelector(\"p\");\n firstContentEl = details;\n inlineContent = summaryP ? serializeInlineContent(summaryP) : \"\";\n } else {\n firstContentEl = getFirstContentElement(el, checkbox);\n inlineContent = firstContentEl\n ? serializeInlineContent(firstContentEl)\n : \"\";\n }\n\n // The marker line ends with a single `\\n` so that consecutive list items\n // produce a \"tight\" list (no blank line between markers). Continuation\n // content within the item (nested lists, continuation paragraphs, other\n // blocks) injects its own spacing as needed.\n let result = ctx.indent + marker + inlineContent + \"\\n\";\n\n // Serialize child content (nested lists, continuation paragraphs, etc.)\n const childIndent = ctx.indent + \" \".repeat(markerWidth);\n const childCtx: SerializeContext = { indent: childIndent, inListItem: true };\n\n // For toggle items, also serialize children inside the details element\n if (details) {\n const summary = details.querySelector(\"summary\");\n for (const child of Array.from(details.children)) {\n if (child === summary) {\n continue;\n }\n const childTag = child.tagName.toLowerCase();\n if (childTag === \"p\") {\n const content = serializeInlineContent(child as HTMLElement);\n // Continuation paragraph needs a blank line to separate it from the\n // previous content; CommonMark would otherwise treat it as a soft\n // wrap of that content.\n result += \"\\n\" + childIndent + content + \"\\n\";\n } else {\n result += serializeNode(child, childCtx);\n }\n }\n }\n\n const children = Array.from(el.children);\n for (const child of children) {\n const childTag = child.tagName.toLowerCase();\n\n // Skip the first content element and checkbox\n if (child === firstContentEl || (child as HTMLElement) === checkbox) {\n continue;\n }\n if (childTag === \"input\") {\n continue;\n }\n\n // Nested lists and other block content\n if (childTag === \"ul\" || childTag === \"ol\") {\n // Nested list flows directly under the parent marker — no blank line.\n result += serializeNode(child, childCtx);\n } else if (childTag === \"p\") {\n // Continuation paragraph within list item — requires blank line before\n // so it isn't read as part of the marker line's text.\n const content = serializeInlineContent(child as HTMLElement);\n result += \"\\n\" + childIndent + content + \"\\n\";\n } else {\n // Other block-level children (code blocks, blockquotes, etc.) already\n // emit their own separating newlines; prefix with a blank line so they\n // are recognized as separate blocks.\n result += \"\\n\" + serializeNode(child, childCtx);\n }\n }\n\n return result;\n}\n\nfunction getFirstContentElement(\n li: HTMLElement,\n checkbox: HTMLInputElement | null,\n): HTMLElement | null {\n for (const child of Array.from(li.children)) {\n if (child === checkbox) {\n continue;\n }\n if (child.tagName.toLowerCase() === \"input\") {\n continue;\n }\n const tag = child.tagName.toLowerCase();\n if (tag === \"p\" || tag === \"span\") {\n return child as HTMLElement;\n }\n }\n return null;\n}\n\n// ─── Table Serializer ────────────────────────────────────────────────────────\n\nfunction serializeTable(el: HTMLElement, ctx: SerializeContext): string {\n // First, determine column count from colgroup or first row\n const colgroup = el.querySelector(\"colgroup\");\n let colCount = 0;\n\n if (colgroup) {\n colCount = colgroup.querySelectorAll(\"col\").length;\n }\n\n const rows: string[][] = [];\n let hasHeader = false;\n\n // Collect all rows, handling colspan/rowspan\n const trElements = el.querySelectorAll(\"tr\");\n // Build a grid to handle colspan/rowspan\n const grid: (string | null)[][] = [];\n\n trElements.forEach((tr, rowIdx) => {\n if (!grid[rowIdx]) {\n grid[rowIdx] = [];\n }\n const cellElements = tr.querySelectorAll(\"th, td\");\n let gridCol = 0;\n\n cellElements.forEach((cell) => {\n // Find next empty column in this row\n while (grid[rowIdx][gridCol] !== undefined) {\n gridCol++;\n }\n\n if (rowIdx === 0 && cell.tagName.toLowerCase() === \"th\") {\n hasHeader = true;\n }\n\n const content = escapeTableCell(\n serializeInlineContent(cell as HTMLElement).trim(),\n );\n const colspan = parseInt(cell.getAttribute(\"colspan\") || \"1\", 10);\n const rowspan = parseInt(cell.getAttribute(\"rowspan\") || \"1\", 10);\n\n // Fill the grid\n for (let r = 0; r < rowspan; r++) {\n for (let c = 0; c < colspan; c++) {\n const ri = rowIdx + r;\n if (!grid[ri]) {\n grid[ri] = [];\n }\n grid[ri][gridCol + c] = r === 0 && c === 0 ? content : \"\";\n }\n }\n\n gridCol += colspan;\n });\n\n // Update colCount\n if (grid[rowIdx]) {\n colCount = Math.max(colCount, grid[rowIdx].length);\n }\n });\n\n // Convert grid to rows\n for (const gridRow of grid) {\n const row: string[] = [];\n for (let c = 0; c < colCount; c++) {\n row.push(gridRow && gridRow[c] !== undefined ? (gridRow[c] ?? \"\") : \"\");\n }\n rows.push(row);\n }\n\n if (rows.length === 0) {\n return \"\";\n }\n\n // Determine column widths\n const colWidths: number[] = [];\n for (let c = 0; c < colCount; c++) {\n let maxWidth = 3; // minimum width for separator \"---\"\n for (const row of rows) {\n const cellWidth = c < row.length ? row[c].length : 0;\n maxWidth = Math.max(maxWidth, cellWidth);\n }\n // Use minimum of 10 to match remark output\n colWidths.push(Math.max(maxWidth, 10));\n }\n\n let result = \"\";\n\n if (hasHeader) {\n result += ctx.indent + formatTableRow(rows[0], colWidths, colCount) + \"\\n\";\n result += ctx.indent + formatSeparatorRow(colWidths, colCount) + \"\\n\";\n for (let r = 1; r < rows.length; r++) {\n result +=\n ctx.indent + formatTableRow(rows[r], colWidths, colCount) + \"\\n\";\n }\n } else {\n // No header — emit empty header + separator\n const emptyRow = new Array(colCount).fill(\"\");\n result += ctx.indent + formatTableRow(emptyRow, colWidths, colCount) + \"\\n\";\n result += ctx.indent + formatSeparatorRow(colWidths, colCount) + \"\\n\";\n for (const row of rows) {\n result += ctx.indent + formatTableRow(row, colWidths, colCount) + \"\\n\";\n }\n }\n\n result += \"\\n\";\n return result;\n}\n\nfunction escapeTableCell(text: string): string {\n return text.replace(/\\|/g, \"\\\\|\");\n}\n\nfunction formatTableRow(\n cells: string[],\n colWidths: number[],\n colCount: number,\n): string {\n const parts: string[] = [];\n for (let c = 0; c < colCount; c++) {\n const cell = c < cells.length ? cells[c] : \"\";\n parts.push(\" \" + cell.padEnd(colWidths[c]) + \" \");\n }\n return \"|\" + parts.join(\"|\") + \"|\";\n}\n\nfunction formatSeparatorRow(colWidths: number[], colCount: number): string {\n const parts: string[] = [];\n for (let c = 0; c < colCount; c++) {\n parts.push(\" \" + \"-\".repeat(colWidths[c]) + \" \");\n }\n return \"|\" + parts.join(\"|\") + \"|\";\n}\n\n// ─── Media Serializers ───────────────────────────────────────────────────────\n\nfunction serializeImage(el: HTMLElement, ctx: SerializeContext): string {\n const src = el.getAttribute(\"src\") || \"\";\n const alt = el.getAttribute(\"alt\") || \"\";\n // Empty placeholder — preserve the block-level break, matching how\n // serializeParagraph/serializeHeading emit `\\n\\n` for empty content.\n if (!src) {\n return \"\\n\\n\";\n }\n return ctx.indent + `\\n\\n`;\n}\n\nfunction serializeVideo(el: HTMLElement, ctx: SerializeContext): string {\n const src = el.getAttribute(\"src\") || el.getAttribute(\"data-url\") || \"\";\n const name = el.getAttribute(\"data-name\") || el.getAttribute(\"title\") || \"\";\n if (!src) {\n return \"\\n\\n\";\n }\n return ctx.indent + `\\n\\n`;\n}\n\nfunction serializeAudio(el: HTMLElement, ctx: SerializeContext): string {\n const src = el.getAttribute(\"src\") || \"\";\n if (!src) {\n return \"\\n\\n\";\n }\n // Audio has no markdown syntax, so emit raw HTML. The markdown parser\n // passes <audio> blocks through verbatim and BlockNote's audio block parser\n // recognizes them, giving a clean round-trip.\n return (\n ctx.indent + `<audio src=\"${escapeHtmlAttr(src)}\" controls></audio>\\n\\n`\n );\n}\n\nfunction serializeEmbed(el: HTMLElement, ctx: SerializeContext): string {\n const src = el.getAttribute(\"src\") || \"\";\n if (!src) {\n return \"\\n\\n\";\n }\n return ctx.indent + `[](${src})\\n\\n`;\n}\n\nfunction serializeFigure(el: HTMLElement, ctx: SerializeContext): string {\n const img = el.querySelector(\"img\");\n const video = el.querySelector(\"video\");\n const audio = el.querySelector(\"audio\");\n const link = el.querySelector(\"a\");\n\n const figcaption = el.querySelector(\"figcaption\");\n const captionText = figcaption?.textContent?.trim() || \"\";\n\n if (img) {\n return serializeMediaFigure(\n \"img\",\n img.getAttribute(\"src\") || \"\",\n img.getAttribute(\"alt\") || \"\",\n captionText,\n ctx,\n );\n }\n if (video) {\n const src =\n video.getAttribute(\"src\") || video.getAttribute(\"data-url\") || \"\";\n const name =\n video.getAttribute(\"data-name\") || video.getAttribute(\"title\") || \"\";\n return serializeMediaFigure(\"video\", src, name, captionText, ctx);\n }\n if (audio) {\n return serializeMediaFigure(\n \"audio\",\n audio.getAttribute(\"src\") || \"\",\n \"\",\n captionText,\n ctx,\n );\n }\n if (link) {\n return serializeBlockLink(link as HTMLElement, ctx);\n }\n return \"\";\n}\n\nfunction serializeMediaFigure(\n kind: \"img\" | \"video\" | \"audio\",\n src: string,\n descriptor: string,\n captionText: string,\n ctx: SerializeContext,\n): string {\n if (!src) {\n return \"\";\n }\n\n // No caption + has a markdown shorthand → use it.\n if (!captionText && kind !== \"audio\") {\n return ctx.indent + `\\n\\n`;\n }\n\n // The descriptor (alt / data-name) is dropped when it duplicates the\n // caption text; otherwise on round-trip both `name` and `caption` would\n // get set to the same string (BlockNote's HTML exporter writes alt =\n // name || caption, so a caption-only image has alt === figcaption text).\n const showDescriptor = descriptor && descriptor !== captionText;\n const descAttr = !showDescriptor\n ? \"\"\n : kind === \"img\"\n ? ` alt=\"${escapeHtmlAttr(descriptor)}\"`\n : kind === \"video\"\n ? ` data-name=\"${escapeHtmlAttr(descriptor)}\"`\n : \"\";\n\n const tag =\n kind === \"img\"\n ? `<img${descAttr} src=\"${escapeHtmlAttr(src)}\">`\n : `<${kind} src=\"${escapeHtmlAttr(src)}\"${descAttr} controls></${kind}>`;\n\n const captionPart = captionText\n ? `<figcaption>${escapeHtmlText(captionText)}</figcaption>`\n : \"\";\n return ctx.indent + `<figure>${tag}${captionPart}</figure>\\n\\n`;\n}\n\nfunction escapeHtmlAttr(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/\"/g, \""\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction escapeHtmlText(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\");\n}\n\nfunction serializeBlockLink(el: HTMLElement, ctx: SerializeContext): string {\n const href = el.getAttribute(\"href\") || \"\";\n const text = el.textContent?.trim() || \"\";\n if (!href) {\n return ctx.indent + text + \"\\n\\n\";\n }\n return ctx.indent + formatLink(text, href) + \"\\n\\n\";\n}\n\n/**\n * Render a link, mirroring the remark-stringify behavior from\n * TypeCellOS/BlockNote#2661: when the link label equals the URL (or is\n * empty), emit the bare URL so that pasting the link into another input\n * produces a valid href instead of `<url>`-autolink brackets or redundant\n * `[url](url)` markup. Otherwise emit `[text](url)` with the URL escaped so\n * a `)` inside the URL does not prematurely close the destination.\n */\nfunction formatLink(text: string, href: string): string {\n if (!text || text === href) {\n return href;\n }\n return `[${text}](${escapeLinkDestination(href)})`;\n}\n\nfunction escapeLinkDestination(url: string): string {\n return url.replace(/[\\\\()]/g, \"\\\\$&\");\n}\n\nfunction serializeDetails(el: HTMLElement, ctx: SerializeContext): string {\n // Toggle heading or toggle list item\n const summary = el.querySelector(\"summary\");\n if (!summary) {\n return serializeChildren(el, ctx);\n }\n\n // Check if summary contains a heading\n const heading = summary.querySelector(\"h1, h2, h3, h4, h5, h6\");\n if (heading) {\n let result = serializeHeading(heading as HTMLElement, ctx);\n // Also serialize non-summary children of details\n for (const child of Array.from(el.children)) {\n if (child !== summary) {\n result += serializeNode(child, ctx);\n }\n }\n return result;\n }\n\n // Otherwise serialize the summary content\n return serializeChildren(summary, ctx);\n}\n\n// ─── Inline Content Serializer ───────────────────────────────────────────────\n\nfunction serializeInlineContent(el: Element): string {\n let result = \"\";\n\n for (const child of Array.from(el.childNodes)) {\n if (child.nodeType === 3 /* Node.TEXT_NODE */) {\n result += child.textContent || \"\";\n } else if (child.nodeType === 1 /* Node.ELEMENT_NODE */) {\n const childEl = child as HTMLElement;\n const tag = childEl.tagName.toLowerCase();\n\n switch (tag) {\n case \"strong\":\n case \"b\": {\n const inner = serializeInlineContent(childEl);\n const { content, trailing } = extractTrailingWhitespace(inner);\n if (content) {\n result += `**${content}**${trailing}`;\n } else {\n // All whitespace — just output it without emphasis\n result += trailing;\n }\n break;\n }\n case \"em\":\n case \"i\": {\n const inner = serializeInlineContent(childEl);\n const { content, trailing } = extractTrailingWhitespace(inner);\n if (content) {\n result += `*${content}*${trailing}`;\n } else {\n result += trailing;\n }\n break;\n }\n case \"s\":\n case \"del\":\n result += `~~${serializeInlineContent(childEl)}~~`;\n break;\n case \"code\": {\n const text = childEl.textContent || \"\";\n const longestRun = Math.max(\n 0,\n ...(text.match(/`+/g) ?? []).map((run) => run.length),\n );\n const fence = \"`\".repeat(longestRun + 1);\n const needsPadding = text.startsWith(\"`\") || text.endsWith(\"`\");\n result += fence + (needsPadding ? ` ${text} ` : text) + fence;\n break;\n }\n case \"u\":\n // No markdown equivalent — strip the tag, keep content\n result += serializeInlineContent(childEl);\n break;\n case \"a\": {\n const href = childEl.getAttribute(\"href\") || \"\";\n const text = serializeInlineContent(childEl);\n result += formatLink(text, href);\n break;\n }\n case \"br\":\n result += \"\\\\\\n\";\n break;\n case \"math\": {\n // Inline math — emit its LaTeX source as a math span. Collapsed to\n // a single line, as $...$ spans cannot contain newlines.\n const latex = extractMathLatexSource(childEl)\n .split(\"\\n\")\n .map((line) => line.trim())\n .join(\" \");\n result += `$${latex}$`;\n break;\n }\n case \"span\":\n // Color spans, etc. — strip the tag, keep content\n result += serializeInlineContent(childEl);\n break;\n case \"img\": {\n const src = childEl.getAttribute(\"src\") || \"\";\n const alt = childEl.getAttribute(\"alt\") || \"\";\n result += ``;\n break;\n }\n case \"video\": {\n const src =\n childEl.getAttribute(\"src\") ||\n childEl.getAttribute(\"data-url\") ||\n \"\";\n const name =\n childEl.getAttribute(\"data-name\") ||\n childEl.getAttribute(\"title\") ||\n \"\";\n result += ``;\n break;\n }\n case \"p\":\n // Paragraph inside inline context (e.g., table cell)\n result += serializeInlineContent(childEl);\n break;\n case \"input\":\n // Checkbox in task list — handled at block level\n break;\n default:\n result += serializeInlineContent(childEl);\n break;\n }\n }\n }\n\n return result;\n}\n\n/**\n * Extract trailing whitespace from emphasis content.\n * Moves trailing spaces outside the emphasis delimiters to produce valid markdown.\n * E.g., `<strong>Bold </strong>` → `**Bold** ` instead of `**Bold **`.\n */\nfunction extractTrailingWhitespace(text: string): {\n content: string;\n trailing: string;\n} {\n const match = text.match(/^(.*?)(\\s*)$/);\n if (match) {\n return { content: match[1], trailing: match[2] };\n }\n return { content: text, trailing: \"\" };\n}\n\n/**\n * Escape leading character after emphasis if it could break parsing.\n * For example, \"Heading\" after \"**Bold **\" — the 'H' should be escaped\n * if the trailing space was escaped.\n */\n\n/**\n * Trim leading/trailing hard breaks from inline content.\n * Matches remark behavior where <br> at start/end of paragraph is dropped.\n */\nfunction trimHardBreaks(content: string): string {\n // Remove leading hard breaks\n let result = content.replace(/^(\\\\\\n)+/, \"\");\n // Remove trailing hard breaks produced by `<br>`\n result = result.replace(/(\\\\\\n)+$/, \"\");\n return result;\n}\n","import { Schema } from \"prosemirror-model\";\n\nimport { PartialBlock } from \"../../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport {\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../../schema/index.js\";\nimport { createExternalHTMLExporter } from \"../html/externalHTMLExporter.js\";\nimport { EMPTY_BLOCK_PLACEHOLDER } from \"../html/util/serializeBlocksExternalHTML.js\";\nimport { htmlToMarkdown } from \"./htmlToMarkdown.js\";\n\n// Needs to be sync because it's used in drag handler event (SideMenuPlugin)\nexport function cleanHTMLToMarkdown(cleanHTMLString: string) {\n // The external HTML exporter fills empty inline-content blocks with a\n // placeholder character so they survive an HTML round trip (see\n // `EMPTY_BLOCK_PLACEHOLDER`). Markdown has no need for that placeholder, so we\n // remove it to avoid it showing up as a stray character in the output.\n const withoutPlaceholder = cleanHTMLString\n .split(EMPTY_BLOCK_PLACEHOLDER)\n .join(\"\");\n\n return htmlToMarkdown(withoutPlaceholder);\n}\n\nexport function blocksToMarkdown<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n blocks: PartialBlock<BSchema, I, S>[],\n schema: Schema,\n editor: BlockNoteEditor<BSchema, I, S>,\n options: { document?: Document },\n): string {\n const exporter = createExternalHTMLExporter(schema, editor);\n const externalHTML = exporter.exportBlocks(blocks, options);\n\n return cleanHTMLToMarkdown(externalHTML);\n}\n","import { Fragment } from \"@tiptap/pm/model\";\nimport {\n BlockNoDefaults,\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../schema/index.js\";\nimport { nodeToBlock } from \"./nodeToBlock.js\";\n\n/**\n * Converts all Blocks within a fragment to BlockNote blocks.\n */\nexport function fragmentToBlocks<\n B extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(fragment: Fragment) {\n // first convert selection to blocknote-style blocks, and then\n // pass these to the exporter\n const blocks: BlockNoDefaults<B, I, S>[] = [];\n fragment.descendants((node) => {\n if (node.type.name === \"blockContainer\") {\n if (node.firstChild?.type.name === \"blockGroup\") {\n // selection started within a block group\n // in this case the fragment starts with:\n // <blockContainer>\n // <blockGroup>\n // <blockContainer ... />\n // <blockContainer ... />\n // </blockGroup>\n // </blockContainer>\n //\n // instead of:\n // <blockContainer>\n // <blockContent ... />\n // <blockGroup>\n // <blockContainer ... />\n // <blockContainer ... />\n // </blockGroup>\n // </blockContainer>\n //\n // so we don't need to serialize this block, just descend into the children of the blockGroup\n return true;\n }\n }\n\n if (node.type.name === \"columnList\" && node.childCount === 1) {\n // column lists with a single column should be flattened (not the entire column list has been selected)\n node.firstChild?.forEach((child) => {\n blocks.push(nodeToBlock(child, node));\n });\n return false;\n }\n\n if (node.type.isInGroup(\"bnBlock\")) {\n blocks.push(nodeToBlock(node, node));\n // don't descend into children, as they're already included in the block returned by nodeToBlock\n return false;\n }\n return true;\n });\n return blocks;\n}\n","import { Fragment, Node, ResolvedPos, Slice } from \"prosemirror-model\";\nimport { Selection } from \"prosemirror-state\";\nimport { Mappable } from \"prosemirror-transform\";\n\n/**\n * This class represents an editor selection which spans multiple nodes/blocks. It's currently only used to allow users\n * to drag multiple blocks at the same time. Expects the selection anchor and head to be between nodes, i.e. just before\n * the first target node and just after the last, and that anchor and head are at the same nesting level.\n *\n * Partially based on ProseMirror's NodeSelection implementation:\n * (https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.ts)\n * MultipleNodeSelection differs from NodeSelection in the following ways:\n * 1. Stores which nodes are included in the selection instead of just a single node.\n * 2. Already expects the selection to start just before the first target node and ends just after the last, while a\n * NodeSelection automatically sets both anchor and head to just before the single target node.\n */\nexport class MultipleNodeSelection extends Selection {\n nodes: Array<Node>;\n\n constructor($anchor: ResolvedPos, $head: ResolvedPos) {\n super($anchor, $head);\n\n // Parent is at the same nesting level as anchor/head since they are just before/ just after target nodes.\n const parentNode = $anchor.node();\n\n this.nodes = [];\n $anchor.doc.nodesBetween($anchor.pos, $head.pos, (node, _pos, parent) => {\n if (parent !== null && parent.eq(parentNode)) {\n this.nodes.push(node);\n return false;\n }\n return;\n });\n }\n\n static create(doc: Node, from: number, to = from): MultipleNodeSelection {\n return new MultipleNodeSelection(doc.resolve(from), doc.resolve(to));\n }\n\n content(): Slice {\n return new Slice(Fragment.from(this.nodes), 0, 0);\n }\n\n eq(selection: Selection): boolean {\n if (!(selection instanceof MultipleNodeSelection)) {\n return false;\n }\n\n if (this.nodes.length !== selection.nodes.length) {\n return false;\n }\n\n if (this.from !== selection.from || this.to !== selection.to) {\n return false;\n }\n\n for (let i = 0; i < this.nodes.length; i++) {\n if (!this.nodes[i].eq(selection.nodes[i])) {\n return false;\n }\n }\n\n return true;\n }\n\n map(doc: Node, mapping: Mappable): Selection {\n const fromResult = mapping.mapResult(this.from);\n const toResult = mapping.mapResult(this.to);\n\n if (toResult.deleted) {\n return Selection.near(doc.resolve(fromResult.pos));\n }\n\n if (fromResult.deleted) {\n return Selection.near(doc.resolve(toResult.pos));\n }\n\n return new MultipleNodeSelection(\n doc.resolve(fromResult.pos),\n doc.resolve(toResult.pos),\n );\n }\n\n toJSON(): any {\n return { type: \"multiple-node\", anchor: this.anchor, head: this.head };\n }\n}\n\nSelection.jsonID(\"multiple-node\", MultipleNodeSelection);\n","import { Node } from \"prosemirror-model\";\nimport { NodeSelection, Selection } from \"prosemirror-state\";\nimport { EditorView } from \"prosemirror-view\";\n\nimport { createExternalHTMLExporter } from \"../../api/exporters/html/externalHTMLExporter.js\";\nimport { cleanHTMLToMarkdown } from \"../../api/exporters/markdown/markdownExporter.js\";\nimport { fragmentToBlocks } from \"../../api/nodeConversions/fragmentToBlocks.js\";\nimport { getNodeById } from \"../../api/nodeUtil.js\";\nimport { Block } from \"../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport { UiElementPosition } from \"../../extensions-shared/UiElementPosition.js\";\nimport {\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../schema/index.js\";\nimport { MultipleNodeSelection } from \"./MultipleNodeSelection.js\";\n\nlet dragImageElement: Element | undefined;\n\nexport type SideMenuState<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n> = UiElementPosition & {\n // The block that the side menu is attached to.\n block: Block<BSchema, I, S>;\n};\n\nfunction blockPositionsFromSelection(selection: Selection, doc: Node) {\n // Absolute positions just before the first block spanned by the selection, and just after the last block. Having the\n // selection start and end just before and just after the target blocks ensures no whitespace/line breaks are left\n // behind after dragging & dropping them.\n let beforeFirstBlockPos: number;\n let afterLastBlockPos: number;\n\n // Even the user starts dragging blocks but drops them in the same place, the selection will still be moved just\n // before & just after the blocks spanned by the selection, and therefore doesn't need to change if they try to drag\n // the same blocks again. If this happens, the anchor & head move out of the block content node they were originally\n // in. If the anchor should update but the head shouldn't and vice versa, it means the user selection is outside a\n // block content node, which should never happen.\n const selectionStartInBlockContent =\n doc.resolve(selection.from).node().type.spec.group === \"blockContent\";\n const selectionEndInBlockContent =\n doc.resolve(selection.to).node().type.spec.group === \"blockContent\";\n\n // Ensures that entire outermost nodes are selected if the selection spans multiple nesting levels.\n const minDepth = Math.min(selection.$anchor.depth, selection.$head.depth);\n\n if (selectionStartInBlockContent && selectionEndInBlockContent) {\n // Absolute positions at the start of the first block in the selection and at the end of the last block. User\n // selections will always start and end in block content nodes, but we want the start and end positions of their\n // parent block nodes, which is why minDepth - 1 is used.\n const startFirstBlockPos = selection.$from.start(minDepth - 1);\n const endLastBlockPos = selection.$to.end(minDepth - 1);\n\n // Shifting start and end positions by one moves them just outside the first and last selected blocks.\n beforeFirstBlockPos = doc.resolve(startFirstBlockPos - 1).pos;\n afterLastBlockPos = doc.resolve(endLastBlockPos + 1).pos;\n } else {\n beforeFirstBlockPos = selection.from;\n afterLastBlockPos = selection.to;\n }\n\n return { from: beforeFirstBlockPos, to: afterLastBlockPos };\n}\n\nfunction setDragImage(view: EditorView, from: number, to = from) {\n if (from === to) {\n // Moves to position to be just after the first (and only) selected block.\n to += view.state.doc.resolve(from + 1).node().nodeSize;\n }\n\n // Parent element is cloned to remove all unselected children without affecting the editor content.\n const parentClone = view.domAtPos(from).node.cloneNode(true) as Element;\n const parent = view.domAtPos(from).node as Element;\n\n const getElementIndex = (parentElement: Element, targetElement: Element) =>\n Array.prototype.indexOf.call(parentElement.children, targetElement);\n\n const firstSelectedBlockIndex = getElementIndex(\n parent,\n // Expects from position to be just before the first selected block.\n view.domAtPos(from + 1).node.parentElement!,\n );\n const lastSelectedBlockIndex = getElementIndex(\n parent,\n // Expects to position to be just after the last selected block.\n view.domAtPos(to - 1).node.parentElement!,\n );\n\n for (let i = parent.childElementCount - 1; i >= 0; i--) {\n if (i > lastSelectedBlockIndex || i < firstSelectedBlockIndex) {\n parentClone.removeChild(parentClone.children[i]);\n }\n }\n\n // dataTransfer.setDragImage(element) only works if element is attached to the DOM.\n unsetDragImage(view.root);\n dragImageElement = parentClone;\n\n // Browsers may have CORS policies which prevents iframes from being\n // manipulated, so better to stay on the safe side and remove them from the\n // drag preview. The drag preview doesn't work with embedded documents\n // (iframe/embed/object) anyway, and including an <embed> (e.g. a PDF)\n // can prevent the drag from initiating at all.\n const embeddedDocs = dragImageElement.querySelectorAll(\n \"iframe, embed, object\",\n );\n embeddedDocs.forEach((el) => el.parentElement?.removeChild(el));\n\n // TODO: This is hacky, need a better way of assigning classes to the editor so that they can also be applied to the\n // drag preview.\n const classes = view.dom.className.split(\" \");\n const inheritedClasses = classes\n .filter(\n (className) =>\n className !== \"ProseMirror\" &&\n className !== \"bn-root\" &&\n className !== \"bn-editor\",\n )\n .join(\" \");\n\n dragImageElement.className =\n dragImageElement.className + \" bn-drag-preview \" + inheritedClasses;\n\n if (view.root instanceof ShadowRoot) {\n view.root.appendChild(dragImageElement);\n } else {\n view.root.body.appendChild(dragImageElement);\n }\n}\n\nexport function unsetDragImage(rootEl: Document | ShadowRoot) {\n if (dragImageElement !== undefined) {\n if (rootEl instanceof ShadowRoot) {\n rootEl.removeChild(dragImageElement);\n } else {\n rootEl.body.removeChild(dragImageElement);\n }\n\n dragImageElement = undefined;\n }\n}\n\nexport function dragStart<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n e: { dataTransfer: DataTransfer | null; clientY: number },\n block: Block<BSchema, I, S>,\n editor: BlockNoteEditor<BSchema, I, S>,\n) {\n if (!e.dataTransfer) {\n return;\n }\n\n if (editor.headless) {\n return;\n }\n const view = editor.prosemirrorView;\n\n const posInfo = getNodeById(block.id, view.state.doc);\n if (!posInfo) {\n throw new Error(`Block with ID ${block.id} not found`);\n }\n const pos = posInfo.posBeforeNode;\n\n if (pos != null) {\n const selection = view.state.selection;\n const doc = view.state.doc;\n\n const { from, to } = blockPositionsFromSelection(selection, doc);\n\n const draggedBlockInSelection = from <= pos && pos < to;\n const multipleBlocksSelected =\n selection.$anchor.node() !== selection.$head.node() ||\n selection instanceof MultipleNodeSelection;\n\n if (draggedBlockInSelection && multipleBlocksSelected) {\n view.dispatch(\n view.state.tr.setSelection(MultipleNodeSelection.create(doc, from, to)),\n );\n setDragImage(view, from, to);\n } else {\n view.dispatch(\n view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)),\n );\n setDragImage(view, pos);\n }\n\n const selectedSlice = view.state.selection.content();\n const schema = editor.pmSchema;\n\n const clipboardHTML =\n view.serializeForClipboard(selectedSlice).dom.innerHTML;\n\n const externalHTMLExporter = createExternalHTMLExporter(schema, editor);\n\n const blocks = fragmentToBlocks(selectedSlice.content);\n const externalHTML = externalHTMLExporter.exportBlocks(blocks, {});\n\n const plainText = cleanHTMLToMarkdown(externalHTML);\n\n e.dataTransfer.clearData();\n e.dataTransfer.setData(\"blocknote/html\", clipboardHTML);\n e.dataTransfer.setData(\"text/html\", externalHTML);\n e.dataTransfer.setData(\"text/plain\", plainText);\n e.dataTransfer.effectAllowed = \"move\";\n e.dataTransfer.setDragImage(dragImageElement!, 0, 0);\n }\n}\n","import { DOMParser, Slice } from \"@tiptap/pm/model\";\nimport {\n EditorState,\n Plugin,\n PluginKey,\n PluginView,\n TextSelection,\n} from \"@tiptap/pm/state\";\nimport { EditorView } from \"@tiptap/pm/view\";\n\nimport { Block } from \"../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport {\n createExtension,\n createStore,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { UiElementPosition } from \"../../extensions-shared/UiElementPosition.js\";\nimport {\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../schema/index.js\";\nimport { getDraggableBlockFromElement } from \"../getDraggableBlockFromElement.js\";\nimport { dragStart, unsetDragImage } from \"./dragging.js\";\n\nexport type SideMenuState<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n> = UiElementPosition & {\n // The block that the side menu is attached to.\n block: Block<BSchema, I, S>;\n};\n\nconst DISTANCE_TO_CONSIDER_EDITOR_BOUNDS = 250;\n\nfunction getBlockFromCoords(\n view: EditorView,\n coords: { left: number; top: number },\n adjustForColumns = true,\n) {\n const elements = view.root.elementsFromPoint(coords.left, coords.top);\n\n for (const element of elements) {\n if (!view.dom.contains(element)) {\n // probably a ui overlay like formatting toolbar etc\n continue;\n }\n if (adjustForColumns) {\n const column = element.closest(\"[data-node-type=columnList]\");\n if (column) {\n return getBlockFromCoords(\n view,\n {\n // TODO can we do better than this?\n left: coords.left + 50, // bit hacky, but if we're inside a column, offset x position to right to account for the width of sidemenu itself\n top: coords.top,\n },\n false,\n );\n }\n }\n return getDraggableBlockFromElement(element, view);\n }\n return undefined;\n}\n\nfunction getBlockFromMousePos(\n mousePos: {\n x: number;\n y: number;\n },\n view: EditorView,\n): { node: HTMLElement; id: string } | undefined {\n // Editor itself may have padding or other styling which affects\n // size/position, so we get the boundingRect of the first child (i.e. the\n // blockGroup that wraps all blocks in the editor) for more accurate side\n // menu placement.\n if (!view.dom.firstChild) {\n return;\n }\n\n const editorBoundingBox = (\n view.dom.firstChild as HTMLElement\n ).getBoundingClientRect();\n\n // Gets block at mouse cursor's position.\n const coords = {\n // Clamps the x position to the editor's bounding box.\n left: Math.min(\n Math.max(editorBoundingBox.left + 10, mousePos.x),\n editorBoundingBox.right - 10,\n ),\n top: mousePos.y,\n };\n\n const referenceBlock = getBlockFromCoords(view, coords);\n\n if (!referenceBlock) {\n // could not find the reference block\n return undefined;\n }\n\n /**\n * Because blocks may be nested, we need to check the right edge of the parent block:\n * ```\n * | BlockA |\n * x | BlockB y|\n * ```\n * Hovering at position x (left edge of BlockB) would return BlockA.\n * Instead, we check at position y (right edge of BlockA) to correctly identify BlockB.\n */\n const referenceBlocksBoundingBox =\n referenceBlock.node.getBoundingClientRect();\n return getBlockFromCoords(\n view,\n {\n left: referenceBlocksBoundingBox.right - 10,\n top: mousePos.y,\n },\n false,\n );\n}\n\n/**\n * With the sidemenu plugin we can position a menu next to a hovered block.\n */\nexport class SideMenuView<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n> implements PluginView {\n public state?: SideMenuState<BSchema, I, S>;\n public readonly emitUpdate: (state: SideMenuState<BSchema, I, S>) => void;\n\n private mousePos: { x: number; y: number } | undefined;\n\n private hoveredBlock: HTMLElement | undefined;\n\n public menuFrozen = false;\n\n public isDragOrigin = false;\n\n constructor(\n private readonly editor: BlockNoteEditor<BSchema, I, S>,\n private readonly pmView: EditorView,\n emitUpdate: (state: SideMenuState<BSchema, I, S>) => void,\n ) {\n this.emitUpdate = () => {\n if (!this.state) {\n throw new Error(\"Attempting to update uninitialized side menu\");\n }\n\n emitUpdate(this.state);\n };\n\n this.pmView.root.addEventListener(\n \"dragstart\",\n this.onDragStart as EventListener,\n );\n this.pmView.root.addEventListener(\n \"dragover\",\n this.onDragOver as EventListener,\n );\n this.pmView.root.addEventListener(\n \"drop\",\n this.onDrop as EventListener,\n true,\n );\n this.pmView.root.addEventListener(\n \"dragend\",\n this.onDragEnd as EventListener,\n true,\n );\n\n // Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.\n this.pmView.root.addEventListener(\n \"mousemove\",\n this.onMouseMove as EventListener,\n true,\n );\n\n // Hides and unfreezes the menu whenever the user presses a key.\n this.pmView.root.addEventListener(\n \"keydown\",\n this.onKeyDown as EventListener,\n true,\n );\n }\n\n updateState = (state: SideMenuState<BSchema, I, S>) => {\n this.state = state;\n this.emitUpdate(this.state);\n };\n\n updateStateFromMousePos = () => {\n if (this.menuFrozen || !this.mousePos) {\n return;\n }\n\n const closestEditor = this.findClosestEditorElement({\n clientX: this.mousePos.x,\n clientY: this.mousePos.y,\n });\n\n if (\n closestEditor?.element !== this.pmView.dom ||\n closestEditor.distance > DISTANCE_TO_CONSIDER_EDITOR_BOUNDS\n ) {\n if (this.state?.show) {\n this.state.show = false;\n this.updateState(this.state);\n }\n return;\n }\n\n const block = getBlockFromMousePos(this.mousePos, this.pmView);\n\n // Closes the menu if the mouse cursor is beyond the editor vertically.\n if (!block || !this.editor.isEditable) {\n if (this.state?.show) {\n this.state.show = false;\n this.updateState(this.state);\n }\n\n return;\n }\n\n // Doesn't update if the menu is already open and the mouse cursor is still hovering the same block.\n if (\n this.state?.show &&\n this.hoveredBlock?.hasAttribute(\"data-id\") &&\n this.hoveredBlock?.getAttribute(\"data-id\") === block.id\n ) {\n return;\n }\n\n this.hoveredBlock = block.node;\n\n // Shows or updates elements.\n if (this.editor.isEditable) {\n const blockContentBoundingBox = block.node.getBoundingClientRect();\n const column = block.node.closest(\"[data-node-type=column]\");\n const sideMenuBlock = this.editor.getBlock(\n this.hoveredBlock!.getAttribute(\"data-id\")!,\n );\n if (!sideMenuBlock) {\n if (this.state?.show) {\n this.state.show = false;\n this.hoveredBlock = undefined;\n this.emitUpdate(this.state);\n }\n return;\n }\n this.state = {\n show: true,\n referencePos: new DOMRect(\n column\n ? // We take the first child as column elements have some default\n // padding. This is a little weird since this child element will\n // be the first block, but since it's always non-nested and we\n // only take the x coordinate, it's ok.\n column.firstElementChild!.getBoundingClientRect().x\n : (\n this.pmView.dom.firstChild as HTMLElement\n ).getBoundingClientRect().x,\n blockContentBoundingBox.y,\n blockContentBoundingBox.width,\n blockContentBoundingBox.height,\n ),\n block: sideMenuBlock,\n };\n this.updateState(this.state);\n }\n };\n\n /**\n * If a block is being dragged, ProseMirror usually gets the context of what's\n * being dragged from `view.dragging`, which is automatically set when a\n * `dragstart` event fires in the editor. However, if the user tries to drag\n * and drop blocks between multiple editors, only the one in which the drag\n * began has that context, so we need to set it on the others manually. This\n * ensures that PM always drops the blocks in between other blocks, and not\n * inside them.\n *\n * After the `dragstart` event fires on the drag handle, it sets\n * `blocknote/html` data on the clipboard. This handler fires right after,\n * parsing the `blocknote/html` data into nodes and setting them on\n * `view.dragging`.\n *\n * Note: Setting `view.dragging` on `dragover` would be better as the user\n * could then drag between editors in different windows, but you can only\n * access `dataTransfer` contents on `dragstart` and `drop` events.\n */\n onDragStart = (event: DragEvent) => {\n const html = event.dataTransfer?.getData(\"blocknote/html\");\n if (!html) {\n return;\n }\n\n if (this.pmView.dragging) {\n // already dragging, so no-op\n return;\n }\n\n const element = document.createElement(\"div\");\n element.innerHTML = html;\n\n const parser = DOMParser.fromSchema(this.pmView.state.schema);\n const node = parser.parse(element, {\n topNode: this.pmView.state.schema.nodes[\"blockGroup\"].create(),\n });\n\n this.pmView.dragging = {\n slice: new Slice(node.content, 0, 0),\n move: true,\n };\n };\n\n /**\n * Finds the closest editor visually to the given coordinates\n */\n private findClosestEditorElement = (coords: {\n clientX: number;\n clientY: number;\n }) => {\n // Get all editor elements in the document\n const editors = Array.from(this.pmView.root.querySelectorAll(\".bn-editor\"));\n\n if (editors.length === 0) {\n return null;\n }\n\n // Find the editor with the smallest distance to the coordinates\n let closestEditor = editors[0];\n let minDistance = Number.MAX_VALUE;\n\n editors.forEach((editor) => {\n const rect = editor\n .querySelector(\".bn-block-group\")!\n .getBoundingClientRect();\n\n const distanceX =\n coords.clientX < rect.left\n ? rect.left - coords.clientX\n : coords.clientX > rect.right\n ? coords.clientX - rect.right\n : 0;\n\n const distanceY =\n coords.clientY < rect.top\n ? rect.top - coords.clientY\n : coords.clientY > rect.bottom\n ? coords.clientY - rect.bottom\n : 0;\n\n const distance = Math.sqrt(\n Math.pow(distanceX, 2) + Math.pow(distanceY, 2),\n );\n\n if (distance < minDistance) {\n minDistance = distance;\n closestEditor = editor;\n }\n });\n\n return {\n element: closestEditor,\n distance: minDistance,\n };\n };\n\n /**\n * This dragover event handler listens at the document level,\n * and is trying to handle dragover events for all editors.\n *\n * It specifically is trying to handle the following cases:\n * - If the dragover event is within the bounds of any editor, then it does nothing\n * - If the dragover event is outside the bounds of any editor, but close enough (within DISTANCE_TO_CONSIDER_EDITOR_BOUNDS) to the closest editor,\n * then it dispatches a synthetic dragover event to the closest editor (which will trigger the drop-cursor to be shown on that editor)\n * - If the dragover event is outside the bounds of the current editor, then it will dispatch a synthetic dragleave event to the current editor\n * (which will trigger the drop-cursor to be removed from the current editor)\n *\n * The synthetic event is a necessary evil because we do not control prosemirror-dropcursor to be able to show the drop-cursor within the range we want\n */\n onDragOver = (event: DragEvent) => {\n if ((event as any).synthetic) {\n return;\n }\n\n // Relevance gate: Only handle drags that belong to BlockNote\n // This prevents interference with external drag-and-drop libraries\n // by avoiding calls to closeDropCursor() for non-BlockNote drags\n const isBlockNoteDrag =\n this.pmView.dragging !== null ||\n this.isDragOrigin ||\n event.dataTransfer?.types.includes(\"blocknote/html\") ||\n (event.target instanceof Node && this.pmView.dom.contains(event.target));\n\n if (!isBlockNoteDrag) {\n // Not a BlockNote-related drag, return early without any processing\n return;\n }\n\n const dragEventContext = this.getDragEventContext(event);\n\n if (!dragEventContext || !dragEventContext.isDropPoint) {\n // This is not a drag event that we are interested in\n // so, we close the drop-cursor\n this.closeDropCursor();\n return;\n }\n\n if (\n dragEventContext.isDropPoint &&\n !dragEventContext.isDropWithinEditorBounds\n ) {\n // we are the drop point, but the drag over event is not within the bounds of this editor instance\n // so, we need to dispatch an event that is in the bounds of this editor instance\n this.dispatchSyntheticEvent(event);\n }\n };\n\n /**\n * Closes the drop-cursor for the current editor\n */\n private closeDropCursor = () => {\n const evt = new Event(\"dragleave\", { bubbles: false });\n // It needs to be synthetic, so we don't accidentally think it is a real dragend event\n (evt as any).synthetic = true;\n // We dispatch the event to the current editor, so that the drop-cursor is removed for it\n this.pmView.dom.dispatchEvent(evt);\n };\n\n /**\n * It is surprisingly difficult to determine the information we need to know about a drag event\n *\n * This function is trying to determine the following:\n * - Whether the current editor instance is the drop point\n * - Whether the current editor instance is the drag origin\n * - Whether the drop event is within the bounds of the current editor instance\n */\n getDragEventContext = (event: DragEvent) => {\n // Relevance gate: Only handle drags that belong to BlockNote\n // Check if at least one of the following is true:\n // 1. ProseMirror drag started in an editor\n // 2. Side menu drag\n // 3. BlockNote-specific data type in the drag\n // 4. (optional stricter mode) Event target is inside this editor\n const isBlockNoteDrag =\n this.pmView.dragging !== null ||\n this.isDragOrigin ||\n event.dataTransfer?.types.includes(\"blocknote/html\") ||\n (event.target instanceof Node && this.pmView.dom.contains(event.target));\n\n if (!isBlockNoteDrag) {\n // Not a BlockNote-related drag, return early\n return undefined;\n }\n\n // We need to check if there is text content that is being dragged (select some text & just drag it)\n const textContentIsBeingDragged =\n !event.dataTransfer?.types.includes(\"blocknote/html\") &&\n !!this.pmView.dragging;\n // This is the side menu drag from this plugin\n const sideMenuIsBeingDragged = !!this.isDragOrigin;\n // Tells us that the current editor instance has a drag ongoing (either text or side menu)\n const isDragOrigin = textContentIsBeingDragged || sideMenuIsBeingDragged;\n\n // Tells us which editor instance is the closest to the drag event (whether or not it is actually reasonably close)\n const closestEditor = this.findClosestEditorElement(event);\n\n // We arbitrarily decide how far is \"too far\" from the closest editor to be considered a drop point\n if (\n !closestEditor ||\n closestEditor.distance > DISTANCE_TO_CONSIDER_EDITOR_BOUNDS\n ) {\n // we are too far from the closest editor, or no editor was found\n return undefined;\n }\n\n // We check if the closest editor is the same as the current editor instance (which is the drop point)\n const isDropPoint = closestEditor.element === this.pmView.dom;\n // We check if the current editor instance is the same as the editor instance that the drag event is happening within\n const isDropWithinEditorBounds =\n isDropPoint && closestEditor.distance === 0;\n\n // We never want to handle drop events that are not related to us\n if (!isDropPoint && !isDragOrigin) {\n // we are not the drop point or drag origin, so not relevant to us\n return undefined;\n }\n\n return {\n isDropPoint,\n isDropWithinEditorBounds,\n isDragOrigin,\n };\n };\n\n /**\n * The drop event handler listens at the document level,\n * and handles drop events for all editors.\n *\n * It specifically handles the following cases:\n * - If we are both the drag origin and drop point:\n * - Let normal drop handling take over\n * - If we are the drop point but not the drag origin:\n * - Collapse selection to prevent PM from deleting unrelated content\n * - If drop event is outside our editor bounds, dispatch synthetic drop event to our editor\n * - If we are the drag origin but not the drop point:\n * - Delete the dragged content from our editor after a delay\n */\n onDrop = (event: DragEvent) => {\n if ((event as any).synthetic) {\n return;\n }\n\n // Relevance gate: Only handle drags that belong to BlockNote\n // This prevents interference with external drag-and-drop libraries\n const isBlockNoteDrag =\n this.pmView.dragging !== null ||\n this.isDragOrigin ||\n event.dataTransfer?.types.includes(\"blocknote/html\") ||\n (event.target instanceof Node && this.pmView.dom.contains(event.target));\n\n if (!isBlockNoteDrag) {\n // Not a BlockNote-related drag, return early without any processing\n return;\n }\n\n const context = this.getDragEventContext(event);\n if (!context) {\n this.closeDropCursor();\n // This is not a drag event that we are interested in\n return;\n }\n const { isDropPoint, isDropWithinEditorBounds, isDragOrigin } = context;\n\n if (!isDropWithinEditorBounds && isDropPoint) {\n // Any time that the drop event is outside of the editor bounds (but still close to an editor instance)\n // We dispatch a synthetic event that is in the bounds of the editor instance, to have the correct drop point\n this.dispatchSyntheticEvent(event);\n }\n\n if (isDropPoint) {\n // The current instance is the drop point\n\n if (this.pmView.dragging) {\n // Do not collapse selection when text content is being dragged\n return;\n }\n // Because the editor selection is unrelated to the dragged content, we\n // don't want PM to delete its content. Therefore, we collapse the\n // selection.\n this.pmView.dispatch(\n this.pmView.state.tr.setSelection(\n TextSelection.create(\n this.pmView.state.tr.doc,\n this.pmView.state.tr.selection.anchor,\n ),\n ),\n );\n return;\n } else if (isDragOrigin) {\n // The current instance is the drag origin, but not the drop point\n // our content got dropped somewhere else\n\n // Because the editor from which the block originates doesn't get a drop\n // event on it, PM doesn't delete its selected content. Therefore, we\n // need to do so manually.\n //\n // Note: Deleting the selected content from the editor from which the\n // block originates, may change its height. This can cause the position of\n // the editor in which the block is being dropping to shift, before it\n // can handle the drop event. That in turn can cause the drop to happen\n // somewhere other than the user intended. To get around this, we delay\n // deleting the selected content until all editors have had the chance to\n // handle the event.\n setTimeout(\n () => this.pmView.dispatch(this.pmView.state.tr.deleteSelection()),\n 0,\n );\n return;\n }\n };\n\n onDragEnd = (event: DragEvent) => {\n if ((event as any).synthetic) {\n return;\n }\n // When the user starts dragging a block, `view.dragging` is set on all\n // BlockNote editors. However, when the drag ends, only the editor that the\n // drag originated in automatically clears `view.dragging`. Therefore, we\n // have to manually clear it on all editors.\n this.pmView.dragging = null;\n };\n\n onKeyDown = (_event: KeyboardEvent) => {\n if (this.state?.show && this.editor.isFocused()) {\n // Typing in editor should hide side menu\n this.state.show = false;\n this.emitUpdate(this.state);\n }\n };\n\n onMouseMove = (event: MouseEvent) => {\n if (this.menuFrozen) {\n return;\n }\n\n // Synthetic mousemove events created via `new Event(\"mousemove\")` (e.g.\n // dispatched by browser extensions) have no `clientX`/`clientY`, which\n // would make `elementsFromPoint` throw on the resulting non-finite\n // coordinates.\n if (!Number.isFinite(event.clientX) || !Number.isFinite(event.clientY)) {\n return;\n }\n\n this.mousePos = { x: event.clientX, y: event.clientY };\n\n // We want the full area of the editor to check if the cursor is hovering\n // above it though.\n const editorOuterBoundingBox = this.pmView.dom.getBoundingClientRect();\n const cursorWithinEditor =\n this.mousePos.x > editorOuterBoundingBox.left &&\n this.mousePos.x < editorOuterBoundingBox.right &&\n this.mousePos.y > editorOuterBoundingBox.top &&\n this.mousePos.y < editorOuterBoundingBox.bottom;\n\n // Doesn't update if the mouse hovers an element that's over the editor but\n // isn't a part of it or the side menu.\n if (\n // Cursor is within the editor area\n cursorWithinEditor &&\n // An element is hovered\n event &&\n event.target &&\n // Element is outside this editor and its portaled UI\n !this.editor.isWithinEditor(event.target as HTMLElement)\n ) {\n if (this.state?.show) {\n this.state.show = false;\n this.emitUpdate(this.state);\n }\n\n return;\n }\n\n this.updateStateFromMousePos();\n };\n\n private dispatchSyntheticEvent(event: DragEvent) {\n const evt = new Event(event.type as \"dragover\", event) as any;\n const dropPointBoundingBox = (\n this.pmView.dom.firstChild as HTMLElement\n ).getBoundingClientRect();\n evt.clientX = event.clientX;\n evt.clientY = event.clientY;\n\n evt.clientX = Math.min(\n Math.max(event.clientX, dropPointBoundingBox.left),\n dropPointBoundingBox.left + dropPointBoundingBox.width,\n );\n evt.clientY = Math.min(\n Math.max(event.clientY, dropPointBoundingBox.top),\n dropPointBoundingBox.top + dropPointBoundingBox.height,\n );\n\n evt.dataTransfer = event.dataTransfer;\n evt.preventDefault = () => event.preventDefault();\n evt.synthetic = true; // prevent recursion\n this.pmView.dom.dispatchEvent(evt);\n }\n\n // Needed in cases where the editor state updates without the mouse cursor\n // moving, as some state updates can require a side menu update. For example,\n // adding a button to the side menu which removes the block can cause the\n // block below to jump up into the place of the removed block when clicked,\n // allowing the user to click the button again without moving the cursor. This\n // would otherwise not update the side menu, and so clicking the button again\n // would attempt to remove the same block again, causing an error.\n update(_view: EditorView, prevState: EditorState) {\n const docChanged = !prevState.doc.eq(this.pmView.state.doc);\n if (docChanged && this.state?.show) {\n this.updateStateFromMousePos();\n }\n }\n\n destroy() {\n if (this.state?.show) {\n this.state.show = false;\n this.emitUpdate(this.state);\n }\n this.pmView.root.removeEventListener(\n \"mousemove\",\n this.onMouseMove as EventListener,\n true,\n );\n this.pmView.root.removeEventListener(\n \"dragstart\",\n this.onDragStart as EventListener,\n );\n this.pmView.root.removeEventListener(\n \"dragover\",\n this.onDragOver as EventListener,\n );\n this.pmView.root.removeEventListener(\n \"drop\",\n this.onDrop as EventListener,\n true,\n );\n this.pmView.root.removeEventListener(\n \"dragend\",\n this.onDragEnd as EventListener,\n true,\n );\n this.pmView.root.removeEventListener(\n \"keydown\",\n this.onKeyDown as EventListener,\n true,\n );\n }\n}\n\nexport const sideMenuPluginKey = new PluginKey(\"SideMenuPlugin\");\n\nexport const SideMenuExtension = createExtension(({ editor }) => {\n let view: SideMenuView<any, any, any> | undefined;\n const store = createStore<SideMenuState<any, any, any> | undefined>(\n undefined,\n );\n\n return {\n key: \"sideMenu\",\n store,\n prosemirrorPlugins: [\n new Plugin({\n key: sideMenuPluginKey,\n view: (editorView) => {\n view = new SideMenuView(editor, editorView, (state) => {\n // TODO: Without spreading the state, in some cases like toggling\n // `show`, this doesn't trigger an update.\n store.setState({ ...state });\n });\n return view;\n },\n }),\n ],\n\n /**\n * Handles drag & drop events for blocks.\n */\n blockDragStart(\n event: { dataTransfer: DataTransfer | null; clientY: number },\n block: Block<any, any, any>,\n ) {\n if (view) {\n view.isDragOrigin = true;\n }\n dragStart(event, block, editor);\n },\n\n /**\n * Handles drag & drop events for blocks.\n */\n blockDragEnd() {\n unsetDragImage(editor.prosemirrorView.root);\n if (view) {\n view.isDragOrigin = false;\n }\n\n editor.blur();\n },\n\n /**\n * Whether the side menu is currently frozen (e.g. because the drag handle\n * menu is open).\n */\n get menuFrozen() {\n return view!.menuFrozen;\n },\n\n /**\n * Freezes the side menu. When frozen, the side menu will stay\n * attached to the same block regardless of which block is hovered by the\n * mouse cursor.\n */\n freezeMenu() {\n view!.menuFrozen = true;\n view!.state!.show = true;\n view!.emitUpdate(view!.state!);\n },\n\n /**\n * Unfreezes the side menu. When frozen, the side menu will stay\n * attached to the same block regardless of which block is hovered by the\n * mouse cursor.\n */\n unfreezeMenu() {\n view!.menuFrozen = false;\n view!.state!.show = false;\n view!.emitUpdate(view!.state!);\n },\n\n /**\n * Hides the side menu unless it is currently frozen (e.g. the drag\n * handle menu is open). Used to dismiss the menu on scroll without\n * interfering with open submenus.\n */\n hideMenuIfNotFrozen() {\n if (!view!.menuFrozen && view!.state?.show) {\n view!.state.show = false;\n view!.emitUpdate(view!.state!);\n }\n },\n } as const;\n});\n","import { TextSelection } from \"prosemirror-state\";\n\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor\";\nimport {\n createExtension,\n createStore,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { Block } from \"../../blocks/index.js\";\n\n/**\n * A single editor-wide extension that drives the source popup for blocks that\n * render a preview. Which blocks it activates on is decided by each spec's\n * `meta.hasPreview` flag, so individual blocks opt in rather than the extension\n * being configured with a block type.\n *\n * The extension is registered once (it's a default extension) and is a no-op\n * when no block declares `meta.hasPreview`.\n */\nexport const SourceBlockWithPreviewExtension = createExtension(\n ({ editor }: { editor: BlockNoteEditor<any> }) => {\n const store = createStore<{\n popupOpen: string | undefined;\n selected: string | undefined;\n }>({\n popupOpen: undefined,\n selected: undefined,\n });\n\n // A block has a preview iff its spec's implementation declares\n // `meta.hasPreview` (read from the spec, like the syntax-highlighting\n // extension reads `meta.highlight`).\n const blockHasPreview = (block: Block<any, any, any>) =>\n !!editor.schema.blockSpecs[block.type]?.implementation?.meta?.hasPreview;\n\n const handleArrow =\n (direction: \"prev\" | \"next\") =>\n ({ editor }: { editor: BlockNoteEditor<any> }) => {\n const { block, prevBlock, nextBlock } = editor.getTextCursorPosition();\n if (!blockHasPreview(block) || store.state.popupOpen === block.id) {\n return false;\n }\n\n const targetBlock = direction === \"prev\" ? prevBlock : nextBlock;\n if (!targetBlock) {\n return false;\n }\n\n editor.setTextCursorPosition(\n targetBlock.id,\n direction === \"prev\" ? \"end\" : \"start\",\n );\n\n return true;\n };\n\n return {\n key: \"sourceBlockWithPreview\",\n store,\n keyboardShortcuts: {\n // Toggles the popup. This may be overridden by `hardBreakShortcut`.\n Enter: ({ editor }) => {\n const { block } = editor.getTextCursorPosition();\n if (!blockHasPreview(block)) {\n return false;\n }\n\n if (\n store.state.popupOpen === block.id &&\n editor.schema.blockSpecs[block.type]?.implementation?.meta\n ?.hardBreakShortcut === \"enter\"\n ) {\n const view = editor.prosemirrorView!;\n view.dispatch(view.state.tr.insertText(\"\\n\"));\n\n return true;\n }\n\n editor.setTextCursorPosition(block.id, \"end\");\n store.setState((state) => ({\n ...state,\n popupOpen:\n store.state.popupOpen === block.id ? undefined : block.id,\n }));\n\n return true;\n },\n // Closes the popup.\n Escape: ({ editor }) => {\n const { block } = editor.getTextCursorPosition();\n if (!blockHasPreview(block) || store.state.popupOpen !== block.id) {\n return false;\n }\n\n editor.setTextCursorPosition(block.id, \"end\");\n\n store.setState((state) => ({ ...state, popupOpen: undefined }));\n\n return true;\n },\n // While the popup is open, selects the whole source instead of the\n // whole document.\n \"Mod-a\": ({ editor }) => {\n const { block } = editor.getTextCursorPosition();\n if (!blockHasPreview(block) || store.state.popupOpen !== block.id) {\n return false;\n }\n\n const view = editor.prosemirrorView!;\n const { $from } = view.state.selection;\n if ($from.parent.type.name !== block.type) {\n return false;\n }\n\n view.dispatch(\n view.state.tr.setSelection(\n TextSelection.create(view.state.doc, $from.start(), $from.end()),\n ),\n );\n\n return true;\n },\n // While the popup is closed, moves the selection straight to the previous/next block\n // instead of into the (hidden) source.\n ArrowUp: handleArrow(\"prev\"),\n ArrowLeft: handleArrow(\"prev\"),\n ArrowDown: handleArrow(\"next\"),\n ArrowRight: handleArrow(\"next\"),\n },\n mount: ({ dom, signal }) => {\n // Closes the popup when the selection leaves the block that owns it and tracks which block\n // the selection is in.\n const unsubscribeSelectionChange = editor.onSelectionChange(() => {\n const { block } = editor.getTextCursorPosition();\n\n const selected = blockHasPreview(block) ? block.id : undefined;\n const popupOpen =\n store.state.popupOpen && store.state.popupOpen !== block.id\n ? undefined\n : store.state.popupOpen;\n\n if (\n selected === store.state.selected &&\n popupOpen === store.state.popupOpen\n ) {\n return;\n }\n\n store.setState((state) => ({ ...state, selected, popupOpen }));\n });\n signal.addEventListener(\"abort\", unsubscribeSelectionChange);\n\n // While the popup is closed, prevents editing of the (hidden) source. Handled here rather\n // than in `keyboardShortcuts` as it needs to match any text-input key, which a keymap\n // can't express.\n const handleKeyDown = (event: KeyboardEvent) => {\n if (!editor.isEditable) {\n return;\n }\n\n const { block } = editor.getTextCursorPosition();\n if (!blockHasPreview(block) || store.state.popupOpen === block.id) {\n return;\n }\n\n if (event.key === \"Backspace\" || event.key === \"Delete\") {\n event.preventDefault();\n event.stopImmediatePropagation();\n editor.removeBlocks([block.id]);\n\n return;\n }\n\n if (\n (event.key.length === 1 && !event.ctrlKey && !event.metaKey) ||\n event.key === \"Tab\"\n ) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n dom.addEventListener(\"keydown\", handleKeyDown, {\n capture: true,\n signal,\n });\n\n const handleBlur = () =>\n store.setState((state) => ({ ...state, popupOpen: undefined }));\n dom.addEventListener(\"blur\", handleBlur, { capture: true, signal });\n },\n };\n },\n);\n","import { Selection, TextSelection } from \"prosemirror-state\";\n\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor\";\nimport {\n createExtension,\n createStore,\n} from \"../../editor/BlockNoteExtension.js\";\n\n/**\n * Inline-content counterpart of {@link SourceBlockWithPreviewExtension}. A\n * single editor-wide extension that drives the source popup for inline content\n * that renders a preview. Which inline content it activates on is decided by\n * each spec's `meta.hasPreview` flag, so individual inline content opts in\n * rather than the extension being configured with a type.\n *\n * Unlike the block version, the popup isn't toggled with a separate state flag:\n * it's open exactly when the selection is inside the inline content's source.\n * The store therefore only tracks which inline content (by its position) holds\n * the selection - moving the selection in opens its popup, moving it out closes\n * it. Since the source popup is always laid out (just hidden via opacity), the\n * cursor can navigate into and out of it with the arrow keys as usual.\n *\n * The extension is registered once (it's a default extension) and is a no-op\n * when no inline content declares `meta.hasPreview`.\n */\nexport const SourceInlineContentWithPreviewExtension = createExtension(\n ({ editor }: { editor: BlockNoteEditor<any, any, any> }) => {\n const store = createStore<{\n selected: number | undefined;\n }>({\n selected: undefined,\n });\n\n // Inline content has a preview iff its spec's implementation declares\n // `meta.hasPreview`.\n const nodeHasPreview = (nodeName: string) =>\n !!editor.schema.inlineContentSpecs[nodeName]?.implementation?.meta\n ?.hasPreview;\n\n // Moves the selection out of the inline content, to just `\"before\"` or\n // `\"after\"` it, which closes the popup via the selection-change handler\n // below. Lets the keyboard commit-and-exit the source the same way arrowing\n // past its edge does, keeps Enter from splitting the block while editing the\n // source, and lets the up/down arrows step out of the source rather than\n // staying trapped inside it.\n const moveSelectionOut =\n (direction: \"before\" | \"after\") =>\n ({ editor }: { editor: BlockNoteEditor<any, any, any> }) => {\n const { $from } = editor.prosemirrorState.selection;\n const node = $from.node();\n if (!nodeHasPreview(node.type.name)) {\n return false;\n }\n\n const view = editor.prosemirrorView!;\n const selection = Selection.near(\n view.state.doc.resolve(\n direction === \"before\" ? $from.before() : $from.after(),\n ),\n direction === \"before\" ? -1 : 1,\n );\n view.dispatch(view.state.tr.setSelection(selection));\n\n return true;\n };\n\n return {\n key: \"sourceInlineContentWithPreview\",\n store,\n keyboardShortcuts: {\n Enter: moveSelectionOut(\"after\"),\n \"Shift-Enter\": moveSelectionOut(\"after\"),\n Escape: moveSelectionOut(\"after\"),\n ArrowUp: moveSelectionOut(\"before\"),\n ArrowDown: moveSelectionOut(\"after\"),\n // While editing the source, selects the whole source instead of the\n // whole document.\n \"Mod-a\": ({ editor }) => {\n const { $from } = editor.prosemirrorState.selection;\n if (!nodeHasPreview($from.node().type.name)) {\n return false;\n }\n\n const view = editor.prosemirrorView!;\n view.dispatch(\n view.state.tr.setSelection(\n TextSelection.create(view.state.doc, $from.start(), $from.end()),\n ),\n );\n\n return true;\n },\n },\n mount: ({ dom, signal }) => {\n // The popup is open exactly when the selection is inside the inline\n // content, so we just track which inline content (if any) holds it.\n const unsubscribeSelectionChange = editor.onSelectionChange(() => {\n const { $from } = editor.prosemirrorState.selection;\n const node = $from.node();\n\n store.setState({\n selected: nodeHasPreview(node.type.name)\n ? $from.before()\n : undefined,\n });\n });\n signal.addEventListener(\"abort\", unsubscribeSelectionChange);\n\n // Sets `visibility: hidden` on the popup for a single frame when pressing up/down arrow\n // keys. The popup is normally hidden through `opacity: 0`, which means it's still visible\n // to the browser for navigation. Therefore, the up/down arrows can sometimes move the\n // selection into the popup from unexpected positions, such as on the same line. Setting\n // `visibility: hidden` makes the browser ignore it when determining the new selection.\n // TODO: This is hacky, we should find a cleaner solution.\n const handleVerticalArrow = (event: KeyboardEvent) => {\n if (event.key !== \"ArrowUp\" && event.key !== \"ArrowDown\") {\n return;\n }\n\n // When the selection is already inside a source, leave navigation\n // (moving within or out of it) to the browser as usual.\n const { $from } = editor.prosemirrorState.selection;\n if (nodeHasPreview($from.node().type.name)) {\n return;\n }\n\n dom.classList.add(\"bn-suppress-source-popup-caret\");\n requestAnimationFrame(() =>\n dom.classList.remove(\"bn-suppress-source-popup-caret\"),\n );\n };\n dom.addEventListener(\"keydown\", handleVerticalArrow, {\n capture: true,\n signal,\n });\n\n const handleBlur = () => store.setState({ selected: undefined });\n dom.addEventListener(\"blur\", handleBlur, { capture: true, signal });\n },\n };\n },\n);\n","import type { HighlighterGeneric } from \"@shikijs/types\";\nimport { Parser, createHighlightPlugin } from \"prosemirror-highlight\";\nimport { createParser } from \"prosemirror-highlight/shiki\";\nimport type { SyntaxHighlightingOptions } from \"./SyntaxHighlighting.js\";\nimport { CustomBlockNoteSchema } from \"../../schema/schema.js\";\n\nexport const shikiParserSymbol = Symbol.for(\"blocknote.shikiParser\");\nexport const shikiHighlighterPromiseSymbol = Symbol.for(\n \"blocknote.shikiHighlighterPromise\",\n);\n\n// Languages that represent \"no highlighting\" - skipped without asking Shiki to\n// load a grammar for them.\nconst PLAIN_TEXT_LANGUAGES = [\"text\", \"none\", \"plaintext\", \"txt\"];\n\n/**\n * Creates the syntax highlighting plugin for the given block types, lazily\n * loading the highlighter on first use.\n *\n * Each spec's `meta.highlight` callback resolves a node to a language, which is\n * passed straight to Shiki - it resolves aliases and loads the grammar from its\n * bundle, so any language the provided highlighter bundles can be highlighted.\n */\nexport function lazyShikiPlugin(\n options: SyntaxHighlightingOptions,\n nodeTypes: string[],\n schema: CustomBlockNoteSchema<any, any, any>,\n) {\n const globalThisForShiki = globalThis as {\n [shikiHighlighterPromiseSymbol]?: Promise<HighlighterGeneric<any, any>>;\n [shikiParserSymbol]?: Parser;\n };\n\n let highlighter: HighlighterGeneric<any, any> | undefined;\n let parser: Parser | undefined;\n // Languages the highlighter failed to load (e.g. not in its bundle). Tracked\n // so we don't keep retrying - and re-triggering re-highlights - forever.\n const unsupportedLanguages = new Set<string>();\n const lazyParser: Parser = (parserOptions) => {\n if (!options.createHighlighter) {\n return [];\n }\n if (!highlighter) {\n globalThisForShiki[shikiHighlighterPromiseSymbol] =\n globalThisForShiki[shikiHighlighterPromiseSymbol] ||\n options.createHighlighter();\n\n return globalThisForShiki[shikiHighlighterPromiseSymbol].then(\n (createdHighlighter) => {\n highlighter = createdHighlighter;\n },\n );\n }\n const language = parserOptions.language;\n\n if (\n !language ||\n PLAIN_TEXT_LANGUAGES.includes(language) ||\n unsupportedLanguages.has(language)\n ) {\n return [];\n }\n\n if (!highlighter.getLoadedLanguages().includes(language)) {\n return highlighter.loadLanguage(language as any).catch(() => {\n // The highlighter doesn't bundle this language - give up on it so we\n // don't loop trying to load it on every re-highlight.\n unsupportedLanguages.add(language);\n });\n }\n\n if (!parser) {\n parser =\n globalThisForShiki[shikiParserSymbol] ||\n createParser(highlighter as any, pickThemeOptions(highlighter));\n globalThisForShiki[shikiParserSymbol] = parser;\n }\n\n return parser(parserOptions);\n };\n\n return createHighlightPlugin({\n parser: lazyParser,\n // The highlight plugin only gives us the block content node, so we can only\n // reconstruct the block's `type` and `props` (which is all a spec's\n // `meta.highlight` needs to pick a language).\n languageExtractor: (node) => {\n const nodeShape = {\n type: node.type.name,\n props: node.attrs,\n };\n // search for the node in the blockSpec or inlineContentSpecs\n const spec =\n schema.blockSpecs[nodeShape.type] ||\n schema.inlineContentSpecs[nodeShape.type];\n\n return spec?.implementation?.meta?.highlight?.(nodeShape) ?? undefined;\n },\n nodeTypes,\n });\n}\n\n// If a light and dark theme is added to the highlighter, this function specifies them in\n// `createParser`. This lets us use `--shiki-light` and `--shiki-dark` CSS variables for correct\n// styling for both light & dark editor themes.\nfunction pickThemeOptions(highlighter: HighlighterGeneric<any, any>) {\n const themes = highlighter.getLoadedThemes();\n const light = themes.find((t) => /light/i.test(t));\n const dark = themes.find((t) => /dark/i.test(t));\n\n if (light && dark) {\n return { themes: { light, dark }, defaultColor: false as const };\n }\n\n return undefined;\n}\n","import type { HighlighterGeneric } from \"@shikijs/types\";\nimport {\n createExtension,\n ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { lazyShikiPlugin } from \"./shiki.js\";\nimport {\n CustomInlineContentConfig,\n InlineContentSpec,\n LooseBlockSpec,\n} from \"../../schema/index.js\";\n\nexport type SyntaxHighlightingOptions = {\n /**\n * Creates the Shiki highlighter used for syntax highlighting. Can be\n * asynchronous, so the highlighter is only loaded once it's first needed.\n *\n * When omitted, content renders without syntax highlighting.\n */\n createHighlighter: () => Promise<HighlighterGeneric<any, any>>;\n};\n\n/**\n * Collects the node type names that should be syntax-highlighted from a schema's\n * block and inline-content specs.\n *\n * A spec is a candidate when it has a `meta.highlight` callback (which decides\n * the language) AND the node actually holds editable text. Block and\n * inline-content specs use different `content` value spaces, so \"editable text\"\n * means `content === \"plain\"` for both blocks (code/math blocks) and inline\n * content (inline math) - both hold plain text - hence the two are filtered\n * separately.\n *\n * Inline content (e.g. inline math) is highlighted too: `prosemirror-highlight`\n * collects nodes by `node.inlineContent` since v0.15.3\n * (https://github.com/ocavue/prosemirror-highlight/pull/137), so inline nodes\n * holding inline content are visited alongside text blocks.\n */\nexport function collectHighlightNodeTypes(schema: {\n blockSpecs: Record<string, unknown>;\n inlineContentSpecs: Record<string, unknown>;\n}): string[] {\n const blockNodeTypes = Object.values(schema.blockSpecs)\n .filter(\n (blockSpec): blockSpec is LooseBlockSpec =>\n typeof (blockSpec as LooseBlockSpec)?.config === \"object\" &&\n (blockSpec as LooseBlockSpec).config.content === \"plain\" &&\n !!(blockSpec as LooseBlockSpec).implementation?.meta?.highlight,\n )\n .map((blockSpec) => blockSpec.config.type);\n\n const inlineContentNodeTypes = Object.values(schema.inlineContentSpecs)\n .filter(\n (\n inlineContentSpec,\n ): inlineContentSpec is InlineContentSpec<CustomInlineContentConfig> =>\n typeof (\n inlineContentSpec as InlineContentSpec<CustomInlineContentConfig>\n )?.config === \"object\" &&\n (inlineContentSpec as InlineContentSpec<CustomInlineContentConfig>)\n .config.content === \"plain\" &&\n !!(inlineContentSpec as InlineContentSpec<CustomInlineContentConfig>)\n .implementation?.meta?.highlight,\n )\n .map((inlineContentSpec) => inlineContentSpec.config.type);\n\n return [...blockNodeTypes, ...inlineContentNodeTypes];\n}\n\n/**\n * A single editor-wide extension that syntax-highlights block and inline-content\n * content. Which nodes get highlighted (and as which language) is decided by\n * each spec's `meta.highlight` callback, so individual specs declare their own\n * language rather than the extension configuring them.\n *\n * Highlighting is opt-in: the user adds this extension to the editor's\n * `extensions` (configured with a `createHighlighter`) to enable it. When it's\n * absent, content renders as plain text.\n */\nexport const SyntaxHighlightingExtension = createExtension(\n ({ editor, options }: ExtensionOptions<SyntaxHighlightingOptions>) => {\n const nodeTypes = collectHighlightNodeTypes(editor.schema);\n\n return {\n key: \"syntaxHighlighting\",\n prosemirrorPlugins: [lazyShikiPlugin(options, nodeTypes, editor.schema)],\n };\n },\n);\n","import type { Emoji, EmojiMartData } from \"@emoji-mart/data\";\n\nimport { defaultInlineContentSchema } from \"../../blocks/defaultBlocks.js\";\nimport { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport {\n BlockSchema,\n InlineContentSchema,\n StyleSchema,\n} from \"../../schema/index.js\";\nimport { DefaultGridSuggestionItem } from \"./DefaultGridSuggestionItem.js\";\n\n// Temporary fix for https://github.com/missive/emoji-mart/pull/929\nlet emojiLoadingPromise:\n | Promise<{\n emojiMart: typeof import(\"emoji-mart\");\n emojiData: EmojiMartData;\n }>\n | undefined;\n\nasync function loadEmojiMart() {\n if (emojiLoadingPromise) {\n return emojiLoadingPromise;\n }\n\n emojiLoadingPromise = (async () => {\n // load dynamically because emoji-mart doesn't specify type: module and breaks in nodejs\n const [emojiMartModule, emojiDataModule] = await Promise.all([\n import(\"emoji-mart\"),\n // use a dynamic import to encourage bundle-splitting\n // and a smaller initial client bundle size\n import(\"@emoji-mart/data\"),\n ]);\n\n const emojiMart =\n \"default\" in emojiMartModule ? emojiMartModule.default : emojiMartModule;\n const emojiData =\n \"default\" in emojiDataModule\n ? (emojiDataModule.default as EmojiMartData)\n : (emojiDataModule as EmojiMartData);\n\n await emojiMart.init({ data: emojiData });\n\n return { emojiMart, emojiData };\n })();\n\n return emojiLoadingPromise;\n}\n\nexport async function getDefaultEmojiPickerItems<\n BSchema extends BlockSchema,\n I extends InlineContentSchema,\n S extends StyleSchema,\n>(\n editor: BlockNoteEditor<BSchema, I, S>,\n query: string,\n): Promise<DefaultGridSuggestionItem[]> {\n if (\n !(\"text\" in editor.schema.inlineContentSchema) ||\n editor.schema.inlineContentSchema[\"text\"] !==\n defaultInlineContentSchema[\"text\"]\n ) {\n return [];\n }\n\n const { emojiData, emojiMart } = await loadEmojiMart();\n\n const emojisToShow =\n query.trim() === \"\"\n ? Object.values(emojiData.emojis)\n : ((await emojiMart!.SearchIndex.search(query)) as Emoji[]);\n\n return emojisToShow.map((emoji) => ({\n id: emoji.skins[0].native,\n onItemClick: () => editor.insertInlineContent(emoji.skins[0].native + \" \"),\n }));\n}\n","import { EditorState, Plugin, PluginKey, PluginView } from \"prosemirror-state\";\nimport {\n CellSelection,\n addColumnAfter,\n addColumnBefore,\n addRowAfter,\n addRowBefore,\n deleteColumn,\n deleteRow,\n mergeCells,\n splitCell,\n} from \"prosemirror-tables\";\nimport { Decoration, DecorationSet, EditorView } from \"prosemirror-view\";\nimport {\n RelativeCellIndices,\n addRowsOrColumns,\n areInSameColumn,\n canColumnBeDraggedInto,\n canRowBeDraggedInto,\n cropEmptyRowsOrColumns,\n getCellsAtColumnHandle,\n getCellsAtRowHandle,\n getDimensionsOfTable,\n moveColumn,\n moveRow,\n} from \"../../api/blockManipulation/tables/tables.js\";\nimport { nodeToBlock } from \"../../api/nodeConversions/nodeToBlock.js\";\nimport { getNodeById } from \"../../api/nodeUtil.js\";\nimport {\n editorHasBlockWithType,\n isTableCellNode,\n isTableCellSelection,\n} from \"../../blocks/defaultBlockTypeGuards.js\";\nimport { DefaultBlockSchema } from \"../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport {\n createExtension,\n createStore,\n} from \"../../editor/BlockNoteExtension.js\";\nimport {\n BlockFromConfigNoChildren,\n BlockSchemaWithBlock,\n} from \"../../schema/index.js\";\nimport { getDraggableBlockFromElement } from \"../getDraggableBlockFromElement.js\";\n\nlet dragImageElement: HTMLElement | undefined;\n\n// TODO consider switching this to jotai, it is a bit messy and noisy\nexport type TableHandlesState = {\n show: boolean;\n showAddOrRemoveRowsButton: boolean;\n showAddOrRemoveColumnsButton: boolean;\n referencePosCell: DOMRect | undefined;\n referencePosTable: DOMRect;\n\n block: BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>;\n colIndex: number | undefined;\n rowIndex: number | undefined;\n\n draggingState:\n | {\n draggedCellOrientation: \"row\" | \"col\";\n originalIndex: number;\n mousePos: number;\n }\n | undefined;\n\n widgetContainer: HTMLElement | undefined;\n};\n\nfunction setHiddenDragImage(rootEl: Document | ShadowRoot) {\n if (dragImageElement) {\n return;\n }\n\n dragImageElement = document.createElement(\"div\");\n dragImageElement.innerHTML = \"_\";\n dragImageElement.style.opacity = \"0\";\n dragImageElement.style.height = \"1px\";\n dragImageElement.style.width = \"1px\";\n if (rootEl instanceof Document) {\n rootEl.body.appendChild(dragImageElement);\n } else {\n rootEl.appendChild(dragImageElement);\n }\n}\n\nfunction unsetHiddenDragImage(rootEl: Document | ShadowRoot) {\n if (dragImageElement) {\n if (rootEl instanceof Document) {\n rootEl.body.removeChild(dragImageElement);\n } else {\n rootEl.removeChild(dragImageElement);\n }\n dragImageElement = undefined;\n }\n}\n\nfunction getChildIndex(node: Element) {\n return Array.prototype.indexOf.call(node.parentElement!.childNodes, node);\n}\n\n// Finds the DOM element corresponding to the table cell that the target element\n// is currently in. If the target element is not in a table cell, returns null.\nfunction domCellAround(target: Element) {\n let currentTarget: Element | undefined = target;\n while (\n currentTarget &&\n currentTarget.nodeName !== \"TD\" &&\n currentTarget.nodeName !== \"TH\" &&\n !currentTarget.classList.contains(\"tableWrapper\")\n ) {\n if (currentTarget.classList.contains(\"ProseMirror\")) {\n return undefined;\n }\n const parent: ParentNode | null = currentTarget.parentNode;\n\n if (!parent || !(parent instanceof Element)) {\n return undefined;\n }\n currentTarget = parent;\n }\n\n return currentTarget.nodeName === \"TD\" || currentTarget.nodeName === \"TH\"\n ? {\n type: \"cell\",\n domNode: currentTarget,\n tbodyNode: currentTarget.closest(\"tbody\"),\n }\n : {\n type: \"wrapper\",\n domNode: currentTarget,\n tbodyNode: currentTarget.querySelector(\"tbody\"),\n };\n}\n\n// Hides elements in the DOMwith the provided class names.\nfunction hideElements(selector: string, rootEl: Document | ShadowRoot) {\n const elementsToHide = rootEl.querySelectorAll(selector);\n\n for (let i = 0; i < elementsToHide.length; i++) {\n (elementsToHide[i] as HTMLElement).style.visibility = \"hidden\";\n }\n}\n\nexport class TableHandlesView implements PluginView {\n public state?: TableHandlesState;\n public emitUpdate: () => void;\n\n public tableId: string | undefined;\n public tablePos: number | undefined;\n public tableElement: HTMLElement | undefined;\n\n public menuFrozen = false;\n\n public mouseState: \"up\" | \"down\" | \"selecting\" = \"up\";\n\n public prevWasEditable: boolean | null = null;\n\n constructor(\n private readonly editor: BlockNoteEditor<\n BlockSchemaWithBlock<\"table\", DefaultBlockSchema[\"table\"]>,\n any,\n any\n >,\n private readonly pmView: EditorView,\n emitUpdate: (state: TableHandlesState | undefined) => void,\n ) {\n this.emitUpdate = () => {\n emitUpdate(this.state);\n };\n\n pmView.dom.addEventListener(\"mousemove\", this.mouseMoveHandler);\n pmView.dom.addEventListener(\"mousedown\", this.viewMousedownHandler);\n window.addEventListener(\"mouseup\", this.mouseUpHandler);\n\n pmView.root.addEventListener(\n \"dragover\",\n this.dragOverHandler as EventListener,\n );\n pmView.root.addEventListener(\n \"drop\",\n this.dropHandler as unknown as EventListener,\n );\n }\n\n viewMousedownHandler = () => {\n this.mouseState = \"down\";\n };\n\n mouseUpHandler = (event: MouseEvent) => {\n this.mouseState = \"up\";\n this.mouseMoveHandler(event);\n };\n\n mouseMoveHandler = (event: MouseEvent) => {\n if (this.menuFrozen) {\n return;\n }\n\n if (this.mouseState === \"selecting\") {\n return;\n }\n\n if (\n !(event.target instanceof Element) ||\n !this.pmView.dom.contains(event.target)\n ) {\n return;\n }\n\n const target = domCellAround(event.target);\n\n if (\n target?.type === \"cell\" &&\n this.mouseState === \"down\" &&\n !this.state?.draggingState\n ) {\n // hide draghandles when selecting text as they could be in the way of the user\n this.mouseState = \"selecting\";\n\n if (this.state?.show) {\n this.state.show = false;\n this.state.showAddOrRemoveRowsButton = false;\n this.state.showAddOrRemoveColumnsButton = false;\n this.emitUpdate();\n }\n return;\n }\n\n if (!target || !this.editor.isEditable) {\n if (this.state?.show) {\n this.state.show = false;\n this.state.showAddOrRemoveRowsButton = false;\n this.state.showAddOrRemoveColumnsButton = false;\n this.emitUpdate();\n }\n return;\n }\n\n if (!target.tbodyNode) {\n return;\n }\n\n const tableRect = target.tbodyNode.getBoundingClientRect();\n\n const blockEl = getDraggableBlockFromElement(target.domNode, this.pmView);\n if (!blockEl) {\n return;\n }\n this.tableElement = blockEl.node;\n\n let tableBlock:\n | BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>\n | undefined;\n\n const { pmNodeInfo, doc } = this.editor.transact((tr) => ({\n pmNodeInfo: getNodeById(blockEl.id, tr.doc),\n doc: tr.doc,\n }));\n if (!pmNodeInfo) {\n throw new Error(`Block with ID ${blockEl.id} not found`);\n }\n\n const block = nodeToBlock(\n pmNodeInfo.node,\n doc,\n ) as unknown as BlockFromConfigNoChildren<\n DefaultBlockSchema[\"table\"],\n any,\n any\n >;\n\n if (editorHasBlockWithType(this.editor, \"table\")) {\n this.tablePos = pmNodeInfo.posBeforeNode + 1;\n tableBlock = block;\n }\n\n if (!tableBlock) {\n return;\n }\n\n this.tableId = blockEl.id;\n const widgetContainer = target.domNode\n .closest(\".tableWrapper\")\n ?.querySelector(\".table-widgets-container\") as HTMLElement;\n\n if (target?.type === \"wrapper\") {\n // if we're just to the right or below the table, show the extend buttons\n // (this is a bit hacky. It would probably be cleaner to render the extend buttons in the Table NodeView instead)\n const belowTable =\n event.clientY >= tableRect.bottom - 1 && // -1 to account for fractions of pixels in \"bottom\"\n event.clientY < tableRect.bottom + 20;\n const toRightOfTable =\n event.clientX >= tableRect.right - 1 &&\n event.clientX < tableRect.right + 20;\n\n const hideHandles =\n // always hide handles when the actively hovered table changed\n this.state?.block?.id !== tableBlock.id ||\n // make sure we don't hide existing handles (keep col / row index) when\n // we're hovering just above or to the right of a table\n event.clientX > tableRect.right ||\n event.clientY > tableRect.bottom;\n\n this.state = {\n ...this.state!,\n show: true,\n showAddOrRemoveRowsButton: belowTable,\n showAddOrRemoveColumnsButton: toRightOfTable,\n referencePosTable: tableRect,\n block: tableBlock,\n widgetContainer,\n colIndex: hideHandles ? undefined : this.state?.colIndex,\n rowIndex: hideHandles ? undefined : this.state?.rowIndex,\n referencePosCell: hideHandles\n ? undefined\n : this.state?.referencePosCell,\n };\n } else {\n const colIndex = getChildIndex(target.domNode);\n const rowIndex = getChildIndex(target.domNode.parentElement!);\n const cellRect = target.domNode.getBoundingClientRect();\n\n if (\n this.state !== undefined &&\n this.state.show &&\n this.tableId === blockEl.id &&\n this.state.rowIndex === rowIndex &&\n this.state.colIndex === colIndex\n ) {\n // no update needed\n return;\n }\n\n this.state = {\n show: true,\n showAddOrRemoveColumnsButton:\n colIndex === tableBlock.content.rows[0].cells.length - 1,\n showAddOrRemoveRowsButton:\n rowIndex === tableBlock.content.rows.length - 1,\n referencePosTable: tableRect,\n\n block: tableBlock,\n draggingState: undefined,\n referencePosCell: cellRect,\n colIndex: colIndex,\n rowIndex: rowIndex,\n\n widgetContainer,\n };\n }\n this.emitUpdate();\n\n return false;\n };\n\n dragOverHandler = (event: DragEvent) => {\n if (this.state?.draggingState === undefined) {\n return;\n }\n\n event.preventDefault();\n event.dataTransfer!.dropEffect = \"move\";\n\n hideElements(\n \".prosemirror-dropcursor-block, .prosemirror-dropcursor-inline\",\n this.pmView.root,\n );\n\n // The mouse cursor coordinates, bounded to the table's bounding box. The\n // bounding box is shrunk by 1px on each side to ensure that the bounded\n // coordinates are always inside a table cell.\n const boundedMouseCoords = {\n left: Math.min(\n Math.max(event.clientX, this.state.referencePosTable.left + 1),\n this.state.referencePosTable.right - 1,\n ),\n top: Math.min(\n Math.max(event.clientY, this.state.referencePosTable.top + 1),\n this.state.referencePosTable.bottom - 1,\n ),\n };\n\n // Gets the table cell element that the bounded mouse cursor coordinates lie\n // in.\n const tableCellElements = this.pmView.root\n .elementsFromPoint(boundedMouseCoords.left, boundedMouseCoords.top)\n .filter(\n (element) => element.tagName === \"TD\" || element.tagName === \"TH\",\n );\n if (tableCellElements.length === 0) {\n return;\n }\n const tableCellElement = tableCellElements[0];\n\n let emitStateUpdate = false;\n\n // Gets current row and column index.\n const rowIndex = getChildIndex(tableCellElement.parentElement!);\n const colIndex = getChildIndex(tableCellElement);\n\n // Checks if the drop cursor needs to be updated. This affects decorations\n // only so it doesn't trigger a state update.\n const oldIndex =\n this.state.draggingState.draggedCellOrientation === \"row\"\n ? this.state.rowIndex\n : this.state.colIndex;\n const newIndex =\n this.state.draggingState.draggedCellOrientation === \"row\"\n ? rowIndex\n : colIndex;\n const dispatchDecorationsTransaction = newIndex !== oldIndex;\n\n // Checks if either the hovered cell has changed and updates the row and\n // column index. Also updates the reference DOMRect.\n if (this.state.rowIndex !== rowIndex || this.state.colIndex !== colIndex) {\n this.state.rowIndex = rowIndex;\n this.state.colIndex = colIndex;\n\n this.state.referencePosCell = tableCellElement.getBoundingClientRect();\n\n emitStateUpdate = true;\n }\n\n // Checks if the mouse cursor position along the axis that the user is\n // dragging on has changed and updates it.\n const mousePos =\n this.state.draggingState.draggedCellOrientation === \"row\"\n ? boundedMouseCoords.top\n : boundedMouseCoords.left;\n if (this.state.draggingState.mousePos !== mousePos) {\n this.state.draggingState.mousePos = mousePos;\n\n emitStateUpdate = true;\n }\n\n // Emits a state update if any of the fields have changed.\n if (emitStateUpdate) {\n this.emitUpdate();\n }\n\n // Dispatches a dummy transaction to force a decorations update if\n // necessary.\n if (dispatchDecorationsTransaction) {\n this.editor.transact((tr) => tr.setMeta(tableHandlesPluginKey, true));\n }\n };\n\n dropHandler = (event: DragEvent) => {\n this.mouseState = \"up\";\n if (this.state === undefined || this.state.draggingState === undefined) {\n return false;\n }\n\n if (\n this.state.rowIndex === undefined ||\n this.state.colIndex === undefined\n ) {\n throw new Error(\n \"Attempted to drop table row or column, but no table block was hovered prior.\",\n );\n }\n\n event.preventDefault();\n\n const { draggingState, colIndex, rowIndex } = this.state;\n // Clear so a re-dispatched drop short-circuits above (issue #2691).\n this.state.draggingState = undefined;\n\n const columnWidths = this.state.block.content.columnWidths;\n\n if (draggingState.draggedCellOrientation === \"row\") {\n if (\n !canRowBeDraggedInto(\n this.state.block,\n draggingState.originalIndex,\n rowIndex,\n )\n ) {\n // If the target row is invalid, don't move the row\n return false;\n }\n const newTable = moveRow(\n this.state.block,\n draggingState.originalIndex,\n rowIndex,\n );\n this.editor.updateBlock(this.state.block, {\n type: \"table\",\n content: {\n ...this.state.block.content,\n rows: newTable as any,\n },\n });\n } else {\n if (\n !canColumnBeDraggedInto(\n this.state.block,\n draggingState.originalIndex,\n colIndex,\n )\n ) {\n // If the target column is invalid, don't move the column\n return false;\n }\n const newTable = moveColumn(\n this.state.block,\n draggingState.originalIndex,\n colIndex,\n );\n const [columnWidth] = columnWidths.splice(draggingState.originalIndex, 1);\n columnWidths.splice(colIndex, 0, columnWidth);\n this.editor.updateBlock(this.state.block, {\n type: \"table\",\n content: {\n ...this.state.block.content,\n columnWidths,\n rows: newTable as any,\n },\n });\n }\n\n // Have to reset text cursor position to the block as `updateBlock` moves\n // the existing selection out of the block.\n this.editor.setTextCursorPosition(this.state.block.id);\n\n return true;\n };\n // Updates drag handles when the table is modified or removed.\n update() {\n if (!this.state || !this.state.show) {\n return;\n }\n\n // Hide handles if the table block has been removed.\n const refreshedBlock = this.editor.getBlock(this.state.block.id);\n if (\n !refreshedBlock ||\n refreshedBlock.type !== \"table\" ||\n // when collaborating, the table element might be replaced and out of date\n // because yjs replaces the element when for example you change the color via the side menu\n !this.tableElement?.isConnected\n ) {\n this.state = undefined;\n this.tableId = undefined;\n this.tableElement = undefined;\n this.emitUpdate();\n\n return;\n }\n this.state.block = refreshedBlock as typeof this.state.block;\n\n const { height: rowCount, width: colCount } = getDimensionsOfTable(\n this.state.block,\n );\n\n if (\n this.state.rowIndex !== undefined &&\n this.state.colIndex !== undefined\n ) {\n // If rows or columns are deleted in the update, the hovered indices for\n // those may now be out of bounds. If this is the case, they are moved to\n // the new last row or column.\n if (this.state.rowIndex >= rowCount) {\n this.state.rowIndex = rowCount - 1;\n }\n if (this.state.colIndex >= colCount) {\n this.state.colIndex = colCount - 1;\n }\n }\n\n // Update bounding boxes.\n const tableBody = this.tableElement!.querySelector(\"tbody\");\n\n if (!tableBody) {\n throw new Error(\n \"Table block does not contain a 'tbody' HTML element. This should never happen.\",\n );\n }\n\n if (\n this.state.rowIndex !== undefined &&\n this.state.colIndex !== undefined\n ) {\n const row = tableBody.children[this.state.rowIndex];\n const cell = row.children[this.state.colIndex];\n if (cell) {\n this.state.referencePosCell = cell.getBoundingClientRect();\n } else {\n this.state.rowIndex = undefined;\n this.state.colIndex = undefined;\n }\n }\n this.state.referencePosTable = tableBody.getBoundingClientRect();\n\n this.emitUpdate();\n }\n\n destroy() {\n this.pmView.dom.removeEventListener(\"mousemove\", this.mouseMoveHandler);\n window.removeEventListener(\"mouseup\", this.mouseUpHandler);\n this.pmView.dom.removeEventListener(\"mousedown\", this.viewMousedownHandler);\n this.pmView.root.removeEventListener(\n \"dragover\",\n this.dragOverHandler as EventListener,\n );\n this.pmView.root.removeEventListener(\n \"drop\",\n this.dropHandler as unknown as EventListener,\n );\n }\n}\n\nexport const tableHandlesPluginKey = new PluginKey(\"TableHandlesPlugin\");\n\nexport const TableHandlesExtension = createExtension(({ editor }) => {\n let view: TableHandlesView | undefined = undefined;\n\n const store = createStore<TableHandlesState | undefined>(undefined);\n\n return {\n key: \"tableHandles\",\n store,\n prosemirrorPlugins: [\n new Plugin({\n key: tableHandlesPluginKey,\n view: (editorView) => {\n view = new TableHandlesView(editor as any, editorView, (state) => {\n store.setState(\n state?.block\n ? {\n ...state,\n draggingState: state.draggingState\n ? { ...state.draggingState }\n : undefined,\n }\n : undefined,\n );\n });\n return view;\n },\n // We use decorations to render the drop cursor when dragging a table row\n // or column. The decorations are updated in the `dragOverHandler` method.\n props: {\n decorations: (state) => {\n if (\n view === undefined ||\n view.state === undefined ||\n view.state.draggingState === undefined ||\n view.tablePos === undefined\n ) {\n return;\n }\n\n const newIndex =\n view.state.draggingState.draggedCellOrientation === \"row\"\n ? view.state.rowIndex\n : view.state.colIndex;\n\n if (newIndex === undefined) {\n return;\n }\n\n const decorations: Decoration[] = [];\n const { block, draggingState } = view.state;\n const { originalIndex, draggedCellOrientation } = draggingState;\n\n // Return empty decorations if:\n // - Dragging to same position\n // - No block exists\n // - Row drag not allowed\n // - Column drag not allowed\n if (\n newIndex === originalIndex ||\n !block ||\n (draggedCellOrientation === \"row\" &&\n !canRowBeDraggedInto(block, originalIndex, newIndex)) ||\n (draggedCellOrientation === \"col\" &&\n !canColumnBeDraggedInto(block, originalIndex, newIndex))\n ) {\n return DecorationSet.create(state.doc, decorations);\n }\n\n // Gets the table to show the drop cursor in.\n const tableResolvedPos = state.doc.resolve(view.tablePos + 1);\n\n if (view.state.draggingState.draggedCellOrientation === \"row\") {\n const cellsInRow = getCellsAtRowHandle(\n view.state.block,\n newIndex,\n );\n\n cellsInRow.forEach(({ row, col }) => {\n // Gets each row in the table.\n const rowResolvedPos = state.doc.resolve(\n tableResolvedPos.posAtIndex(row) + 1,\n );\n\n // Gets the cell within the row.\n const cellResolvedPos = state.doc.resolve(\n rowResolvedPos.posAtIndex(col) + 1,\n );\n const cellNode = cellResolvedPos.node();\n // Creates a decoration at the start or end of each cell,\n // depending on whether the new index is before or after the\n // original index.\n const decorationPos =\n cellResolvedPos.pos +\n (newIndex > originalIndex ? cellNode.nodeSize - 2 : 0);\n decorations.push(\n // The widget is a small bar which spans the width of the cell.\n Decoration.widget(decorationPos, () => {\n const widget = document.createElement(\"div\");\n widget.className = \"bn-table-drop-cursor\";\n widget.style.left = \"0\";\n widget.style.right = \"0\";\n // This is only necessary because the drop indicator's height\n // is an even number of pixels, whereas the border between\n // table cells is an odd number of pixels. So this makes the\n // positioning slightly more consistent regardless of where\n // the row is being dropped.\n if (newIndex > originalIndex) {\n widget.style.bottom = \"-2px\";\n } else {\n widget.style.top = \"-3px\";\n }\n widget.style.height = \"4px\";\n\n return widget;\n }),\n );\n });\n } else {\n const cellsInColumn = getCellsAtColumnHandle(\n view.state.block,\n newIndex,\n );\n\n cellsInColumn.forEach(({ row, col }) => {\n // Gets each row in the table.\n const rowResolvedPos = state.doc.resolve(\n tableResolvedPos.posAtIndex(row) + 1,\n );\n\n // Gets the cell within the row.\n const cellResolvedPos = state.doc.resolve(\n rowResolvedPos.posAtIndex(col) + 1,\n );\n const cellNode = cellResolvedPos.node();\n\n // Creates a decoration at the start or end of each cell,\n // depending on whether the new index is before or after the\n // original index.\n const decorationPos =\n cellResolvedPos.pos +\n (newIndex > originalIndex ? cellNode.nodeSize - 2 : 0);\n\n decorations.push(\n // The widget is a small bar which spans the height of the cell.\n Decoration.widget(decorationPos, () => {\n const widget = document.createElement(\"div\");\n widget.className = \"bn-table-drop-cursor\";\n widget.style.top = \"0\";\n widget.style.bottom = \"0\";\n // This is only necessary because the drop indicator's width\n // is an even number of pixels, whereas the border between\n // table cells is an odd number of pixels. So this makes the\n // positioning slightly more consistent regardless of where\n // the column is being dropped.\n if (newIndex > originalIndex) {\n widget.style.right = \"-2px\";\n } else {\n widget.style.left = \"-3px\";\n }\n widget.style.width = \"4px\";\n\n return widget;\n }),\n );\n });\n }\n\n return DecorationSet.create(state.doc, decorations);\n },\n },\n }),\n ],\n\n /**\n * Callback that should be set on the `dragStart` event for whichever element\n * is used as the column drag handle.\n */\n colDragStart(event: {\n dataTransfer: DataTransfer | null;\n clientX: number;\n }) {\n if (\n view === undefined ||\n view.state === undefined ||\n view.state.colIndex === undefined\n ) {\n throw new Error(\n \"Attempted to drag table column, but no table block was hovered prior.\",\n );\n }\n\n view.state.draggingState = {\n draggedCellOrientation: \"col\",\n originalIndex: view.state.colIndex,\n mousePos: event.clientX,\n };\n view.emitUpdate();\n\n editor.transact((tr) =>\n tr.setMeta(tableHandlesPluginKey, {\n draggedCellOrientation:\n view!.state!.draggingState!.draggedCellOrientation,\n originalIndex: view!.state!.colIndex,\n newIndex: view!.state!.colIndex,\n tablePos: view!.tablePos,\n }),\n );\n\n if (editor.headless) {\n return;\n }\n\n setHiddenDragImage(editor.prosemirrorView.root);\n event.dataTransfer!.setDragImage(dragImageElement!, 0, 0);\n event.dataTransfer!.effectAllowed = \"move\";\n },\n\n /**\n * Callback that should be set on the `dragStart` event for whichever element\n * is used as the row drag handle.\n */\n rowDragStart(event: {\n dataTransfer: DataTransfer | null;\n clientY: number;\n }) {\n if (view!.state === undefined || view!.state.rowIndex === undefined) {\n throw new Error(\n \"Attempted to drag table row, but no table block was hovered prior.\",\n );\n }\n\n view!.state.draggingState = {\n draggedCellOrientation: \"row\",\n originalIndex: view!.state.rowIndex,\n mousePos: event.clientY,\n };\n view!.emitUpdate();\n\n editor.transact((tr) =>\n tr.setMeta(tableHandlesPluginKey, {\n draggedCellOrientation:\n view!.state!.draggingState!.draggedCellOrientation,\n originalIndex: view!.state!.rowIndex,\n newIndex: view!.state!.rowIndex,\n tablePos: view!.tablePos,\n }),\n );\n\n if (editor.headless) {\n return;\n }\n\n setHiddenDragImage(editor.prosemirrorView.root);\n event.dataTransfer!.setDragImage(dragImageElement!, 0, 0);\n event.dataTransfer!.effectAllowed = \"copyMove\";\n },\n\n /**\n * Callback that should be set on the `dragEnd` event for both the element\n * used as the row drag handle, and the one used as the column drag handle.\n */\n dragEnd() {\n if (view!.state === undefined) {\n throw new Error(\n \"Attempted to drag table row, but no table block was hovered prior.\",\n );\n }\n\n view!.state.draggingState = undefined;\n view!.emitUpdate();\n\n editor.transact((tr) => tr.setMeta(tableHandlesPluginKey, null));\n\n if (editor.headless) {\n return;\n }\n\n unsetHiddenDragImage(editor.prosemirrorView.root);\n },\n\n /**\n * Freezes the drag handles. When frozen, they will stay attached to the same\n * cell regardless of which cell is hovered by the mouse cursor.\n */\n freezeHandles() {\n view!.menuFrozen = true;\n },\n\n /**\n * Unfreezes the drag handles. When frozen, they will stay attached to the\n * same cell regardless of which cell is hovered by the mouse cursor.\n */\n unfreezeHandles() {\n view!.menuFrozen = false;\n },\n\n /**\n * Hides the table handles unless they are currently frozen (e.g. a\n * handle menu is open). Used to dismiss the handles on scroll without\n * interfering with open submenus.\n */\n hideHandlesIfNotFrozen() {\n if (!view!.menuFrozen && view!.state?.show) {\n view!.state.show = false;\n view!.state.showAddOrRemoveRowsButton = false;\n view!.state.showAddOrRemoveColumnsButton = false;\n view!.emitUpdate();\n }\n },\n\n getCellsAtRowHandle(\n block: BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>,\n relativeRowIndex: RelativeCellIndices[\"row\"],\n ) {\n return getCellsAtRowHandle(block, relativeRowIndex);\n },\n\n /**\n * Get all the cells in a column of the table block.\n */\n getCellsAtColumnHandle(\n block: BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>,\n relativeColumnIndex: RelativeCellIndices[\"col\"],\n ) {\n return getCellsAtColumnHandle(block, relativeColumnIndex);\n },\n\n /**\n * Sets the selection to the given cell or a range of cells.\n * @returns The new state after the selection has been set.\n */\n setCellSelection(\n state: EditorState,\n relativeStartCell: RelativeCellIndices,\n relativeEndCell: RelativeCellIndices = relativeStartCell,\n ) {\n if (!view) {\n throw new Error(\"Table handles view not initialized\");\n }\n\n const tableResolvedPos = state.doc.resolve(view.tablePos! + 1);\n const startRowResolvedPos = state.doc.resolve(\n tableResolvedPos.posAtIndex(relativeStartCell.row) + 1,\n );\n const startCellResolvedPos = state.doc.resolve(\n // No need for +1, since CellSelection expects the position before the cell\n startRowResolvedPos.posAtIndex(relativeStartCell.col),\n );\n const endRowResolvedPos = state.doc.resolve(\n tableResolvedPos.posAtIndex(relativeEndCell.row) + 1,\n );\n const endCellResolvedPos = state.doc.resolve(\n // No need for +1, since CellSelection expects the position before the cell\n endRowResolvedPos.posAtIndex(relativeEndCell.col),\n );\n\n // Begin a new transaction to set the selection\n const tr = state.tr;\n\n // Set the selection to the given cell or a range of cells\n tr.setSelection(\n new CellSelection(startCellResolvedPos, endCellResolvedPos),\n );\n\n // Quickly apply the transaction to get the new state to update the selection before splitting the cell\n return state.apply(tr);\n },\n\n /**\n * Adds a row or column to the table using prosemirror-table commands\n */\n addRowOrColumn(\n index: RelativeCellIndices[\"row\"],\n direction:\n | { orientation: \"row\"; side: \"above\" | \"below\" }\n | { orientation: \"column\"; side: \"left\" | \"right\" },\n ) {\n editor.exec((beforeState, dispatch) => {\n const state = this.setCellSelection(\n beforeState,\n direction.orientation === \"row\"\n ? { row: index, col: 0 }\n : { row: 0, col: index },\n );\n\n if (direction.orientation === \"row\") {\n if (direction.side === \"above\") {\n return addRowBefore(state, dispatch);\n } else {\n return addRowAfter(state, dispatch);\n }\n } else {\n if (direction.side === \"left\") {\n return addColumnBefore(state, dispatch);\n } else {\n return addColumnAfter(state, dispatch);\n }\n }\n });\n },\n\n /**\n * Removes a row or column from the table using prosemirror-table commands\n */\n removeRowOrColumn(\n index: RelativeCellIndices[\"row\"],\n direction: \"row\" | \"column\",\n ) {\n if (direction === \"row\") {\n return editor.exec((beforeState, dispatch) => {\n const state = this.setCellSelection(beforeState, {\n row: index,\n col: 0,\n });\n return deleteRow(state, dispatch);\n });\n } else {\n return editor.exec((beforeState, dispatch) => {\n const state = this.setCellSelection(beforeState, {\n row: 0,\n col: index,\n });\n return deleteColumn(state, dispatch);\n });\n }\n },\n\n /**\n * Merges the cells in the table block.\n */\n mergeCells(cellsToMerge?: {\n relativeStartCell: RelativeCellIndices;\n relativeEndCell: RelativeCellIndices;\n }) {\n return editor.exec((beforeState, dispatch) => {\n const state = cellsToMerge\n ? this.setCellSelection(\n beforeState,\n cellsToMerge.relativeStartCell,\n cellsToMerge.relativeEndCell,\n )\n : beforeState;\n\n return mergeCells(state, dispatch);\n });\n },\n\n /**\n * Splits the cell in the table block.\n * If no cell is provided, the current cell selected will be split.\n */\n splitCell(relativeCellToSplit?: RelativeCellIndices) {\n return editor.exec((beforeState, dispatch) => {\n const state = relativeCellToSplit\n ? this.setCellSelection(beforeState, relativeCellToSplit)\n : beforeState;\n\n return splitCell(state, dispatch);\n });\n },\n\n /**\n * Gets the start and end cells of the current cell selection.\n * @returns The start and end cells of the current cell selection.\n */\n getCellSelection():\n | undefined\n | {\n from: RelativeCellIndices;\n to: RelativeCellIndices;\n /**\n * All of the cells that are within the selected range.\n */\n cells: RelativeCellIndices[];\n } {\n // Based on the current selection, find the table cells that are within the selected range\n\n return editor.transact((tr) => {\n const selection = tr.selection;\n\n let $fromCell = selection.$from;\n let $toCell = selection.$to;\n if (isTableCellSelection(selection)) {\n // When the selection is a table cell selection, we can find the\n // from and to cells by iterating over the ranges in the selection\n const { ranges } = selection;\n ranges.forEach((range) => {\n $fromCell = range.$from.min($fromCell ?? range.$from);\n $toCell = range.$to.max($toCell ?? range.$to);\n });\n } else {\n // When the selection is a normal text selection\n // Assumes we are within a tableParagraph\n // And find the from and to cells by resolving the positions\n const fromCellPos =\n selection.$from.pos - selection.$from.parentOffset - 1;\n const toCellPos = selection.$to.pos - selection.$to.parentOffset - 1;\n\n // Opt-out when the selection is not pointing into cells. This happens when the selection\n // is at the start of the table's `blockContainer` node and therefore just before the\n // actual `table` node.\n if (fromCellPos < 0 || toCellPos < 0) {\n return undefined;\n }\n\n $fromCell = tr.doc.resolve(fromCellPos);\n $toCell = tr.doc.resolve(toCellPos);\n\n // Opt-out when the selection is not actually pointing into table\n // cells (e.g. a gap cursor next to a nested block).\n if (\n !isTableCellNode($fromCell.parent) ||\n !isTableCellNode($toCell.parent)\n ) {\n return undefined;\n }\n }\n\n // Find the row and table that the from and to cells are in\n const $fromRow = tr.doc.resolve(\n $fromCell.pos - $fromCell.parentOffset - 1,\n );\n const $toRow = tr.doc.resolve($toCell.pos - $toCell.parentOffset - 1);\n\n // Find the table\n const $table = tr.doc.resolve($fromRow.pos - $fromRow.parentOffset - 1);\n\n // Find the column and row indices of the from and to cells\n const fromColIndex = $fromCell.index($fromRow.depth);\n const fromRowIndex = $fromRow.index($table.depth);\n const toColIndex = $toCell.index($toRow.depth);\n const toRowIndex = $toRow.index($table.depth);\n\n const cells: RelativeCellIndices[] = [];\n for (let row = fromRowIndex; row <= toRowIndex; row++) {\n for (let col = fromColIndex; col <= toColIndex; col++) {\n cells.push({ row, col });\n }\n }\n\n return {\n from: {\n row: fromRowIndex,\n col: fromColIndex,\n },\n to: {\n row: toRowIndex,\n col: toColIndex,\n },\n cells,\n };\n });\n },\n\n /**\n * Gets the direction of the merge based on the current cell selection.\n *\n * Returns undefined when there is no cell selection, or the selection is not within a table.\n */\n getMergeDirection(\n block:\n | BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>\n | undefined,\n ) {\n return editor.transact((tr) => {\n const isSelectingTableCells = isTableCellSelection(tr.selection)\n ? tr.selection\n : undefined;\n\n if (\n !isSelectingTableCells ||\n !block ||\n // Only offer the merge button if there is more than one cell selected.\n isSelectingTableCells.ranges.length <= 1\n ) {\n return undefined;\n }\n\n const cellSelection = this.getCellSelection();\n\n if (!cellSelection) {\n return undefined;\n }\n\n if (areInSameColumn(cellSelection.from, cellSelection.to, block)) {\n return \"vertical\";\n }\n\n return \"horizontal\";\n });\n },\n\n cropEmptyRowsOrColumns(\n block: BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>,\n removeEmpty: \"columns\" | \"rows\",\n ) {\n return cropEmptyRowsOrColumns(block, removeEmpty);\n },\n\n addRowsOrColumns(\n block: BlockFromConfigNoChildren<DefaultBlockSchema[\"table\"], any, any>,\n addType: \"columns\" | \"rows\",\n numToAdd: number,\n ) {\n return addRowsOrColumns(block, addType, numToAdd);\n },\n } as const;\n});\n","import type { Node as PMNode } from \"prosemirror-model\";\nimport {\n Plugin,\n PluginKey,\n Selection,\n type Transaction,\n} from \"prosemirror-state\";\nimport { Decoration, DecorationSet } from \"prosemirror-view\";\nimport {\n createExtension,\n ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\n\nconst PLUGIN_KEY = new PluginKey<DecorationSet>(\"trailingNode\");\n\n// Skip the widget when the container already ends with an empty paragraph\n// block (since the user can just type into it).\nfunction containerNeedsTrailingWidget(container: PMNode): boolean {\n const lastBlock = container.lastChild;\n const lastContent = lastBlock?.firstChild;\n\n return !(\n lastBlock?.type.name === \"blockContainer\" &&\n lastContent?.type.name === \"paragraph\" &&\n lastContent.content.size === 0\n );\n}\n\n// Returns the position at the end of each container that should render a\n// trailing widget: the root blockGroup, and columns from the multi-column\n// package. Nested blockGroups (a block's children) are excluded, as they have\n// no empty space below them for a widget to occupy.\nfunction getTrailingWidgetPositions(doc: PMNode): number[] {\n // When the schema has no columns, the root blockGroup is the only possible\n // container, so traversing the doc to find others can be skipped.\n if (!doc.type.schema.nodes[\"column\"]) {\n const rootGroup = doc.lastChild;\n return rootGroup && containerNeedsTrailingWidget(rootGroup)\n ? [doc.content.size - 1]\n : [];\n }\n\n const positions: number[] = [];\n\n doc.descendants((node, pos, parent) => {\n if (node.isTextblock) {\n return false;\n }\n\n const isContainer =\n node.type.name === \"column\" ||\n (node.type.name === \"blockGroup\" && parent?.type.name === \"doc\");\n\n if (isContainer && containerNeedsTrailingWidget(node)) {\n positions.push(pos + node.nodeSize - 1);\n }\n\n return true;\n });\n\n return positions;\n}\n\n/**\n * Renders a fake trailing block as a widget decoration after the last block of\n * the document, as well as of any other container that blocks can be appended\n * to (e.g. columns). Clicking it inserts a real trailing block in the\n * container and moves the selection into it. This way the trailing block is\n * not part of the document content, so it doesn't appear when the editor is\n * read-only or when the content is exported.\n */\nexport const TrailingNodeExtension = createExtension(\n ({ editor }: ExtensionOptions) => {\n function createTrailingWidget(pos: number): Decoration {\n return Decoration.widget(\n pos,\n () => {\n const el = document.createElement(\"div\");\n el.className = \"bn-trailing-block\";\n el.contentEditable = \"false\";\n el.addEventListener(\"mousedown\", (event) => {\n // Stop ProseMirror from trying to place the selection somewhere\n // based on this click.\n event.preventDefault();\n\n const view = editor.prosemirrorView;\n if (!view) {\n return;\n }\n\n // The widget may have been remapped since it was created, so its\n // container is resolved from its current DOM position instead of\n // captured up front.\n const container = view.state.doc.resolve(\n view.posAtDOM(el, 0),\n ).parent;\n const lastBlockId = container.lastChild?.attrs[\"id\"];\n if (!lastBlockId) {\n return;\n }\n\n editor.transact((tr) => {\n const [insertedBlock] = editor.insertBlocks(\n [{ type: \"paragraph\" }],\n lastBlockId,\n \"after\",\n );\n editor.setTextCursorPosition(insertedBlock, \"start\");\n tr.scrollIntoView();\n });\n\n view.focus();\n });\n return el;\n },\n { side: 1 },\n );\n }\n\n // Maps the existing DecorationSet through the transaction, then diffs it\n // against the containers that should currently show a widget, only adding\n // and removing where the two differ. Decorations (and their rendered DOM)\n // stay reference-stable across transactions for unchanged containers.\n function nextDecorationSet(\n tr: Transaction,\n oldSet: DecorationSet,\n isEditable: boolean,\n ): DecorationSet {\n const mapped = oldSet.map(tr.mapping, tr.doc);\n const desiredPositions = new Set(\n isEditable ? getTrailingWidgetPositions(tr.doc) : [],\n );\n\n const keptPositions = new Set<number>();\n const stale: Decoration[] = [];\n for (const decoration of mapped.find()) {\n if (\n desiredPositions.has(decoration.from) &&\n !keptPositions.has(decoration.from)\n ) {\n keptPositions.add(decoration.from);\n } else {\n stale.push(decoration);\n }\n }\n const missing = [...desiredPositions].filter(\n (pos) => !keptPositions.has(pos),\n );\n\n let next = mapped;\n if (stale.length > 0) {\n next = next.remove(stale);\n }\n if (missing.length > 0) {\n next = next.add(tr.doc, missing.map(createTrailingWidget));\n }\n return next;\n }\n\n return {\n key: \"trailingNode\",\n prosemirrorPlugins: [\n new Plugin<DecorationSet>({\n key: PLUGIN_KEY,\n state: {\n init: (_, state) =>\n nextDecorationSet(\n state.tr,\n DecorationSet.empty,\n editor.isEditable,\n ),\n apply: (tr, oldSet) => {\n if (!tr.docChanged && !tr.getMeta(PLUGIN_KEY)) {\n return oldSet;\n }\n return nextDecorationSet(tr, oldSet, editor.isEditable);\n },\n },\n // Editable changes don't dispatch a transaction on their own, so the\n // plugin state can't re-evaluate on its own. Watch for the change\n // and dispatch a no-op transaction tagged with this plugin's key so\n // `apply` re-runs and adds or removes the widget.\n view(view) {\n let lastEditable = view.editable;\n return {\n update(view) {\n if (view.editable === lastEditable) {\n return;\n }\n lastEditable = view.editable;\n view.dispatch(view.state.tr.setMeta(PLUGIN_KEY, true));\n },\n };\n },\n props: {\n decorations: (state) => PLUGIN_KEY.getState(state),\n // Prevents ProseMirror from trying to move the selection into the\n // trailing block at the end of the document, which causes the text\n // caret to flicker in it before returning to its previous\n // position.\n handleKeyDown: (view, event) => {\n if (event.key !== \"ArrowRight\" && event.key !== \"ArrowDown\") {\n return false;\n }\n\n const { selection } = view.state;\n if (!selection.empty) {\n return false;\n }\n\n const docEnd = Selection.atEnd(view.state.doc);\n if (selection.$head.pos !== docEnd.$head.pos) {\n return false;\n }\n\n const rootGroup = view.state.doc.lastChild;\n if (\n !editor.isEditable ||\n !rootGroup ||\n !containerNeedsTrailingWidget(rootGroup)\n ) {\n return false;\n }\n\n event.preventDefault();\n return true;\n },\n },\n }),\n ],\n } as const;\n },\n);\n","import type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport {\n createExtension,\n createStore,\n type ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport {\n normalizeToUserStore,\n type User,\n type UserStoreOrResolver,\n} from \"../../user/index.js\";\n\n/**\n * Represents a single snapshot of a document's history, including metadata and content information.\n * Snapshots are used for versioning and can be created, listed, restored, and previewed through the\n * {@link VersioningEndpoints}.\n */\nexport interface VersionSnapshot {\n /**\n * The unique identifier for the snapshot. A plain string for real snapshots;\n * the {@link CURRENT_VERSION_ID} symbol for the synthetic \"Current version\"\n * entry (which no backend ever persists or round-trips).\n */\n id: string | typeof CURRENT_VERSION_ID;\n\n /**\n * The name of the snapshot.\n */\n name?: string;\n\n /**\n * The timestamp when the snapshot was created (unix timestamp).\n */\n createdAt: number;\n\n /**\n * The timestamp when the snapshot was last updated (unix timestamp).\n */\n updatedAt: number;\n\n /**\n * An optional secondary label for the snapshot, which can display additional information such as a custom description.\n * This is for display purposes only and is not used for any logic in the versioning system.\n *\n * For author attribution, prefer {@link by}: it holds raw user ids that the\n * view layer resolves to user info (and keeps up to date as users load).\n * When both are set, `secondaryLabel` wins.\n */\n secondaryLabel?: string;\n\n /**\n * The id(s) of the user(s) that authored this version, as raw user ids —\n * never pre-resolved to display names. The view layer resolves them via the\n * {@link VersioningExtension}'s user store (see\n * {@link VersioningExtensionOptions.resolveUsers}), reactively updating as\n * user info loads. Only used when {@link secondaryLabel} is unset.\n */\n by?: User[\"id\"] | User[\"id\"][];\n\n /**\n * The ID of the previous snapshot that this snapshot was restored from.\n */\n restoredFromSnapshotId?: string;\n}\n\n/**\n * Identifier for a single {@link VersionSnapshot}, either the bare id or the\n * whole reference. Tracks {@link VersionSnapshot.id}, so it also accepts the\n * {@link CURRENT_VERSION_ID} symbol.\n */\nexport type VersionSnapshotIdentifier =\n | VersionSnapshot[\"id\"]\n | Pick<VersionSnapshot, \"id\">;\n\n/**\n * The `id` of the synthetic \"Current version\" entry — the live document shown at\n * the top of `list()` and set as `previewedSnapshotId` while previewing it (see\n * {@link VersioningExtension.previewCurrentVersion}).\n *\n * A `unique symbol`, not a string, so it can never clash with a real snapshot id.\n * It's client-only — never fetched via `getContent` / `getAttributions` (the row\n * is previewed live) and never serialised, so no backend round-trips it. Because\n * {@link VersionSnapshot.id} is `string | typeof CURRENT_VERSION_ID`, code that\n * needs a string form for this one row (e.g. a React `key`) derives it locally.\n */\nexport const CURRENT_VERSION_ID: unique symbol = Symbol(\"bn-current-version\");\n\n/**\n * The backend contract for versioning: **where snapshot data lives** (pure\n * storage — in-memory, `localStorage`, HTTP, …). Counterpart to\n * {@link PreviewController} (*how a snapshot is rendered*) and\n * {@link VersioningExtensionOptions} (*how the live editor is bridged in*);\n * {@link VersioningExtension} orchestrates the three.\n *\n * Type params trace the data flow:\n * @typeParam Input - Live document handle passed to {@link create} / {@link restore},\n * from {@link VersioningExtensionOptions.getCurrentDocument} (e.g. `Y.Type`, `Block[]`).\n * @typeParam Output - Serialised snapshot content from {@link getContent} /\n * {@link restore}, rendered by {@link PreviewController.enterPreview} (e.g. `Uint8Array`).\n * @typeParam Attributions - Optional diff-authorship data from {@link getAttributions},\n * also consumed by {@link PreviewController.enterPreview} (e.g. `Y.ContentMap`).\n */\nexport interface VersioningEndpoints<\n Input = any,\n Output = any,\n Attributions = any,\n> {\n /**\n * List all snapshots for this document, sorted newest-first by\n * {@link VersionSnapshot.createdAt}.\n */\n list: () => Promise<VersionSnapshot[]>;\n /**\n * Create a new snapshot from the current content.\n *\n * @note omit for backends with continuous history (e.g. YHub's activity\n * timeline). Gates the extension's `canCreate` flag.\n */\n create?: (\n /** Live document to snapshot, from {@link VersioningExtensionOptions.getCurrentDocument}. */\n content: Input,\n options?: {\n /** Optional name for this snapshot. */\n name?: string;\n /** Id of the snapshot this one was restored from, if any. */\n restoredFromSnapshot?: VersionSnapshot;\n },\n ) => Promise<VersionSnapshot>;\n /**\n * Restore the document to a snapshot. Implementations should create any backup\n * snapshots they need before returning.\n *\n * @returns The restored content ({@link Output}, **not `void`**) — passed to\n * {@link PreviewController.applyRestore}.\n * @note omit to disable restore. Gates the extension's `canRestore` flag.\n */\n restore?: (\n /** Live document, from {@link VersioningExtensionOptions.getCurrentDocument} (for backup). */\n doc: Input,\n /** The snapshot to restore. */\n snapshot: VersionSnapshot,\n ) => Promise<Output>;\n /**\n * Fetch a snapshot's content ({@link Output}) for preview — same format as\n * {@link VersioningExtensionOptions.serializeCurrentContent}. Sibling of\n * {@link getAttributions}; both are the storage-side fetch that\n * {@link PreviewController.enterPreview} renders.\n */\n getContent: (snapshot: VersionSnapshot) => Promise<Output>;\n /**\n * Fetch diff-authorship data ({@link Attributions}: who/when) for the range\n * `compareTo → snapshot`, rendered by {@link PreviewController.enterPreview}\n * (its only consumer). Lives on the endpoint, not `enterPreview`, so one\n * preview controller pairs with attribution-capable (YHub) or attribution-less\n * (`localStorage`) backends — {@link Attributions} is that seam.\n *\n * @note omit and previews still render the content diff, minus attribution.\n */\n getAttributions?: (\n /** The previewed snapshot (the \"new\" side of the diff). */\n snapshot: VersionSnapshot,\n /** The baseline it's diffed against (the \"old\" side). */\n compareTo?: VersionSnapshot,\n ) => Promise<Attributions>;\n /**\n * Rename a snapshot.\n *\n * @note omit to disable rename. Gates the extension's `canRename` flag.\n */\n rename?: (snapshot: VersionSnapshot, name?: string) => Promise<void>;\n /**\n * Permanently remove a snapshot.\n *\n * @note omit for immutable-history backends (e.g. YHub). Gates the extension's\n * `canRemove` flag.\n */\n remove?: (snapshot: VersionSnapshot) => Promise<void>;\n}\n\n/**\n * A factory function for the endpoints to receive a reference to the editor.\n *\n * @typeParam Input - See {@link VersioningEndpoints}.\n * @typeParam Output - See {@link VersioningEndpoints}.\n * @typeParam Attributions - See {@link VersioningEndpoints}.\n */\nexport type VersioningEndpointsFactory<\n Input = any,\n Output = any,\n Attributions = any,\n> = (\n editor: BlockNoteEditor<any, any, any>,\n) => VersioningEndpoints<Input, Output, Attributions>;\n\n/**\n * Controls **how a snapshot is rendered** — the render-side counterpart to\n * {@link VersioningEndpoints} (storage). {@link VersioningExtension} fetches\n * content/attributions from the endpoints and delegates rendering here; keeping\n * the two separate lets one controller pair with different backends.\n *\n * @typeParam Output - Serialised snapshot content; matches the endpoints' `Output`.\n * @typeParam Attributions - Optional attribution data; matches the endpoints' `Attributions`.\n */\nexport interface PreviewController<Output = any, Attributions = any> {\n /**\n * Whether {@link enterPreview} can render a diff (uses `compareToContent`).\n * Defaults to `true`; `false` for show-one-version-only backends (e.g. the Yjs\n * v13 adapter). Surfaced as {@link VersioningExtension.canCompare}.\n */\n supportsComparison?: boolean;\n /**\n * Enter preview mode. Arguments come from the endpoints:\n * {@link VersioningEndpoints.getContent} (content) and\n * {@link VersioningEndpoints.getAttributions} (attributions).\n */\n enterPreview: (\n /** Snapshot to preview ({@link Output}, from {@link VersioningEndpoints.getContent}). */\n snapshotContent: Output,\n /** When set, diff `compareToContent` (baseline) against `snapshotContent`. */\n compareToContent?: Output,\n /**\n * Diff attributions ({@link Attributions}, from\n * {@link VersioningEndpoints.getAttributions}). Only meaningful with\n * `compareToContent`.\n */\n attributions?: Attributions,\n /**\n * The snapshot(s) this preview is for (metadata only — the content is\n * `snapshotContent` / `compareToContent`). Lets a controller label the\n * preview with e.g. the version's name, without smuggling it through the\n * {@link Attributions} channel. `snapshot` is the previewed version (the\n * {@link CURRENT_VERSION_ID} entry when previewing the live document);\n * `compareTo` is the baseline it's diffed against, if any.\n */\n context?: { snapshot: VersionSnapshot; compareTo?: VersionSnapshot },\n ) => void;\n /** Exit preview mode and resume normal editing. */\n exitPreview: () => void;\n /**\n * Apply restored content to the live document. Called with the {@link Output}\n * from {@link VersioningEndpoints.restore}, after preview mode has exited.\n */\n applyRestore: (snapshotContent: Output) => void;\n}\n\n/** Sort snapshots newest-first by creation time. */\nexport function sortSnapshotsNewestFirst(\n snapshots: VersionSnapshot[],\n): VersionSnapshot[] {\n return [...snapshots].sort((a, b) => b.createdAt - a.createdAt);\n}\n\n/**\n * Options accepted by the {@link VersioningExtension} — **how the live editor is\n * bridged in**, alongside the {@link VersioningEndpoints} (storage) and\n * {@link PreviewController} (rendering).\n *\n * @typeParam Input - See {@link VersioningEndpoints}.\n * @typeParam Output - See {@link VersioningEndpoints}.\n * @typeParam Attributions - See {@link VersioningEndpoints}.\n */\nexport type VersioningExtensionOptions<\n Input = any,\n Output = any,\n Attributions = any,\n> = {\n /**\n * Backend storage for snapshots.\n */\n endpoints:\n | VersioningEndpoints<Input, Output, Attributions>\n | VersioningEndpointsFactory<Input, Output, Attributions>;\n /**\n * Controls how snapshot previews and restores are rendered in the editor.\n */\n preview: PreviewController<Output, Attributions>;\n /**\n * The **live, mutable document handle** ({@link Input}) the backend snapshots\n * *from* / restores *into*. Passed to {@link VersioningEndpoints.create} and\n * {@link VersioningEndpoints.restore}. Cf. {@link serializeCurrentContent} (a\n * detached copy); the two coincide for some backends (in-memory:\n * `Input === Output === Block[]`) and differ for others (Yjs: `Y.Type` vs `Uint8Array`).\n */\n getCurrentDocument: () => Input;\n /**\n * The live document **serialised to snapshot format** ({@link Output}, matching\n * {@link VersioningEndpoints.getContent}), for diffing the live doc against a\n * snapshot (see {@link VersioningExtension.previewCurrentVersion}). Cf.\n * {@link getCurrentDocument} (the live handle).\n *\n * @note omit and the UI can't offer a \"Current version\" diff. Gates the\n * extension's `canPreviewCurrent` flag.\n */\n serializeCurrentContent?: () => Output | Promise<Output>;\n /**\n * Resolve user information for the author ids in {@link VersionSnapshot.by},\n * used by the view layer to render version-author labels.\n *\n * Either a resolver function (called with the ids of users that are not yet\n * cached, returning their information — a user store is built from it\n * internally) or a pre-built user store (see `createUserStore`). Pass the\n * same store you give the comments/collaboration extensions so a single\n * de-duped user cache is shared across features.\n *\n * @note omit and author ids are displayed as-is.\n */\n resolveUsers?: UserStoreOrResolver;\n};\n\nfunction snapshotNotFoundError(\n id: VersionSnapshotIdentifier | undefined,\n): never {\n const idResolved = typeof id === \"object\" ? id.id : id;\n throw new Error(`Snapshot not found: ${String(idResolved)}`);\n}\n\nexport const VersioningExtension = createExtension(\n ({\n options: optionsOrFactory,\n editor,\n }: ExtensionOptions<\n | VersioningExtensionOptions\n | ((editor: BlockNoteEditor<any, any, any>) => VersioningExtensionOptions)\n >) => {\n const {\n endpoints: endpointsRaw,\n preview,\n getCurrentDocument,\n serializeCurrentContent,\n resolveUsers,\n } = typeof optionsOrFactory === \"function\"\n ? optionsOrFactory(editor)\n : optionsOrFactory;\n\n const endpoints =\n typeof endpointsRaw === \"function\" ? endpointsRaw(editor) : endpointsRaw;\n // With no resolver this is an empty store: `getUser` always misses, so the\n // view layer falls back to showing the raw ids from `VersionSnapshot.by`.\n const userStore = normalizeToUserStore(resolveUsers);\n const store = createStore<{\n snapshots: VersionSnapshot[];\n /**\n * The id of the version currently shown in the editor (the \"new\" side of\n * a diff). `undefined` means the live, editable document. Is the\n * {@link CURRENT_VERSION_ID} symbol when previewing the live document as a\n * read-only diff against a snapshot.\n */\n previewedSnapshotId?: string | typeof CURRENT_VERSION_ID;\n /**\n * The id of the snapshot the preview is being diffed against (the\n * \"baseline\" / old side). `undefined` when not showing a diff. Always a\n * real snapshot id (never the current entry), but typed as the same union\n * as {@link VersionSnapshot.id} since it's copied from one. Used to render\n * the \"Comparing to\" indicator in the sidebar.\n */\n compareToSnapshotId?: string | typeof CURRENT_VERSION_ID;\n }>({\n snapshots: [],\n previewedSnapshotId: undefined,\n compareToSnapshotId: undefined,\n });\n\n const getSnapshot = (id: VersionSnapshotIdentifier | undefined) => {\n const idResolved = typeof id === \"object\" ? id.id : id;\n return store.state.snapshots.find(\n (snapshot) => snapshot.id === idResolved,\n );\n };\n\n const updateSnapshots = async () => {\n const snapshots = sortSnapshotsNewestFirst(await endpoints.list());\n store.setState((state) => ({\n ...state,\n snapshots,\n }));\n\n return snapshots;\n };\n\n const previewSnapshot = async (\n id: VersionSnapshotIdentifier,\n previewOptions?: {\n /**\n * When set, the preview shows a diff against this snapshot (typically the\n * chronologically previous version in the history list).\n */\n compareTo?: VersionSnapshotIdentifier;\n },\n ) => {\n const snapshot = getSnapshot(id);\n\n if (!snapshot) {\n snapshotNotFoundError(id);\n }\n\n const compareToSnapshot = previewOptions?.compareTo\n ? getSnapshot(previewOptions.compareTo)\n : undefined;\n\n store.setState((state) => ({\n ...state,\n previewedSnapshotId: snapshot.id,\n compareToSnapshotId: compareToSnapshot?.id,\n }));\n\n let compareToContent: unknown;\n let attributions: unknown;\n if (compareToSnapshot) {\n compareToContent = await endpoints.getContent(compareToSnapshot);\n // Attributions describe the diff between the baseline and this\n // snapshot, so they're only meaningful when comparing against another\n // version. Fetching them is optional: previews still render the content\n // diff without author/timestamp information when unavailable.\n if (endpoints.getAttributions) {\n attributions = await endpoints.getAttributions(\n snapshot,\n compareToSnapshot,\n );\n }\n }\n\n const snapshotContent = await endpoints.getContent(snapshot);\n preview.enterPreview(snapshotContent, compareToContent, attributions, {\n snapshot,\n compareTo: compareToSnapshot,\n });\n };\n\n /**\n * Preview the live (\"current\") document as a read-only diff against a\n * snapshot baseline. Unlike {@link previewSnapshot}, the \"new\" side of the\n * diff is the live document — serialised via `serializeCurrentContent` —\n * rather than a stored snapshot. The editor becomes non-editable while\n * previewing (editing is gated on `previewedSnapshotId === undefined`).\n */\n const previewCurrentVersion = async (previewOptions?: {\n /**\n * The snapshot to diff the live document against (the baseline). When\n * omitted, the live document is shown without a diff.\n */\n compareTo?: VersionSnapshotIdentifier;\n }) => {\n if (!serializeCurrentContent) {\n throw new Error(\n \"previewCurrentVersion requires `serializeCurrentContent` to be \" +\n \"provided to the VersioningExtension options.\",\n );\n }\n\n const compareToSnapshot = previewOptions?.compareTo\n ? getSnapshot(previewOptions.compareTo)\n : undefined;\n\n store.setState((state) => ({\n ...state,\n previewedSnapshotId: CURRENT_VERSION_ID,\n compareToSnapshotId: compareToSnapshot?.id,\n }));\n\n // Synthesise a snapshot for the live document so timestamp-based backends\n // (e.g. YHub) resolve the changeset window up to \"now\", and so the preview\n // controller gets a snapshot to key off. The id is the current-version\n // sentinel; backends ignore it and resolve the window from `createdAt`.\n const currentSnapshot: VersionSnapshot = {\n id: CURRENT_VERSION_ID,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n\n let compareToContent: unknown;\n let attributions: unknown;\n if (compareToSnapshot) {\n compareToContent = await endpoints.getContent(compareToSnapshot);\n if (endpoints.getAttributions) {\n attributions = await endpoints.getAttributions(\n currentSnapshot,\n compareToSnapshot,\n );\n }\n }\n\n const currentContent = await serializeCurrentContent();\n preview.enterPreview(currentContent, compareToContent, attributions, {\n snapshot: currentSnapshot,\n compareTo: compareToSnapshot,\n });\n };\n\n const exitPreview = () => {\n store.setState((state) => ({\n ...state,\n previewedSnapshotId: undefined,\n compareToSnapshotId: undefined,\n }));\n preview.exitPreview();\n };\n\n return {\n key: \"versioning\",\n store,\n userStore,\n list: async (): Promise<VersionSnapshot[]> => {\n return await updateSnapshots();\n },\n // Comparison is only offered when the preview controller can actually\n // render a diff (see PreviewController.supportsComparison). A getter so a\n // controller whose `supportsComparison` is itself dynamic (e.g. gated on\n // an opt-in diff extension that may be registered after this one) is read\n // lazily, not captured at init time.\n get canCompare() {\n return preview.supportsComparison !== false;\n },\n canCreate: endpoints.create !== undefined,\n create: endpoints.create\n ? async (options?: {\n /**\n * The optional name for this snapshot.\n */\n name?: string;\n /**\n * The ID of the snapshot this one was restored from, if applicable.\n */\n restoredFromSnapshot?: VersionSnapshotIdentifier;\n }): Promise<VersionSnapshot> => {\n const snapshot = await endpoints.create!(getCurrentDocument(), {\n name: options?.name,\n restoredFromSnapshot: getSnapshot(options?.restoredFromSnapshot),\n });\n // Show the new version immediately. Some backends (e.g. YHub) build\n // their version list from an activity timeline that lags a beat\n // behind the create, so waiting on a re-list would leave the UI\n // briefly stale.\n store.setState((state) => ({\n ...state,\n snapshots: sortSnapshotsNewestFirst([\n ...state.snapshots,\n snapshot,\n ]),\n }));\n // Reconcile with the backend's `list()` — it owns the \"current\n // version\" entry and any server-assigned metadata. If the refreshed\n // list doesn't include the just-created version yet (indexing lag),\n // keep the optimistic entry so it never flickers out.\n const listed = await endpoints.list();\n store.setState((state) => ({\n ...state,\n snapshots: sortSnapshotsNewestFirst(\n listed.some((s) => s.id === snapshot.id)\n ? listed\n : [...listed, snapshot],\n ),\n }));\n return snapshot;\n }\n : undefined,\n canRestore: endpoints.restore !== undefined,\n restore: endpoints.restore\n ? async (id: VersionSnapshotIdentifier) => {\n exitPreview();\n const snapshot = getSnapshot(id);\n\n if (!snapshot) {\n snapshotNotFoundError(id);\n }\n const snapshotContent = await endpoints.restore!(\n getCurrentDocument(),\n snapshot,\n );\n preview.applyRestore(snapshotContent);\n await updateSnapshots();\n return snapshotContent;\n }\n : undefined,\n canRename: endpoints.rename !== undefined,\n rename: endpoints.rename\n ? async (\n id: VersionSnapshotIdentifier,\n name?: string,\n ): Promise<void> => {\n const snapshot = getSnapshot(id);\n if (!snapshot) {\n snapshotNotFoundError(id);\n }\n await endpoints.rename!(snapshot, name);\n store.setState((state) => ({\n ...state,\n snapshots: state.snapshots.map((s) =>\n s.id === id ? { ...s, name, updatedAt: Date.now() } : s,\n ),\n }));\n }\n : undefined,\n canRemove: endpoints.remove !== undefined,\n remove: endpoints.remove\n ? async (id: VersionSnapshotIdentifier): Promise<void> => {\n const snapshot = getSnapshot(id);\n if (!snapshot) {\n snapshotNotFoundError(id);\n }\n // If the snapshot being removed is the one currently previewed, or\n // the baseline it's being diffed against, exit preview first so the\n // editor returns to the live document instead of showing (or\n // comparing against) a version that no longer exists.\n if (\n store.state.previewedSnapshotId === snapshot.id ||\n store.state.compareToSnapshotId === snapshot.id\n ) {\n exitPreview();\n }\n await endpoints.remove!(snapshot);\n // Remove it optimistically so the row disappears immediately, then\n // reconcile with the backend's authoritative list.\n store.setState((state) => ({\n ...state,\n snapshots: state.snapshots.filter((s) => s.id !== snapshot.id),\n }));\n await updateSnapshots();\n }\n : undefined,\n previewSnapshot,\n canPreviewCurrent: serializeCurrentContent !== undefined,\n previewCurrentVersion: serializeCurrentContent\n ? previewCurrentVersion\n : undefined,\n exitPreview,\n } as const;\n },\n);\n","import type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport type { Block } from \"../../blocks/defaultBlocks.js\";\nimport type { DiffVersioningExtension } from \"../../y/extensions/DiffVersioningExtension.js\";\nimport type {\n PreviewController,\n VersioningEndpoints,\n VersioningExtensionOptions,\n VersionSnapshot,\n} from \"./Versioning.js\";\nimport { CURRENT_VERSION_ID, sortSnapshotsNewestFirst } from \"./Versioning.js\";\n\n/**\n * Label shown on a diff's marks for the version that introduced the changes.\n * The previewed snapshot is the \"new\" side of the diff; the current-version\n * entry (previewing the live doc) has no name, so it reads \"Current version\".\n */\nfunction versionLabel(snapshot: VersionSnapshot): string {\n if (snapshot.id === CURRENT_VERSION_ID) {\n return \"Current version\";\n }\n return snapshot.name ?? \"Unnamed version\";\n}\n\n// ---------------------------------------------------------------------------\n// Preview Controller\n// ---------------------------------------------------------------------------\n\n/**\n * Create a {@link PreviewController} that swaps the BlockNote document in and\n * out using `editor.replaceBlocks`.\n *\n * When entering preview mode the current document is saved so it can be\n * restored on exit. Successive `enterPreview` calls without an intervening\n * `exitPreview` preserve the original saved document.\n */\nexport function createInMemoryPreviewController(\n editor: BlockNoteEditor<any, any, any>,\n): PreviewController<Block<any, any, any>[]> {\n let savedDoc: Block<any, any, any>[] | undefined;\n // True while a diff (attribution marks) is on screen, so exit/restore knows to\n // route the cleanup through the diff extension's node-view rebuild.\n let showingDiff = false;\n\n const replaceDoc = (blocks: Block<any, any, any>[]) => {\n editor.replaceBlocks(editor.document, blocks);\n };\n\n // The opt-in diff extension, if the consuming editor registered it. Looked up\n // by key so this module keeps zero runtime dependency on `@y/*`.\n const getDiff = () =>\n editor.getExtension<typeof DiffVersioningExtension>(\"diffVersioning\");\n\n return {\n // Comparison is only possible when the (opt-in) diff extension is present —\n // otherwise previewing a comparison just statically shows the snapshot, so\n // the UI shouldn't offer it. A getter (not a static `true`) so it's\n // independent of the order the extensions were registered in: the diff\n // extension is typically added after the versioning extension, and this is\n // read lazily (on render) once both are registered.\n get supportsComparison() {\n return getDiff() !== undefined;\n },\n enterPreview(\n snapshotContent: Block<any, any, any>[],\n compareToContent?: Block<any, any, any>[],\n _attributions?: unknown,\n context?: { snapshot: VersionSnapshot; compareTo?: VersionSnapshot },\n ) {\n // Save the live doc on first enter (successive enters keep the original).\n if (savedDoc === undefined) {\n savedDoc = editor.document;\n }\n\n const diff = getDiff();\n if (compareToContent && diff) {\n // Render a diff of compareTo → snapshot, labelling the changes with the\n // previewed version's name (the diff's single \"author\").\n diff.renderDiff(\n snapshotContent,\n compareToContent,\n context && versionLabel(context.snapshot),\n );\n showingDiff = true;\n return;\n }\n\n // No comparison requested, or no diff extension registered: just show the\n // snapshot content statically.\n showingDiff = false;\n replaceDoc(snapshotContent);\n },\n\n exitPreview() {\n if (savedDoc !== undefined) {\n const diff = getDiff();\n if (showingDiff && diff) {\n diff.clearDiff(savedDoc);\n } else {\n replaceDoc(savedDoc);\n }\n savedDoc = undefined;\n showingDiff = false;\n }\n },\n\n applyRestore(snapshotContent: Block<any, any, any>[]) {\n const diff = getDiff();\n if (showingDiff && diff) {\n diff.clearDiff(snapshotContent);\n } else {\n replaceDoc(snapshotContent);\n }\n // Clear saved doc — the restored content is now the live document.\n savedDoc = undefined;\n showingDiff = false;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Endpoints (in-memory storage)\n// ---------------------------------------------------------------------------\n\n/**\n * Create a {@link VersioningEndpoints} that stores snapshots entirely in\n * memory. Useful for local-only / non-collaborative editors where you want\n * versioning without any persistence layer.\n *\n * Snapshots are stored as BlockNote document JSON (`Block[]`).\n */\nexport function createInMemoryVersioningEndpoints(): VersioningEndpoints<\n Block<any, any, any>[],\n Block<any, any, any>[]\n> {\n const snapshots: VersionSnapshot[] = [];\n const contents = new Map<string, Block<any, any, any>[]>();\n let nextId = 1;\n\n // `Date.now()` only has millisecond resolution, so two snapshots created in\n // the same tick would share a timestamp and `sortSnapshotsNewestFirst` (which\n // has nothing else to order on) could list them oldest-first. Hand out\n // strictly increasing timestamps so creation order is always preserved.\n let lastTimestamp = 0;\n function nextTimestamp() {\n lastTimestamp = Math.max(Date.now(), lastTimestamp + 1);\n return lastTimestamp;\n }\n\n return {\n async list() {\n return sortSnapshotsNewestFirst([...snapshots]);\n },\n\n async create(currentDoc, options) {\n const now = nextTimestamp();\n const id = String(nextId++);\n const snapshot: VersionSnapshot = {\n id,\n name: options?.name,\n createdAt: now,\n updatedAt: now,\n };\n snapshots.push(snapshot);\n contents.set(id, structuredClone(currentDoc));\n return snapshot;\n },\n\n async restore(currentDoc, snapshot) {\n // Stored snapshots always have string ids (only the synthetic current\n // entry carries the symbol, and it never reaches these methods).\n const id = String(snapshot.id);\n const snapshotContent = contents.get(id);\n if (!snapshotContent) {\n throw new Error(`Snapshot ${id} not found`);\n }\n\n // Create a \"Restored from …\" snapshot of the current state before\n // restoring, so the user can undo the restore.\n const now = nextTimestamp();\n const backupId = String(nextId++);\n const backup: VersionSnapshot = {\n id: backupId,\n name: \"Before restore\",\n createdAt: now,\n updatedAt: now,\n restoredFromSnapshotId: id,\n };\n snapshots.push(backup);\n contents.set(backupId, structuredClone(currentDoc));\n\n return structuredClone(snapshotContent);\n },\n\n async getContent(snapshot) {\n const id = String(snapshot.id);\n const content = contents.get(id);\n if (!content) {\n throw new Error(`Snapshot ${id} not found`);\n }\n return structuredClone(content);\n },\n\n async rename(snapshot, name) {\n const stored = snapshots.find((s) => s.id === snapshot.id);\n if (!stored) {\n throw new Error(`Snapshot ${String(snapshot.id)} not found`);\n }\n stored.name = name;\n stored.updatedAt = nextTimestamp();\n },\n\n async remove(snapshot) {\n const index = snapshots.findIndex((s) => s.id === snapshot.id);\n if (index === -1) {\n throw new Error(`Snapshot ${String(snapshot.id)} not found`);\n }\n snapshots.splice(index, 1);\n contents.delete(String(snapshot.id));\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Adapter (convenience)\n// ---------------------------------------------------------------------------\n\n/**\n * Create all the options needed to wire a {@link VersioningExtension} with\n * fully in-memory storage and BlockNote JSON-based preview.\n *\n * @example\n * ```ts\n * import { VersioningExtension } from \"@blocknote/core/extensions\";\n * import { createInMemoryVersioningAdapter } from \"@blocknote/core/extensions\";\n *\n * const editor = BlockNoteEditor.create({\n * extensions: [\n * VersioningExtension(createInMemoryVersioningAdapter(editor)),\n * ],\n * });\n * ```\n */\nexport function createInMemoryVersioningAdapter(\n editor: BlockNoteEditor<any, any, any>,\n): VersioningExtensionOptions<Block<any, any, any>[], Block<any, any, any>[]> {\n const endpoints = createInMemoryVersioningEndpoints();\n\n return {\n // The raw endpoints are pure snapshot storage. The \"current version\" is a\n // view concern owned by the adapter (it's the layer that knows about the\n // live editor), so we wrap `list()` to always surface a current entry: the\n // live document is the editable working copy, and the entry is how the user\n // returns to live editing and compares against saved snapshots. No\n // timestamp/author is tracked, so the row just reads \"Current version\"\n // (see CurrentSnapshot in @blocknote/react).\n endpoints: {\n ...endpoints,\n list: async () => {\n const current: VersionSnapshot = {\n id: CURRENT_VERSION_ID,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n };\n return [current, ...(await endpoints.list())];\n },\n },\n preview: createInMemoryPreviewController(editor),\n getCurrentDocument: () => editor.document,\n // The live document is already in the snapshot content format (`Block[]`),\n // so previewing \"current\" as a diff just reuses the live blocks.\n serializeCurrentContent: () => editor.document,\n };\n}\n"],"mappings":"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"}