{"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 + `![${alt}](${src})\\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 + `![${name}](${src})\\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 + `![${descriptor}](${src})\\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, \"&amp;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\");\n}\n\nfunction escapeHtmlText(value: string): string {\n  return value\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\");\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 += `![${alt}](${src})`;\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 += `![${name}](${src})`;\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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"}