{"version":3,"file":"src-D5R5YzV7.cjs","names":[],"sources":["../src/schema/inlineContent/createSpec.ts","../src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts","../src/api/blockManipulation/commands/replaceBlocks/util/fixColumnList.ts","../src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts","../src/api/exporters/html/util/serializeBlocksInternalHTML.ts","../src/api/exporters/html/internalHTMLSerializer.ts","../src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts","../src/api/blockManipulation/commands/nestBlock/nestBlock.ts","../src/api/blockManipulation/getBlock/getBlock.ts","../src/editor/managers/BlockManager.ts","../src/editor/managers/EventManager.ts","../src/api/parsers/html/util/nestedLists.ts","../src/api/parsers/html/util/normalizeWhitespace.ts","../src/api/parsers/html/parseHTML.ts","../src/api/parsers/markdown/markdownToHtml.ts","../src/api/parsers/markdown/parseMarkdown.ts","../src/editor/managers/ExportManager.ts","../src/api/blockManipulation/selections/textCursorPosition.ts","../src/extensions/tiptap-extensions/Link/helpers/tlds.ts","../src/extensions/tiptap-extensions/Link/helpers/linkDetector.ts","../src/extensions/tiptap-extensions/Link/helpers/whitespace.ts","../src/extensions/tiptap-extensions/Link/helpers/autolink.ts","../src/extensions/tiptap-extensions/Link/helpers/clickHandler.ts","../src/extensions/tiptap-extensions/Link/helpers/pasteHandler.ts","../src/extensions/tiptap-extensions/Link/link.ts","../src/api/clipboard/fromClipboard/acceptedMIMETypes.ts","../src/api/clipboard/fromClipboard/handleFileInsertion.ts","../src/api/clipboard/fromClipboard/fileDropExtension.ts","../src/api/parsers/markdown/detectMarkdown.ts","../src/api/clipboard/fromClipboard/handleVSCodePaste.ts","../src/api/clipboard/fromClipboard/pasteExtension.ts","../src/api/clipboard/toClipboard/copyExtension.ts","../src/extensions/tiptap-extensions/BackgroundColor/BackgroundColorExtension.ts","../src/extensions/tiptap-extensions/HardBreak/HardBreak.ts","../src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts","../src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts","../src/extensions/tiptap-extensions/Suggestions/SuggestionMarks.ts","../src/extensions/tiptap-extensions/TextAlignment/TextAlignmentExtension.ts","../src/extensions/tiptap-extensions/TextColor/TextColorExtension.ts","../src/pm-nodes/BlockContainer.ts","../src/pm-nodes/BlockGroup.ts","../src/pm-nodes/Doc.ts","../src/extensions/Collaboration/Collaboration.ts","../src/editor/managers/ExtensionManager/extensions.ts","../src/editor/managers/ExtensionManager/index.ts","../src/util/expandToWords.ts","../src/api/blockManipulation/selections/selection.ts","../src/editor/managers/SelectionManager.ts","../src/editor/managers/StateManager.ts","../src/api/blockManipulation/insertContentAt.ts","../src/editor/managers/StyleManager.ts","../src/editor/transformPasted.ts","../src/editor/BlockNoteEditor.ts","../src/exporter/Exporter.ts","../src/exporter/mapping.ts","../src/util/combineByGroup.ts"],"sourcesContent":["import { Node } from \"@tiptap/core\";\n\nimport { TagParseRule } from \"@tiptap/pm/model\";\nimport { inlineContentToNodes } from \"../../api/nodeConversions/blockToNode.js\";\nimport { nodeToCustomInlineContent } from \"../../api/nodeConversions/nodeToBlock.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport { propsToAttributes } from \"../blocks/internal.js\";\nimport { Props } from \"../propTypes.js\";\nimport { StyleSchema } from \"../styles/types.js\";\nimport {\n  addInlineContentAttributes,\n  addInlineContentKeyboardShortcuts,\n  createInlineContentSpecFromTipTapNode,\n} from \"./internal.js\";\nimport {\n  CustomInlineContentConfig,\n  InlineContentFromConfig,\n  InlineContentSpec,\n  PartialCustomInlineContentFromConfig,\n} from \"./types.js\";\n\nexport type CustomInlineContentImplementation<\n  T extends CustomInlineContentConfig,\n  S extends StyleSchema,\n> = {\n  meta?: {\n    draggable?: boolean;\n  };\n\n  /**\n   * Parses an external HTML element into a inline content of this type when it returns the block props object, otherwise undefined\n   */\n  parse?: (el: HTMLElement) => Partial<Props<T[\"propSchema\"]>> | undefined;\n\n  /**\n   * Renders an inline content to DOM elements\n   */\n  render: (\n    /**\n     * The custom inline content to render\n     */\n    inlineContent: InlineContentFromConfig<T, S>,\n    /**\n     * A callback that allows overriding the inline content element\n     */\n    updateInlineContent: (\n      update: PartialCustomInlineContentFromConfig<T, S>,\n    ) => void,\n    /**\n     * The BlockNote editor instance\n     * This is typed generically. If you want an editor with your custom schema, you need to\n     * cast it manually, e.g.: `const e = editor as BlockNoteEditor<typeof mySchema>;`\n     */\n    editor: BlockNoteEditor<any, any, S>,\n    // (note) if we want to fix the manual cast, we need to prevent circular references and separate block definition and render implementations\n    // or allow manually passing <BSchema>, but that's not possible without passing the other generics because Typescript doesn't support partial inferred generics\n  ) => {\n    dom: HTMLElement;\n    contentDOM?: HTMLElement;\n    destroy?: () => void;\n  };\n\n  /**\n   * Renders an inline content to external HTML elements for use outside the editor\n   * If not provided, falls back to the render method\n   */\n  toExternalHTML?: (\n    /**\n     * The custom inline content to render\n     */\n    inlineContent: InlineContentFromConfig<T, S>,\n    /**\n     * The BlockNote editor instance\n     * This is typed generically. If you want an editor with your custom schema, you need to\n     * cast it manually, e.g.: `const e = editor as BlockNoteEditor<typeof mySchema>;`\n     */\n    editor: BlockNoteEditor<any, any, S>,\n  ) =>\n    | {\n        dom: HTMLElement | DocumentFragment;\n        contentDOM?: HTMLElement;\n      }\n    | undefined;\n\n  runsBefore?: string[];\n};\n\nexport function getInlineContentParseRules<C extends CustomInlineContentConfig>(\n  config: C,\n  customParseFunction?: CustomInlineContentImplementation<C, any>[\"parse\"],\n) {\n  const rules: TagParseRule[] = [\n    {\n      tag: `[data-inline-content-type=\"${config.type}\"]`,\n      contentElement: (element) => {\n        const htmlElement = element as HTMLElement;\n\n        if (htmlElement.matches(\"[data-editable]\")) {\n          return htmlElement;\n        }\n\n        return htmlElement.querySelector(\"[data-editable]\") || htmlElement;\n      },\n    },\n  ];\n\n  if (customParseFunction) {\n    rules.push({\n      tag: \"*\",\n      getAttrs(node: string | HTMLElement) {\n        if (typeof node === \"string\") {\n          return false;\n        }\n\n        const props = customParseFunction?.(node);\n\n        if (props === undefined) {\n          return false;\n        }\n\n        return props;\n      },\n    });\n  }\n  return rules;\n}\n\nexport function createInlineContentSpec<\n  T extends CustomInlineContentConfig,\n  S extends StyleSchema,\n>(\n  inlineContentConfig: T,\n  inlineContentImplementation: CustomInlineContentImplementation<T, S>,\n): InlineContentSpec<T> {\n  const node = Node.create({\n    name: inlineContentConfig.type,\n    inline: true,\n    group: \"inline\",\n    draggable: inlineContentImplementation.meta?.draggable,\n    selectable: inlineContentConfig.content === \"styled\",\n    atom: inlineContentConfig.content === \"none\",\n    content: inlineContentConfig.content === \"styled\" ? \"inline*\" : \"\",\n\n    addAttributes() {\n      return propsToAttributes(inlineContentConfig.propSchema);\n    },\n\n    addKeyboardShortcuts() {\n      return addInlineContentKeyboardShortcuts(inlineContentConfig);\n    },\n\n    parseHTML() {\n      return getInlineContentParseRules(\n        inlineContentConfig,\n        inlineContentImplementation.parse,\n      );\n    },\n\n    renderHTML({ node }) {\n      const editor = this.options.editor;\n\n      const output = inlineContentImplementation.render.call(\n        { renderType: \"dom\", props: undefined },\n        nodeToCustomInlineContent(\n          node,\n          editor.schema.inlineContentSchema,\n          editor.schema.styleSchema,\n        ) as any as InlineContentFromConfig<T, S>, // TODO: fix cast\n        () => {\n          // No-op\n        },\n        editor,\n      );\n\n      return addInlineContentAttributes(\n        output,\n        inlineContentConfig.type,\n        node.attrs as Props<T[\"propSchema\"]>,\n        inlineContentConfig.propSchema,\n      );\n    },\n\n    addNodeView() {\n      return (props) => {\n        const { node, getPos } = props;\n        const editor = this.options.editor as BlockNoteEditor<any, any, S>;\n\n        const output = inlineContentImplementation.render.call(\n          { renderType: \"nodeView\", props },\n          nodeToCustomInlineContent(\n            node,\n            editor.schema.inlineContentSchema,\n            editor.schema.styleSchema,\n          ) as any as InlineContentFromConfig<T, S>, // TODO: fix cast\n          (update) => {\n            const content = inlineContentToNodes([update], editor.pmSchema);\n\n            const pos = getPos();\n\n            if (!pos) {\n              return;\n            }\n\n            editor.transact((tr) =>\n              tr.replaceWith(pos, pos + node.nodeSize, content),\n            );\n          },\n          editor,\n        );\n\n        return addInlineContentAttributes(\n          output,\n          inlineContentConfig.type,\n          node.attrs as Props<T[\"propSchema\"]>,\n          inlineContentConfig.propSchema,\n        );\n      };\n    },\n  });\n\n  return createInlineContentSpecFromTipTapNode(\n    node,\n    inlineContentConfig.propSchema,\n    {\n      ...inlineContentImplementation,\n      toExternalHTML: inlineContentImplementation.toExternalHTML,\n      render(inlineContent, updateInlineContent, editor) {\n        const output = inlineContentImplementation.render(\n          inlineContent,\n          updateInlineContent,\n          editor,\n        );\n\n        return addInlineContentAttributes(\n          output,\n          inlineContentConfig.type,\n          inlineContent.props,\n          inlineContentConfig.propSchema,\n        );\n      },\n    },\n  ) as InlineContentSpec<T>;\n}\n","import { Fragment, Slice } from \"prosemirror-model\";\nimport type { Transaction } from \"prosemirror-state\";\nimport { ReplaceStep } from \"prosemirror-transform\";\nimport { Block, PartialBlock } from \"../../../../blocks/defaultBlocks.js\";\nimport {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../../schema/index.js\";\nimport { blockToNode } from \"../../../nodeConversions/blockToNode.js\";\nimport { nodeToBlock } from \"../../../nodeConversions/nodeToBlock.js\";\nimport { getNodeById } from \"../../../nodeUtil.js\";\nimport { getPmSchema } from \"../../../pmUtil.js\";\n\nexport function insertBlocks<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  tr: Transaction,\n  blocksToInsert: PartialBlock<BSchema, I, S>[],\n  referenceBlock: BlockIdentifier,\n  placement: \"before\" | \"after\" = \"before\",\n): Block<BSchema, I, S>[] {\n  const id =\n    typeof referenceBlock === \"string\" ? referenceBlock : referenceBlock.id;\n  const pmSchema = getPmSchema(tr);\n  const nodesToInsert = blocksToInsert.map((block) => {\n    const node = blockToNode(block, pmSchema);\n    node.check(); // `blockToNode` is lenient; validate before mutating the doc\n    return node;\n  });\n\n  const posInfo = getNodeById(id, tr.doc);\n  if (!posInfo) {\n    throw new Error(`Block with ID ${id} not found`);\n  }\n\n  let pos = posInfo.posBeforeNode;\n  if (placement === \"after\") {\n    pos += posInfo.node.nodeSize;\n  }\n\n  tr.step(\n    new ReplaceStep(pos, pos, new Slice(Fragment.from(nodesToInsert), 0, 0)),\n  );\n\n  // Now that the `PartialBlock`s have been converted to nodes, we can\n  // re-convert them into full `Block`s.\n  const insertedBlocks = nodesToInsert.map((node) =>\n    nodeToBlock(node, pmSchema),\n  ) as Block<BSchema, I, S>[];\n\n  return insertedBlocks;\n}\n","import { Slice, type Node } from \"prosemirror-model\";\nimport { type Transaction } from \"prosemirror-state\";\nimport { ReplaceAroundStep } from \"prosemirror-transform\";\n\n/**\n * Checks if a `column` node is empty, i.e. if it has only a single empty\n * paragraph.\n * @param column The column to check.\n * @returns Whether the column is empty.\n */\nexport function isEmptyColumn(column: Node) {\n  if (!column || column.type.name !== \"column\") {\n    throw new Error(\"Invalid columnPos: does not point to column node.\");\n  }\n\n  const blockContainer = column.firstChild;\n  if (!blockContainer) {\n    throw new Error(\"Invalid column: does not have child node.\");\n  }\n\n  const blockContent = blockContainer.firstChild;\n  if (!blockContent) {\n    throw new Error(\"Invalid blockContainer: does not have child node.\");\n  }\n\n  return (\n    column.childCount === 1 &&\n    blockContainer.childCount === 1 &&\n    blockContent.type.name === \"paragraph\" &&\n    blockContent.content.content.length === 0\n  );\n}\n\n/**\n * Removes all empty `column` nodes in a `columnList`. A `column` node is empty\n * if it has only a single empty block. If, however, removing the `column`s\n * leaves the `columnList` that has fewer than two, ProseMirror will re-add\n * empty columns.\n * @param tr The `Transaction` to add the changes to.\n * @param columnListPos The position just before the `columnList` node.\n */\nexport function removeEmptyColumns(tr: Transaction, columnListPos: number) {\n  const $columnListPos = tr.doc.resolve(columnListPos);\n  const columnList = $columnListPos.nodeAfter;\n  if (!columnList || columnList.type.name !== \"columnList\") {\n    throw new Error(\n      \"Invalid columnListPos: does not point to columnList node.\",\n    );\n  }\n\n  for (\n    let columnIndex = columnList.childCount - 1;\n    columnIndex >= 0;\n    columnIndex--\n  ) {\n    const columnPos = tr.doc\n      .resolve($columnListPos.pos + 1)\n      .posAtIndex(columnIndex);\n    const $columnPos = tr.doc.resolve(columnPos);\n    const column = $columnPos.nodeAfter;\n    if (!column || column.type.name !== \"column\") {\n      throw new Error(\"Invalid columnPos: does not point to column node.\");\n    }\n\n    if (isEmptyColumn(column)) {\n      tr.delete(columnPos, columnPos + column.nodeSize);\n    }\n  }\n}\n\n/**\n * Fixes potential issues in a `columnList` node after a\n * `blockContainer`/`column` node is (re)moved from it:\n *\n * - Removes all empty `column` nodes. A `column` node is empty if it has only\n * a single empty block.\n * - If all but one `column` nodes are empty, replaces the `columnList` with\n * the content of the non-empty `column`.\n * - If all `column` nodes are empty, removes the `columnList` entirely.\n * @param tr The `Transaction` to add the changes to.\n * @param columnListPos\n * @returns The position just before the `columnList` node.\n */\nexport function fixColumnList(tr: Transaction, columnListPos: number) {\n  removeEmptyColumns(tr, columnListPos);\n\n  const $columnListPos = tr.doc.resolve(columnListPos);\n  const columnList = $columnListPos.nodeAfter;\n  if (!columnList || columnList.type.name !== \"columnList\") {\n    throw new Error(\n      \"Invalid columnListPos: does not point to columnList node.\",\n    );\n  }\n\n  if (columnList.childCount > 2) {\n    // Do nothing if the `columnList` has more than two non-empty `column`s. In\n    // the case that the `columnList` has exactly two columns, we may need to\n    // still remove it, as it's possible that one or both columns are empty.\n    // This is because after `removeEmptyColumns` is called, if the\n    // `columnList` has fewer than two `column`s, ProseMirror will re-add empty\n    // `column`s until there are two total, in order to fit the schema.\n    return;\n  }\n\n  if (columnList.childCount < 2) {\n    // Throw an error if the `columnList` has fewer than two columns. After\n    // `removeEmptyColumns` is called, if the `columnList` has fewer than two\n    // `column`s, ProseMirror will re-add empty `column`s until there are two\n    // total, in order to fit the schema. So if there are fewer than two here,\n    // either the schema, or ProseMirror's internals, must have changed.\n    throw new Error(\"Invalid columnList: contains fewer than two children.\");\n  }\n\n  const firstColumnBeforePos = columnListPos + 1;\n  const $firstColumnBeforePos = tr.doc.resolve(firstColumnBeforePos);\n  const firstColumn = $firstColumnBeforePos.nodeAfter;\n\n  const lastColumnAfterPos = columnListPos + columnList.nodeSize - 1;\n  const $lastColumnAfterPos = tr.doc.resolve(lastColumnAfterPos);\n  const lastColumn = $lastColumnAfterPos.nodeBefore;\n\n  if (!firstColumn || !lastColumn) {\n    throw new Error(\"Invalid columnList: does not contain children.\");\n  }\n\n  const firstColumnEmpty = isEmptyColumn(firstColumn);\n  const lastColumnEmpty = isEmptyColumn(lastColumn);\n\n  if (firstColumnEmpty && lastColumnEmpty) {\n    // Removes `columnList`\n    tr.delete(columnListPos, columnListPos + columnList.nodeSize);\n\n    return;\n  }\n\n  if (firstColumnEmpty) {\n    tr.step(\n      new ReplaceAroundStep(\n        // Replaces `columnList`.\n        columnListPos,\n        columnListPos + columnList.nodeSize,\n        // Replaces with content of last `column`.\n        lastColumnAfterPos - lastColumn.nodeSize + 1,\n        lastColumnAfterPos - 1,\n        // Doesn't append anything.\n        Slice.empty,\n        0,\n        false,\n      ),\n    );\n\n    return;\n  }\n\n  if (lastColumnEmpty) {\n    tr.step(\n      new ReplaceAroundStep(\n        // Replaces `columnList`.\n        columnListPos,\n        columnListPos + columnList.nodeSize,\n        // Replaces with content of first `column`.\n        firstColumnBeforePos + 1,\n        firstColumnBeforePos + firstColumn.nodeSize - 1,\n        // Doesn't append anything.\n        Slice.empty,\n        0,\n        false,\n      ),\n    );\n\n    return;\n  }\n}\n","import { type Node } from \"prosemirror-model\";\nimport { type Transaction } from \"prosemirror-state\";\nimport type { Block, PartialBlock } from \"../../../../blocks/defaultBlocks.js\";\nimport type {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../../schema/index.js\";\nimport { blockToNode } from \"../../../nodeConversions/blockToNode.js\";\nimport { nodeToBlock } from \"../../../nodeConversions/nodeToBlock.js\";\nimport { getPmSchema } from \"../../../pmUtil.js\";\nimport { fixColumnList } from \"./util/fixColumnList.js\";\n\nexport function removeAndInsertBlocks<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  tr: Transaction,\n  blocksToRemove: BlockIdentifier[],\n  blocksToInsert: PartialBlock<BSchema, I, S>[],\n): {\n  insertedBlocks: Block<BSchema, I, S>[];\n  removedBlocks: Block<BSchema, I, S>[];\n} {\n  const pmSchema = getPmSchema(tr);\n  // Converts the `PartialBlock`s to ProseMirror nodes to insert them into the\n  // document.\n  const nodesToInsert: Node[] = blocksToInsert.map((block) => {\n    const node = blockToNode(block, pmSchema);\n    node.check(); // `blockToNode` is lenient; validate before mutating the doc\n    return node;\n  });\n\n  const idsOfBlocksToRemove = new Set<string>(\n    blocksToRemove.map((block) =>\n      typeof block === \"string\" ? block : block.id,\n    ),\n  );\n  const removedBlocks: Block<BSchema, I, S>[] = [];\n  const columnListPositions = new Set<number>();\n\n  const idOfFirstBlock =\n    typeof blocksToRemove[0] === \"string\"\n      ? blocksToRemove[0]\n      : blocksToRemove[0].id;\n  let removedSize = 0;\n\n  tr.doc.descendants((node, pos) => {\n    // Skips traversing nodes after all target blocks have been removed.\n    if (idsOfBlocksToRemove.size === 0) {\n      return false;\n    }\n\n    // Keeps traversing nodes if block with target ID has not been found.\n    if (\n      !node.type.isInGroup(\"bnBlock\") ||\n      !idsOfBlocksToRemove.has(node.attrs.id)\n    ) {\n      return true;\n    }\n\n    // Saves the block that is being deleted.\n    removedBlocks.push(nodeToBlock(node, pmSchema));\n    idsOfBlocksToRemove.delete(node.attrs.id);\n\n    if (blocksToInsert.length > 0 && node.attrs.id === idOfFirstBlock) {\n      const oldDocSize = tr.doc.nodeSize;\n      tr.insert(pos, nodesToInsert);\n      const newDocSize = tr.doc.nodeSize;\n\n      removedSize += oldDocSize - newDocSize;\n    }\n\n    const oldDocSize = tr.doc.nodeSize;\n\n    const $pos = tr.doc.resolve(pos - removedSize);\n\n    if ($pos.node().type.name === \"column\") {\n      columnListPositions.add($pos.before(-1));\n    } else if ($pos.node().type.name === \"columnList\") {\n      columnListPositions.add($pos.before());\n    }\n\n    if (\n      $pos.node().type.name === \"blockGroup\" &&\n      $pos.node($pos.depth - 1).type.name !== \"doc\" &&\n      $pos.node().childCount === 1\n    ) {\n      // Checks if the block is the only child of a parent `blockGroup` node.\n      // In this case, we need to delete the parent `blockGroup` node instead\n      // of just the `blockContainer`.\n      tr.delete($pos.before(), $pos.after());\n    } else {\n      tr.delete(pos - removedSize, pos - removedSize + node.nodeSize);\n    }\n\n    const newDocSize = tr.doc.nodeSize;\n    removedSize += oldDocSize - newDocSize;\n\n    return false;\n  });\n\n  // Throws an error if not all blocks could be found.\n  if (idsOfBlocksToRemove.size > 0) {\n    const notFoundIds = [...idsOfBlocksToRemove].join(\"\\n\");\n\n    throw Error(\n      \"Blocks with the following IDs could not be found in the editor: \" +\n        notFoundIds,\n    );\n  }\n\n  columnListPositions.forEach((pos) => fixColumnList(tr, pos));\n\n  // Converts the nodes created from `blocksToInsert` into full `Block`s.\n  const insertedBlocks = nodesToInsert.map((node) =>\n    nodeToBlock(node, pmSchema),\n  ) as Block<BSchema, I, S>[];\n\n  return { insertedBlocks, removedBlocks };\n}\n","import { DOMSerializer, Fragment, Node } 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 { UnreachableCaseError } from \"../../../../util/typescript.js\";\nimport {\n  inlineContentToNodes,\n  tableContentToNodes,\n} from \"../../../nodeConversions/blockToNode.js\";\n\nimport { nodeToCustomInlineContent } from \"../../../nodeConversions/nodeToBlock.js\";\nexport function serializeInlineContentInternalHTML<\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  blockType?: string,\n  options?: { document?: Document },\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([blockContent], editor.pmSchema, blockType);\n  } else if (Array.isArray(blockContent)) {\n    nodes = inlineContentToNodes(blockContent, editor.pmSchema, blockType);\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\n        const output = 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 = editor.schema.styleSpecs[\n            mark.type.name\n          ].implementation.render(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  return fragment;\n}\n\nfunction serializeBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n  block: PartialBlock<BSchema, I, S>,\n  serializer: DOMSerializer,\n  options?: { document?: Document },\n) {\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  const children = block.children || [];\n\n  const impl = editor.blockImplementations[block.type as any].implementation;\n  const ret = impl.render.call(\n    {\n      renderType: \"dom\",\n      props: undefined,\n    },\n    { ...block, props, children } as any,\n    editor as any,\n  );\n\n  if (ret.contentDOM && block.content) {\n    const ic = serializeInlineContentInternalHTML(\n      editor,\n      block.content as any, // TODO\n      serializer,\n      block.type,\n      options,\n    );\n    ret.contentDOM.appendChild(ic);\n  }\n\n  const pmType = editor.pmSchema.nodes[block.type as any];\n\n  if (pmType.isInGroup(\"bnBlock\")) {\n    if (block.children && block.children.length > 0) {\n      const fragment = serializeBlocks(\n        editor,\n        block.children,\n        serializer,\n        options,\n      );\n\n      ret.contentDOM?.append(fragment);\n    }\n    return ret.dom;\n  }\n\n  // wrap the block in a blockContainer\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  bc.contentDOM?.appendChild(ret.dom);\n\n  if (block.children && block.children.length > 0) {\n    bc.contentDOM?.appendChild(\n      serializeBlocksInternalHTML(editor, block.children, serializer, options),\n    );\n  }\n  return bc.dom;\n}\n\nfunction serializeBlocks<\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  options?: { document?: Document },\n) {\n  const doc = options?.document ?? document;\n  const fragment = doc.createDocumentFragment();\n\n  for (const block of blocks) {\n    const blockDOM = serializeBlock(editor, block, serializer, options);\n    fragment.appendChild(blockDOM);\n  }\n\n  return fragment;\n}\n\nexport const serializeBlocksInternalHTML = <\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  options?: { document?: Document },\n) => {\n  const BG_NODE = editor.pmSchema.nodes[\"blockGroup\"];\n\n  const bg = BG_NODE.spec!.toDOM!(BG_NODE.create({})) as {\n    dom: HTMLElement;\n    contentDOM?: HTMLElement;\n  };\n\n  const fragment = serializeBlocks(editor, blocks, serializer, options);\n\n  bg.contentDOM?.appendChild(fragment);\n\n  return bg.dom;\n};\n","import { DOMSerializer, Schema } from \"prosemirror-model\";\n\nimport { PartialBlock } from \"../../../blocks/defaultBlocks.js\";\nimport { EMPTY_CELL_WIDTH } from \"../../../blocks/index.js\";\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { serializeBlocksInternalHTML } from \"./util/serializeBlocksInternalHTML.js\";\n\n// This is normally handled using decorations in the\n// `NumberedListIndexingDecorationPlugin`. This does not run when exporting, so\n// we have to add the necessary HTML attributes ourselves.\nconst addIndexToNumberedListItems = (element: HTMLElement) => {\n  const numberedListItems = element.querySelectorAll(\n    '[data-content-type=\"numberedListItem\"]',\n  );\n  numberedListItems.forEach((numberedListItem) => {\n    const prevNumberedListItem = numberedListItem\n      .closest(\".bn-block-outer\")\n      ?.previousElementSibling?.querySelector(\n        '[data-content-type=\"numberedListItem\"]',\n      );\n\n    if (!prevNumberedListItem) {\n      numberedListItem.setAttribute(\n        \"data-index\",\n        numberedListItem.getAttribute(\"data-start\") || \"1\",\n      );\n    } else {\n      const prevNumberedListItemIndex =\n        prevNumberedListItem.getAttribute(\"data-index\");\n      numberedListItem.setAttribute(\n        \"data-index\",\n        (parseInt(prevNumberedListItemIndex || \"0\") + 1).toString(),\n      );\n    }\n  });\n\n  return element;\n};\n\n// Makes the checkboxes in check list items read-only, as the HTML should be\n// static and therefore read-only when rendered.\nconst makeCheckListItemsReadOnly = (element: HTMLElement) => {\n  const checkboxes: NodeListOf<HTMLInputElement> = element.querySelectorAll(\n    '[data-content-type=\"checkListItem\"] input',\n  );\n  checkboxes.forEach((checkbox) => {\n    checkbox.disabled = true;\n  });\n\n  return element;\n};\n\n// Forces toggle blocks (toggle headings, toggle list items) to be expanded.\n// This is because event listeners for the toggle button are lost when\n// serializing HTML elements to a string, so the button no longer works if the\n// HTML string is rendered out.\nconst forceToggleBlocksShow = (element: HTMLElement) => {\n  const hiddenToggleWrappers = element.querySelectorAll(\n    '.bn-toggle-wrapper[data-show-children=\"false\"]',\n  );\n  hiddenToggleWrappers.forEach((toggleWrapper) => {\n    toggleWrapper.setAttribute(\"data-show-children\", \"true\");\n  });\n\n  return element;\n};\n\n// Adds minimum cell widths, which would normally be done by the\n// `columnResizing` extension. This extension doesn't run when exporting to\n// HTML, so we have to add this manually.\nconst addTableMinCellWidths = (element: HTMLElement) => {\n  const tables = element.querySelectorAll('[data-content-type=\"table\"] table');\n  tables.forEach((table) => {\n    table.setAttribute(\n      \"style\",\n      `--default-cell-min-width: ${EMPTY_CELL_WIDTH}px;`,\n    );\n    table.setAttribute(\"data-show-children\", \"true\");\n  });\n\n  return element;\n};\n\n// Adds table wrapping elements, which would normally be done by the\n// `columnResizing` extension. This extension doesn't run when exporting to\n// HTML, so we have to add this manually. This adds the correct padding to\n// tables.\nconst addTableWrappers = (element: HTMLElement) => {\n  const tables = element.querySelectorAll('[data-content-type=\"table\"] table');\n  tables.forEach((table) => {\n    const tableWrapper = document.createElement(\"div\");\n    tableWrapper.className = \"tableWrapper\";\n    const tableWrapperInner = document.createElement(\"div\");\n    tableWrapperInner.className = \"tableWrapper-inner\";\n\n    tableWrapper.appendChild(tableWrapperInner);\n    table.parentElement?.appendChild(tableWrapper);\n    tableWrapper.appendChild(table);\n  });\n\n  return element;\n};\n\n// Adds trailing breaks to blocks with empty inline content. This is normally\n// done by ProseMirror, but only when rendering an actual editor. Without them,\n// empty inline content has a height of 0.\nconst addTrailingBreakToEmptyInlineContent = (element: HTMLElement) => {\n  const emptyInlineContent = element.querySelectorAll(\n    \".bn-inline-content:empty\",\n  );\n  emptyInlineContent.forEach((inlineContent) => {\n    // We actually use a `span` instead of a `br` to avoid potential false\n    // positives when parsing.\n    const trailingBreak = document.createElement(\"span\");\n    trailingBreak.className = \"ProseMirror-trailingBreak\";\n    trailingBreak.setAttribute(\"style\", \"display: inline-block;\");\n\n    inlineContent.appendChild(trailingBreak);\n  });\n\n  return element;\n};\n\n// Used to serialize BlockNote blocks and ProseMirror nodes to HTML without\n// losing data. Blocks are exported using the `toInternalHTML` method in their\n// `blockSpec`.\n//\n// The HTML created by this serializer is the same as what's rendered by the\n// editor to the DOM. This means that it retains the same structure as the\n// editor, including the `blockGroup` and `blockContainer` wrappers. This also\n// means that it can be converted back to the original blocks without any data\n// loss.\nexport const createInternalHTMLSerializer = <\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  // Set of transforms to run on the output HTML element after serializing\n  // blocks. These are used to add HTML elements, attributes, or class names\n  // which would normally be done by extensions and plugins. Since these don't\n  // run when converting blocks to HTML, tranforms are used to mock their\n  // functionality so that the rendered HTML looks identical to that of a live\n  // editor.\n  const transforms: ((element: HTMLElement) => HTMLElement)[] = [\n    addIndexToNumberedListItems,\n    makeCheckListItemsReadOnly,\n    forceToggleBlocksShow,\n    addTableMinCellWidths,\n    addTableWrappers,\n    addTrailingBreakToEmptyInlineContent,\n  ];\n\n  return {\n    serializeBlocks: (\n      blocks: PartialBlock<BSchema, I, S>[],\n      options: { document?: Document },\n    ) => {\n      let element = serializeBlocksInternalHTML(\n        editor,\n        blocks,\n        serializer,\n        options,\n      );\n\n      for (const transform of transforms) {\n        element = transform(element);\n      }\n\n      return element.outerHTML;\n    },\n  };\n};\n","import {\n  NodeSelection,\n  Selection,\n  TextSelection,\n  Transaction,\n} from \"prosemirror-state\";\nimport { CellSelection } from \"prosemirror-tables\";\n\nimport { Block } from \"../../../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../../../editor/BlockNoteEditor\";\nimport { BlockIdentifier } from \"../../../../schema/index.js\";\nimport { getNearestBlockPos } from \"../../../getBlockInfoFromPos.js\";\nimport { getNodeById } from \"../../../nodeUtil.js\";\n\ntype BlockSelectionData = (\n  | {\n      type: \"text\";\n      headBlockId: string;\n      anchorOffset: number;\n      headOffset: number;\n    }\n  | {\n      type: \"node\";\n    }\n  | {\n      type: \"cell\";\n      anchorCellOffset: number;\n      headCellOffset: number;\n    }\n) & {\n  anchorBlockId: string;\n};\n\n/**\n * `getBlockSelectionData` and `updateBlockSelectionFromData` are used to save\n * and restore the selection within a block, when the block is moved. This is\n * done by first saving the offsets of the anchor and head from the before\n * positions of their surrounding blocks, as well as the IDs of those blocks. We\n * can then recreate the selection by finding the blocks with those IDs, getting\n * their before positions, and adding the offsets to those positions.\n * @param editor The BlockNote editor instance to get the selection data from.\n */\nfunction getBlockSelectionData(\n  editor: BlockNoteEditor<any, any, any>,\n): BlockSelectionData {\n  return editor.transact((tr) => {\n    const anchorBlockPosInfo = getNearestBlockPos(tr.doc, tr.selection.anchor);\n\n    if (tr.selection instanceof CellSelection) {\n      return {\n        type: \"cell\" as const,\n        anchorBlockId: anchorBlockPosInfo.node.attrs.id,\n        anchorCellOffset:\n          tr.selection.$anchorCell.pos - anchorBlockPosInfo.posBeforeNode,\n        headCellOffset:\n          tr.selection.$headCell.pos - anchorBlockPosInfo.posBeforeNode,\n      };\n    } else if (tr.selection instanceof NodeSelection) {\n      return {\n        type: \"node\" as const,\n        anchorBlockId: anchorBlockPosInfo.node.attrs.id,\n      };\n    } else {\n      const headBlockPosInfo = getNearestBlockPos(tr.doc, tr.selection.head);\n\n      return {\n        type: \"text\" as const,\n        anchorBlockId: anchorBlockPosInfo.node.attrs.id,\n        headBlockId: headBlockPosInfo.node.attrs.id,\n        anchorOffset: tr.selection.anchor - anchorBlockPosInfo.posBeforeNode,\n        headOffset: tr.selection.head - headBlockPosInfo.posBeforeNode,\n      };\n    }\n  });\n}\n\n/**\n * `getBlockSelectionData` and `updateBlockSelectionFromData` are used to save\n * and restore the selection within a block, when the block is moved. This is\n * done by first saving the offsets of the anchor and head from the before\n * positions of their surrounding blocks, as well as the IDs of those blocks. We\n * can then recreate the selection by finding the blocks with those IDs, getting\n * their before positions, and adding the offsets to those positions.\n * @param tr The transaction to update the selection in.\n * @param data The selection data to update the selection with (generated by\n * `getBlockSelectionData`).\n */\nfunction updateBlockSelectionFromData(\n  tr: Transaction,\n  data: BlockSelectionData,\n) {\n  const anchorBlockPos = getNodeById(data.anchorBlockId, tr.doc)?.posBeforeNode;\n  if (anchorBlockPos === undefined) {\n    throw new Error(\n      `Could not find block with ID ${data.anchorBlockId} to update selection`,\n    );\n  }\n\n  let selection: Selection;\n  if (data.type === \"cell\") {\n    selection = CellSelection.create(\n      tr.doc,\n      anchorBlockPos + data.anchorCellOffset,\n      anchorBlockPos + data.headCellOffset,\n    );\n  } else if (data.type === \"node\") {\n    selection = NodeSelection.create(tr.doc, anchorBlockPos + 1);\n  } else {\n    const headBlockPos = getNodeById(data.headBlockId, tr.doc)?.posBeforeNode;\n    if (headBlockPos === undefined) {\n      throw new Error(\n        `Could not find block with ID ${data.headBlockId} to update selection`,\n      );\n    }\n\n    selection = TextSelection.create(\n      tr.doc,\n      anchorBlockPos + data.anchorOffset,\n      headBlockPos + data.headOffset,\n    );\n  }\n\n  tr.setSelection(selection);\n}\n\n// Replaces top-level `column` blocks with their children, as a `column` is not\n// a valid block outside a `columnList`. Other blocks are returned as-is.\nfunction flattenColumns(\n  blocks: Block<any, any, any>[],\n): Block<any, any, any>[] {\n  return blocks.flatMap((block) =>\n    block.type === \"column\" ? block.children : [block],\n  );\n}\n\n/**\n * Removes the given blocks from the editor, then inserts them before/after a\n * reference block.\n * @param editor The BlockNote editor instance to move the blocks in.\n * @param blocks The blocks to move.\n * @param referenceBlock The reference block to insert the blocks before/after.\n * @param placement Whether to insert the blocks before or after the reference\n * block.\n */\nexport function moveBlocks(\n  editor: BlockNoteEditor<any, any, any>,\n  blocks: Block<any, any, any>[],\n  referenceBlock: BlockIdentifier,\n  placement: \"before\" | \"after\",\n) {\n  editor.transact(() => {\n    // A `columnList` reference can be dissolved by `fixColumnList` when its\n    // `column`s are removed, leaving its ID invalid for re-insertion. Anchor\n    // to an adjacent block instead, which is unaffected by the removal.\n    const refBlock = editor.getBlock(referenceBlock);\n    if (refBlock?.type === \"columnList\") {\n      const adjacent =\n        placement === \"after\"\n          ? editor.getNextBlock(refBlock)\n          : editor.getPrevBlock(refBlock);\n      if (adjacent) {\n        referenceBlock = adjacent;\n        placement = placement === \"after\" ? \"before\" : \"after\";\n      }\n    }\n\n    editor.removeBlocks(blocks);\n    editor.insertBlocks(flattenColumns(blocks), referenceBlock, placement);\n  });\n}\n\n/**\n * Removes the selected blocks from the editor, then inserts them before/after a\n * reference block. Also updates the selection to match the original selection\n * using `getBlockSelectionData` and `updateBlockSelectionFromData`.\n * @param editor The BlockNote editor instance to move the blocks in.\n * @param referenceBlock The reference block to insert the selected blocks\n * before/after.\n * @param placement Whether to insert the selected blocks before or after the\n * reference block.\n */\nexport function moveSelectedBlocksAndSelection(\n  editor: BlockNoteEditor<any, any, any>,\n  referenceBlock: BlockIdentifier,\n  placement: \"before\" | \"after\",\n) {\n  // We want this to be a single step in the undo history\n  editor.transact((tr) => {\n    const blocks = editor.getSelection()?.blocks || [\n      editor.getTextCursorPosition().block,\n    ];\n    const selectionData = getBlockSelectionData(editor);\n\n    moveBlocks(editor, blocks, referenceBlock, placement);\n\n    updateBlockSelectionFromData(tr, selectionData);\n  });\n}\n\n// Checks if a block is in a valid place after being moved. This check is\n// primitive at the moment and only returns false if the block's parent is a\n// `columnList` block. This is because regular blocks cannot be direct children\n// of `columnList` blocks.\nfunction checkPlacementIsValid(parentBlock?: Block<any, any, any>): boolean {\n  return !parentBlock || parentBlock.type !== \"columnList\";\n}\n\n// Gets the placement for moving a block up. This has 3 cases:\n// 1. If the block has a previous sibling without children, the placement is\n// before it.\n// 2. If the block has a previous sibling with children, the placement is after\n// the last child.\n// 3. If the block has no previous sibling, but is nested, the placement is\n// before its parent.\n// If the placement is invalid, the function is called recursively until a valid\n// placement is found. Returns undefined if no valid placement is found, meaning\n// the block is already at the top of the document.\nfunction getMoveUpPlacement(\n  editor: BlockNoteEditor<any, any, any>,\n  prevBlock?: Block<any, any, any>,\n  parentBlock?: Block<any, any, any>,\n):\n  | { referenceBlock: BlockIdentifier; placement: \"before\" | \"after\" }\n  | undefined {\n  let referenceBlock: Block<any, any, any> | undefined;\n  let placement: \"before\" | \"after\" | undefined;\n\n  if (!prevBlock) {\n    if (parentBlock) {\n      referenceBlock = parentBlock;\n      placement = \"before\";\n    }\n  } else if (prevBlock.children.length > 0) {\n    referenceBlock = prevBlock.children[prevBlock.children.length - 1];\n    placement = \"after\";\n  } else {\n    referenceBlock = prevBlock;\n    placement = \"before\";\n  }\n\n  // Case when the block is already at the top of the document.\n  if (!referenceBlock || !placement) {\n    return undefined;\n  }\n\n  const referenceBlockParent = editor.getParentBlock(referenceBlock);\n  if (!checkPlacementIsValid(referenceBlockParent)) {\n    return getMoveUpPlacement(\n      editor,\n      placement === \"after\"\n        ? referenceBlock\n        : editor.getPrevBlock(referenceBlock),\n      referenceBlockParent,\n    );\n  }\n\n  return { referenceBlock, placement };\n}\n\n// Gets the placement for moving a block down. This has 3 cases:\n// 1. If the block has a next sibling without children, the placement is  after\n// it.\n// 2. If the block has a next sibling with children, the placement is before the\n// first child.\n// 3. If the block has no next sibling, but is nested, the placement is\n// after its parent.\n// If the placement is invalid, the function is called recursively until a valid\n// placement is found. Returns undefined if no valid placement is found, meaning\n// the block is already at the bottom of the document.\nfunction getMoveDownPlacement(\n  editor: BlockNoteEditor<any, any, any>,\n  nextBlock?: Block<any, any, any>,\n  parentBlock?: Block<any, any, any>,\n):\n  | { referenceBlock: BlockIdentifier; placement: \"before\" | \"after\" }\n  | undefined {\n  let referenceBlock: Block<any, any, any> | undefined;\n  let placement: \"before\" | \"after\" | undefined;\n\n  if (!nextBlock) {\n    if (parentBlock) {\n      referenceBlock = parentBlock;\n      placement = \"after\";\n    }\n  } else if (nextBlock.children.length > 0) {\n    referenceBlock = nextBlock.children[0];\n    placement = \"before\";\n  } else {\n    referenceBlock = nextBlock;\n    placement = \"after\";\n  }\n\n  // Case when the block is already at the bottom of the document.\n  if (!referenceBlock || !placement) {\n    return undefined;\n  }\n\n  const referenceBlockParent = editor.getParentBlock(referenceBlock);\n  if (!checkPlacementIsValid(referenceBlockParent)) {\n    return getMoveDownPlacement(\n      editor,\n      placement === \"before\"\n        ? referenceBlock\n        : editor.getNextBlock(referenceBlock),\n      referenceBlockParent,\n    );\n  }\n\n  return { referenceBlock, placement };\n}\n\nexport function moveBlocksUp(\n  editor: BlockNoteEditor<any, any, any>,\n  blockIdentifier?: BlockIdentifier,\n) {\n  editor.transact(() => {\n    let sourceBlock: Block<any, any, any> | undefined;\n    if (blockIdentifier) {\n      sourceBlock = editor.getBlock(blockIdentifier);\n      if (!sourceBlock) {\n        return;\n      }\n    } else {\n      const selection = editor.getSelection();\n      sourceBlock =\n        selection?.blocks[0] || editor.getTextCursorPosition().block;\n    }\n\n    const moveUpPlacement = getMoveUpPlacement(\n      editor,\n      editor.getPrevBlock(sourceBlock),\n      editor.getParentBlock(sourceBlock),\n    );\n\n    if (!moveUpPlacement) {\n      return;\n    }\n\n    if (blockIdentifier) {\n      moveBlocks(\n        editor,\n        [sourceBlock],\n        moveUpPlacement.referenceBlock,\n        moveUpPlacement.placement,\n      );\n    } else {\n      moveSelectedBlocksAndSelection(\n        editor,\n        moveUpPlacement.referenceBlock,\n        moveUpPlacement.placement,\n      );\n    }\n  });\n}\n\nexport function moveBlocksDown(\n  editor: BlockNoteEditor<any, any, any>,\n  blockIdentifier?: BlockIdentifier,\n) {\n  editor.transact(() => {\n    let sourceBlock: Block<any, any, any> | undefined;\n    if (blockIdentifier) {\n      sourceBlock = editor.getBlock(blockIdentifier);\n      if (!sourceBlock) {\n        return;\n      }\n    } else {\n      const selection = editor.getSelection();\n      sourceBlock =\n        selection?.blocks[selection?.blocks.length - 1] ||\n        editor.getTextCursorPosition().block;\n    }\n\n    const moveDownPlacement = getMoveDownPlacement(\n      editor,\n      editor.getNextBlock(sourceBlock),\n      editor.getParentBlock(sourceBlock),\n    );\n\n    if (!moveDownPlacement) {\n      return;\n    }\n\n    if (blockIdentifier) {\n      moveBlocks(\n        editor,\n        [sourceBlock],\n        moveDownPlacement.referenceBlock,\n        moveDownPlacement.placement,\n      );\n    } else {\n      moveSelectedBlocksAndSelection(\n        editor,\n        moveDownPlacement.referenceBlock,\n        moveDownPlacement.placement,\n      );\n    }\n  });\n}\n","import { Fragment, NodeRange, NodeType, Slice } from \"prosemirror-model\";\nimport { Transaction } from \"prosemirror-state\";\nimport { canJoin, liftTarget, ReplaceAroundStep } from \"prosemirror-transform\";\n\nimport { BlockNoteEditor } from \"../../../../editor/BlockNoteEditor.js\";\nimport { getBlockInfoFromTransaction } from \"../../../getBlockInfoFromPos.js\";\n\n/**\n * Modified version of prosemirror-schema-list's sinkItem.\n * https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.ts\n *\n * Changes from the original:\n * 1. Range predicate checks node.type instead of firstChild.type\n * 2. nestedBefore checks groupType instead of parent.type\n * 3. Slice creates groupType instead of parent.type\n * 4. Operates on Transaction directly instead of state+dispatch\n */\nfunction sinkItem(\n  tr: Transaction,\n  itemType: NodeType,\n  groupType: NodeType,\n) {\n  const { $from, $to } = tr.selection;\n  const range = $from.blockRange(\n    $to,\n    (node) =>\n      node.childCount > 0 &&\n      (node.type.name === \"blockGroup\" || node.type.name === \"column\"), // change 1\n  );\n  if (!range) {\n    return false;\n  }\n  const startIndex = range.startIndex;\n  if (startIndex === 0) {\n    return false;\n  }\n  const parent = range.parent;\n  const nodeBefore = parent.child(startIndex - 1);\n  if (nodeBefore.type !== itemType) {\n    return false;\n  }\n  const nestedBefore =\n    nodeBefore.lastChild && nodeBefore.lastChild.type === groupType; // change 2\n  const inner = Fragment.from(nestedBefore ? itemType.create() : null);\n  const slice = new Slice(\n    Fragment.from(\n      itemType.create(null, Fragment.from(groupType.create(null, inner))), // change 3\n    ),\n    nestedBefore ? 3 : 1,\n    0,\n  );\n\n  const before = range.start;\n  const after = range.end;\n\n  tr.step(\n    new ReplaceAroundStep(\n      before - (nestedBefore ? 3 : 1),\n      after,\n      before,\n      after,\n      slice,\n      1,\n      true,\n    ),\n  ).scrollIntoView();\n\n  return true;\n}\n\nexport function nestBlock(editor: BlockNoteEditor<any, any, any>) {\n  return editor.transact((tr) => {\n    return sinkItem(\n      tr,\n      editor.pmSchema.nodes[\"blockContainer\"],\n      editor.pmSchema.nodes[\"blockGroup\"],\n    );\n  });\n}\n\n/**\n * Modified version of prosemirror-schema-list's liftToOuterList.\n * https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.ts\n *\n * Changes from the original:\n * 1. Operates on Transaction directly instead of state+dispatch (TipTap compat)\n * 2. When the lifted block already has children (a groupType child), uses deeper\n *    openStart/offset so siblings merge into the existing group instead of\n *    creating a second one (which would violate blockContainer's schema)\n * 3. Uses groupType.create() instead of range.parent.copy() (same as sinkItem)\n */\nfunction liftToOuterList(\n  tr: Transaction,\n  itemType: NodeType,\n  groupType: NodeType, // change 3\n  range: NodeRange,\n) {\n  const end = range.end;\n  const endOfList = range.$to.end(range.depth);\n\n  if (end < endOfList) {\n    // There are siblings after the lifted items, which must become\n    // children of the last item\n    const blockBeingLifted = range.parent.child(range.endIndex - 1);\n    const nestedAfter =\n      blockBeingLifted.lastChild &&\n      blockBeingLifted.lastChild.type === groupType; // change 2\n\n    tr.step(\n      new ReplaceAroundStep(\n        end - (nestedAfter ? 2 : 1), // change 2: go deeper when merging into existing children\n        endOfList,\n        end,\n        endOfList,\n        new Slice(\n          Fragment.from(\n            itemType.create(null, groupType.create()), // change 3\n          ),\n          nestedAfter ? 2 : 1, // change 2: open deeper when merging into existing children\n          0,\n        ),\n        nestedAfter ? 0 : 1, // change 2: Slice.insertAt offsets by openStart, so 0+2=2 lands inside existing bg\n        true,\n      ),\n    );\n    range = new NodeRange(\n      tr.doc.resolve(range.$from.pos),\n      tr.doc.resolve(endOfList),\n      range.depth,\n    );\n  }\n\n  const target = liftTarget(range);\n  if (target == null) {\n    return false;\n  }\n\n  tr.lift(range, target);\n\n  const $after = tr.doc.resolve(tr.mapping.map(end, -1) - 1);\n  if (\n    canJoin(tr.doc, $after.pos) &&\n    $after.nodeBefore!.type === $after.nodeAfter!.type\n  ) {\n    tr.join($after.pos);\n  }\n\n  tr.scrollIntoView();\n  return true;\n}\n\n/**\n * Modified version of prosemirror-schema-list's liftListItem.\n * https://github.com/ProseMirror/prosemirror-schema-list/blob/master/src/schema-list.ts\n *\n * Changes from the original:\n * 1. Range predicate checks node.type instead of firstChild.type (same as sinkItem)\n * 2. Passes groupType to liftToOuterList\n * 3. Operates on Transaction directly instead of state+dispatch\n * 4. Skips liftOutOfList (root-level blocks can't be unnested in BlockNote)\n */\nexport function liftItem(\n  tr: Transaction,\n  itemType: NodeType,\n  groupType: NodeType, // change 2\n) {\n  const { $from, $to } = tr.selection;\n  const range = $from.blockRange(\n    $to,\n    (node) =>\n      node.childCount > 0 &&\n      (node.type.name === \"blockGroup\" || node.type.name === \"column\"), // change 1\n  );\n  if (!range) {\n    return false;\n  }\n\n  if ($from.node(range.depth - 1).type === itemType) {\n    // Inside a parent node\n    return liftToOuterList(tr, itemType, groupType, range); // change 2\n  }\n\n  // This is the \"liftOutOfList\" path — lifting out of a list entirely.\n  // Not applicable to BlockNote (root-level blocks can't be unnested). // change 4\n  return false;\n}\n\nexport function unnestBlock(editor: BlockNoteEditor<any, any, any>) {\n  return editor.transact((tr) =>\n    liftItem(\n      tr,\n      editor.pmSchema.nodes[\"blockContainer\"],\n      editor.pmSchema.nodes[\"blockGroup\"],\n    ),\n  );\n}\n\nexport function canNestBlock(editor: BlockNoteEditor<any, any, any>) {\n  return editor.transact((tr) => {\n    const { bnBlock: blockContainer } = getBlockInfoFromTransaction(tr);\n\n    return tr.doc.resolve(blockContainer.beforePos).nodeBefore !== null;\n  });\n}\n\nexport function canUnnestBlock(editor: BlockNoteEditor<any, any, any>) {\n  return editor.transact((tr) => {\n    const { bnBlock: blockContainer } = getBlockInfoFromTransaction(tr);\n\n    return tr.doc.resolve(blockContainer.beforePos).depth > 1;\n  });\n}\n","import type { Node } from \"prosemirror-model\";\nimport type { Block } from \"../../../blocks/defaultBlocks.js\";\nimport type {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { nodeToBlock } from \"../../nodeConversions/nodeToBlock.js\";\nimport { getNodeById } from \"../../nodeUtil.js\";\nimport { getPmSchema } from \"../../pmUtil.js\";\n\nexport function getBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  doc: Node,\n  blockIdentifier: BlockIdentifier,\n): Block<BSchema, I, S> | undefined {\n  const id =\n    typeof blockIdentifier === \"string\" ? blockIdentifier : blockIdentifier.id;\n  const pmSchema = getPmSchema(doc);\n\n  const posInfo = getNodeById(id, doc);\n  if (!posInfo) {\n    return undefined;\n  }\n\n  return nodeToBlock(posInfo.node, pmSchema);\n}\n\nexport function getPrevBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  doc: Node,\n  blockIdentifier: BlockIdentifier,\n): Block<BSchema, I, S> | undefined {\n  const id =\n    typeof blockIdentifier === \"string\" ? blockIdentifier : blockIdentifier.id;\n\n  const posInfo = getNodeById(id, doc);\n  const pmSchema = getPmSchema(doc);\n  if (!posInfo) {\n    return undefined;\n  }\n\n  const $posBeforeNode = doc.resolve(posInfo.posBeforeNode);\n  const nodeToConvert = $posBeforeNode.nodeBefore;\n  if (!nodeToConvert) {\n    return undefined;\n  }\n\n  return nodeToBlock(nodeToConvert, pmSchema);\n}\n\nexport function getNextBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  doc: Node,\n  blockIdentifier: BlockIdentifier,\n): Block<BSchema, I, S> | undefined {\n  const id =\n    typeof blockIdentifier === \"string\" ? blockIdentifier : blockIdentifier.id;\n  const posInfo = getNodeById(id, doc);\n  const pmSchema = getPmSchema(doc);\n  if (!posInfo) {\n    return undefined;\n  }\n\n  const $posAfterNode = doc.resolve(\n    posInfo.posBeforeNode + posInfo.node.nodeSize,\n  );\n  const nodeToConvert = $posAfterNode.nodeAfter;\n  if (!nodeToConvert) {\n    return undefined;\n  }\n\n  return nodeToBlock(nodeToConvert, pmSchema);\n}\n\nexport function getParentBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  doc: Node,\n  blockIdentifier: BlockIdentifier,\n): Block<BSchema, I, S> | undefined {\n  const id =\n    typeof blockIdentifier === \"string\" ? blockIdentifier : blockIdentifier.id;\n  const pmSchema = getPmSchema(doc);\n  const posInfo = getNodeById(id, doc);\n  if (!posInfo) {\n    return undefined;\n  }\n\n  const $posBeforeNode = doc.resolve(posInfo.posBeforeNode);\n  const parentNode = $posBeforeNode.node();\n  const grandparentNode = $posBeforeNode.node(-1);\n  const nodeToConvert =\n    grandparentNode.type.name !== \"doc\"\n      ? parentNode.type.name === \"blockGroup\"\n        ? grandparentNode\n        : parentNode\n      : undefined;\n  if (!nodeToConvert) {\n    return undefined;\n  }\n\n  return nodeToBlock(nodeToConvert, pmSchema);\n}\n","import { insertBlocks } from \"../../api/blockManipulation/commands/insertBlocks/insertBlocks.js\";\nimport {\n  moveBlocksDown,\n  moveBlocksUp,\n} from \"../../api/blockManipulation/commands/moveBlocks/moveBlocks.js\";\nimport {\n  canNestBlock,\n  canUnnestBlock,\n  nestBlock,\n  unnestBlock,\n} from \"../../api/blockManipulation/commands/nestBlock/nestBlock.js\";\nimport { removeAndInsertBlocks } from \"../../api/blockManipulation/commands/replaceBlocks/replaceBlocks.js\";\nimport { updateBlock } from \"../../api/blockManipulation/commands/updateBlock/updateBlock.js\";\nimport {\n  getBlock,\n  getNextBlock,\n  getParentBlock,\n  getPrevBlock,\n} from \"../../api/blockManipulation/getBlock/getBlock.js\";\nimport { docToBlocks } from \"../../api/nodeConversions/nodeToBlock.js\";\nimport {\n  Block,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  PartialBlock,\n} from \"../../blocks/defaultBlocks.js\";\nimport {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../schema/index.js\";\nimport { BlockNoteEditor } from \"../BlockNoteEditor.js\";\n\nexport class BlockManager<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> {\n  constructor(private editor: BlockNoteEditor<BSchema, ISchema, SSchema>) {}\n\n  /**\n   * Gets a snapshot of all top-level (non-nested) blocks in the editor.\n   * @returns A snapshot of all top-level (non-nested) blocks in the editor.\n   */\n  public get document(): Block<BSchema, ISchema, SSchema>[] {\n    return this.editor.transact((tr) => {\n      return docToBlocks(tr.doc, this.editor.pmSchema);\n    });\n  }\n\n  /**\n   * Gets a snapshot of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block that should be\n   * retrieved.\n   * @returns The block that matches the identifier, or `undefined` if no\n   * matching block was found.\n   */\n  public getBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this.editor.transact((tr) => getBlock(tr.doc, blockIdentifier));\n  }\n\n  /**\n   * Gets a snapshot of the previous sibling of an existing block from the\n   * editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * previous sibling should be retrieved.\n   * @returns The previous sibling of the block that matches the identifier.\n   * `undefined` if no matching block was found, or it's the first child/block\n   * in the document.\n   */\n  public getPrevBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this.editor.transact((tr) => getPrevBlock(tr.doc, blockIdentifier));\n  }\n\n  /**\n   * Gets a snapshot of the next sibling of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * next sibling should be retrieved.\n   * @returns The next sibling of the block that matches the identifier.\n   * `undefined` if no matching block was found, or it's the last child/block in\n   * the document.\n   */\n  public getNextBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this.editor.transact((tr) => getNextBlock(tr.doc, blockIdentifier));\n  }\n\n  /**\n   * Gets a snapshot of the parent of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * parent should be retrieved.\n   * @returns The parent of the block that matches the identifier. `undefined`\n   * if no matching block was found, or the block isn't nested.\n   */\n  public getParentBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this.editor.transact((tr) =>\n      getParentBlock(tr.doc, blockIdentifier),\n    );\n  }\n\n  /**\n   * Traverses all blocks in the editor depth-first, and executes a callback for each.\n   * @param callback The callback to execute for each block. Returning `false` stops the traversal.\n   * @param reverse Whether the blocks should be traversed in reverse order.\n   */\n  public forEachBlock(\n    callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,\n    reverse = false,\n  ): void {\n    const blocks = this.document.slice();\n\n    if (reverse) {\n      blocks.reverse();\n    }\n\n    function traverseBlockArray(\n      blockArray: Block<BSchema, ISchema, SSchema>[],\n    ): boolean {\n      for (const block of blockArray) {\n        if (callback(block) === false) {\n          return false;\n        }\n\n        const children = reverse\n          ? block.children.slice().reverse()\n          : block.children;\n\n        if (!traverseBlockArray(children)) {\n          return false;\n        }\n      }\n\n      return true;\n    }\n\n    traverseBlockArray(blocks);\n  }\n\n  /**\n   * Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an\n   * error if the reference block could not be found.\n   * @param blocksToInsert An array of partial blocks that should be inserted.\n   * @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.\n   * @param placement Whether the blocks should be inserted just before, just after, or nested inside the\n   * `referenceBlock`.\n   */\n  public insertBlocks(\n    blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],\n    referenceBlock: BlockIdentifier,\n    placement: \"before\" | \"after\" = \"before\",\n  ) {\n    return this.editor.transact((tr) =>\n      insertBlocks(tr, blocksToInsert, referenceBlock, placement),\n    );\n  }\n\n  /**\n   * Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be\n   * defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could\n   * not be found.\n   * @param blockToUpdate The block that should be updated.\n   * @param update A partial block which defines how the existing block should be changed.\n   */\n  public updateBlock(\n    blockToUpdate: BlockIdentifier,\n    update: PartialBlock<BSchema, ISchema, SSchema>,\n  ) {\n    return this.editor.transact((tr) => updateBlock(tr, blockToUpdate, update));\n  }\n\n  /**\n   * Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.\n   * @param blocksToRemove An array of identifiers for existing blocks that should be removed.\n   */\n  public removeBlocks(blocksToRemove: BlockIdentifier[]) {\n    return this.editor.transact(\n      (tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks,\n    );\n  }\n\n  /**\n   * Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or\n   * are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in\n   * `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.\n   * @param blocksToRemove An array of blocks that should be replaced.\n   * @param blocksToInsert An array of partial blocks to replace the old ones with.\n   */\n  public replaceBlocks(\n    blocksToRemove: BlockIdentifier[],\n    blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],\n  ) {\n    return this.editor.transact((tr) =>\n      removeAndInsertBlocks(tr, blocksToRemove, blocksToInsert),\n    );\n  }\n\n  /**\n   * Checks if the block containing the text cursor can be nested.\n   */\n  public canNestBlock() {\n    return canNestBlock(this.editor);\n  }\n\n  /**\n   * Nests the block containing the text cursor into the block above it.\n   */\n  public nestBlock() {\n    nestBlock(this.editor);\n  }\n\n  /**\n   * Checks if the block containing the text cursor is nested.\n   */\n  public canUnnestBlock() {\n    return canUnnestBlock(this.editor);\n  }\n\n  /**\n   * Lifts the block containing the text cursor out of its parent.\n   */\n  public unnestBlock() {\n    unnestBlock(this.editor);\n  }\n\n  /**\n   * Moves the selected blocks up. If the previous block has children, moves\n   * them to the end of its children. If there is no previous block, but the\n   * current blocks share a common parent, moves them out of & before it. If a\n   * `blockIdentifier` is provided, that block is moved instead of the\n   * selection, and the selection is left unchanged.\n   */\n  public moveBlocksUp(blockIdentifier?: BlockIdentifier) {\n    return moveBlocksUp(this.editor, blockIdentifier);\n  }\n\n  /**\n   * Moves the selected blocks down. If the next block has children, moves\n   * them to the start of its children. If there is no next block, but the\n   * current blocks share a common parent, moves them out of & after it. If a\n   * `blockIdentifier` is provided, that block is moved instead of the\n   * selection, and the selection is left unchanged.\n   */\n  public moveBlocksDown(blockIdentifier?: BlockIdentifier) {\n    return moveBlocksDown(this.editor, blockIdentifier);\n  }\n}\n","import type { BlockNoteEditor } from \"../BlockNoteEditor.js\";\nimport {\n  getBlocksChangedByTransaction,\n  type BlocksChanged,\n} from \"../../api/getBlocksChangedByTransaction.js\";\nimport { Transaction } from \"prosemirror-state\";\nimport { EventEmitter } from \"../../util/EventEmitter.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../schema/index.js\";\n\n/**\n * A function that can be used to unsubscribe from an event.\n */\nexport type Unsubscribe = () => void;\n\n/**\n * EventManager is a class which manages the events of the editor\n */\nexport class EventManager<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> extends EventEmitter<{\n  onChange: [\n    ctx: {\n      editor: BlockNoteEditor<BSchema, I, S>;\n      transaction: Transaction;\n      appendedTransactions: Transaction[];\n    },\n  ];\n  onSelectionChange: [\n    ctx: { editor: BlockNoteEditor<BSchema, I, S>; transaction: Transaction },\n  ];\n  onMount: [ctx: { editor: BlockNoteEditor<BSchema, I, S> }];\n  onUnmount: [ctx: { editor: BlockNoteEditor<BSchema, I, S> }];\n}> {\n  constructor(private editor: BlockNoteEditor<BSchema, I, S>) {\n    super();\n    // We register tiptap events only once the editor is finished initializing\n    // otherwise we would be trying to register events on a tiptap editor which does not exist yet\n    editor.on(\"create\", () => {\n      editor._tiptapEditor.on(\n        \"update\",\n        ({ transaction, appendedTransactions }) => {\n          this.emit(\"onChange\", { editor, transaction, appendedTransactions });\n        },\n      );\n      editor._tiptapEditor.on(\"selectionUpdate\", ({ transaction }) => {\n        this.emit(\"onSelectionChange\", { editor, transaction });\n      });\n      editor._tiptapEditor.on(\"mount\", () => {\n        this.emit(\"onMount\", { editor });\n      });\n      editor._tiptapEditor.on(\"unmount\", () => {\n        this.emit(\"onUnmount\", { editor });\n      });\n    });\n  }\n\n  /**\n   * Register a callback that will be called when the editor changes.\n   */\n  public onChange(\n    callback: (\n      editor: BlockNoteEditor<BSchema, I, S>,\n      ctx: {\n        getChanges(): BlocksChanged<BSchema, I, S>;\n      },\n    ) => void,\n    /**\n     * If true, the callback will be triggered when the changes are caused by a remote user\n     * @default true\n     */\n    includeUpdatesFromRemote = true,\n  ): Unsubscribe {\n    const cb = ({\n      transaction,\n      appendedTransactions,\n    }: {\n      transaction: Transaction;\n      appendedTransactions: Transaction[];\n    }) => {\n      if (!includeUpdatesFromRemote && isRemoteTransaction(transaction)) {\n        // don't trigger the callback if the changes are caused by a remote user\n        return;\n      }\n      callback(this.editor, {\n        getChanges() {\n          return getBlocksChangedByTransaction<BSchema, I, S>(\n            transaction,\n            appendedTransactions,\n          );\n        },\n      });\n    };\n    this.on(\"onChange\", cb);\n\n    return () => {\n      this.off(\"onChange\", cb);\n    };\n  }\n\n  /**\n   * Register a callback that will be called when the selection changes.\n   */\n  public onSelectionChange(\n    callback: (editor: BlockNoteEditor<BSchema, I, S>) => void,\n    /**\n     * If true, the callback will be triggered when the selection changes due to a yjs sync (i.e.: other user was typing)\n     */\n    includeSelectionChangedByRemote = false,\n  ): Unsubscribe {\n    const cb = (e: { transaction: Transaction }) => {\n      if (\n        !includeSelectionChangedByRemote &&\n        isRemoteTransaction(e.transaction)\n      ) {\n        // don't trigger the callback if the selection changed because of a remote user\n        return;\n      }\n      callback(this.editor);\n    };\n\n    this.on(\"onSelectionChange\", cb);\n\n    return () => {\n      this.off(\"onSelectionChange\", cb);\n    };\n  }\n\n  /**\n   * Register a callback that will be called when the editor is mounted.\n   */\n  public onMount(\n    callback: (ctx: { editor: BlockNoteEditor<BSchema, I, S> }) => void,\n  ): Unsubscribe {\n    this.on(\"onMount\", callback);\n\n    return () => {\n      this.off(\"onMount\", callback);\n    };\n  }\n\n  /**\n   * Register a callback that will be called when the editor is unmounted.\n   */\n  public onUnmount(\n    callback: (ctx: { editor: BlockNoteEditor<BSchema, I, S> }) => void,\n  ): Unsubscribe {\n    this.on(\"onUnmount\", callback);\n\n    return () => {\n      this.off(\"onUnmount\", callback);\n    };\n  }\n}\n\nfunction isRemoteTransaction(transaction: Transaction): boolean {\n  return !!transaction.getMeta(\"y-sync$\");\n}\n","function getChildIndex(node: Element) {\n  return Array.prototype.indexOf.call(node.parentElement!.childNodes, node);\n}\n\nfunction isWhitespaceNode(node: Node) {\n  return node.nodeType === 3 && !/\\S/.test(node.nodeValue || \"\");\n}\n\n/**\n * Step 1, Turns:\n *\n * <ul>\n *  <li>item</li>\n *  <li>\n *   <ul>\n *      <li>...</li>\n *      <li>...</li>\n *   </ul>\n * </li>\n *\n * Into:\n * <ul>\n *  <li>item</li>\n *  <ul>\n *      <li>...</li>\n *      <li>...</li>\n *  </ul>\n * </ul>\n *\n */\nfunction liftNestedListsToParent(element: HTMLElement) {\n  element.querySelectorAll(\"li > ul, li > ol\").forEach((list) => {\n    const index = getChildIndex(list);\n    const parentListItem = list.parentElement!;\n    const siblingsAfter = Array.from(parentListItem.childNodes).slice(\n      index + 1,\n    );\n    list.remove();\n    siblingsAfter.forEach((sibling) => {\n      sibling.remove();\n    });\n\n    parentListItem.insertAdjacentElement(\"afterend\", list);\n\n    siblingsAfter.reverse().forEach((sibling) => {\n      if (isWhitespaceNode(sibling)) {\n        return;\n      }\n      const siblingContainer = document.createElement(\"li\");\n      siblingContainer.append(sibling);\n      list.insertAdjacentElement(\"afterend\", siblingContainer);\n    });\n    if (parentListItem.childNodes.length === 0) {\n      parentListItem.remove();\n    }\n  });\n}\n\n/**\n * Step 2, Turns (output of liftNestedListsToParent):\n *\n * <li>item</li>\n * <ul>\n *   <li>...</li>\n *   <li>...</li>\n * </ul>\n *\n * Into:\n * <div>\n *  <li>item</li>\n *  <div data-node-type=\"blockGroup\">\n *      <ul>\n *          <li>...</li>\n *          <li>...</li>\n *      </ul>\n *  </div>\n * </div>\n *\n * This resulting format is parsed\n */\nfunction createGroups(element: HTMLElement) {\n  element.querySelectorAll(\"li + ul, li + ol\").forEach((list) => {\n    const listItem = list.previousElementSibling as HTMLElement;\n    const blockContainer = document.createElement(\"div\");\n\n    listItem.insertAdjacentElement(\"afterend\", blockContainer);\n    blockContainer.append(listItem);\n\n    const blockGroup = document.createElement(\"div\");\n    blockGroup.setAttribute(\"data-node-type\", \"blockGroup\");\n    blockContainer.append(blockGroup);\n\n    while (\n      blockContainer.nextElementSibling?.nodeName === \"UL\" ||\n      blockContainer.nextElementSibling?.nodeName === \"OL\"\n    ) {\n      blockGroup.append(blockContainer.nextElementSibling);\n    }\n  });\n}\n\n// prevent XSS, similar to https://github.com/ProseMirror/prosemirror-view/blob/1251b2b412656a2a06263e4187574beb43651273/src/clipboard.ts#L204\n// https://github.com/TypeCellOS/BlockNote/issues/601\nlet _detachedDoc: Document | null = null;\nfunction detachedDoc() {\n  return (\n    _detachedDoc ||\n    (_detachedDoc = document.implementation.createHTMLDocument(\"title\"))\n  );\n}\n\nexport function nestedListsToBlockNoteStructure(\n  elementOrHTML: HTMLElement | string,\n) {\n  if (typeof elementOrHTML === \"string\") {\n    const element = detachedDoc().createElement(\"div\");\n    element.innerHTML = elementOrHTML;\n    elementOrHTML = element;\n  }\n  liftNestedListsToParent(elementOrHTML);\n  createGroups(elementOrHTML);\n  return elementOrHTML;\n}\n","/**\n * Checks if the given HTML element contains markers indicating it was\n * generated by Notion. Notion uses `\\n` in text nodes to represent hard\n * breaks, which is non-standard but intentional.\n *\n * Detected by the `<!-- notionvc: UUID -->` comment that Notion places\n * on the clipboard.\n */\nfunction isNotionHTML(element: HTMLElement): boolean {\n  const walker = element.ownerDocument.createTreeWalker(\n    element,\n    // NodeFilter.SHOW_COMMENT\n    128,\n  );\n\n  let node: Node | null;\n  while ((node = walker.nextNode())) {\n    if (/^\\s*notionvc:/.test(node.nodeValue || \"\")) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\n/**\n * Normalizes whitespace in text nodes by collapsing runs of whitespace\n * (including newlines) to single spaces, matching CSS white-space:normal\n * behavior.\n *\n * This is needed because ProseMirror's DOMParser, when `linebreakReplacement`\n * is set in the schema (as BlockNote does for hard breaks), converts `\\n`\n * characters in text nodes to hard break nodes instead of collapsing them.\n * This causes HTML source line wrapping (e.g. from MS Word) to create\n * visible line breaks in the editor.\n *\n * Skipped for sources like Notion that intentionally use `\\n` in text nodes\n * to represent hard breaks instead of `<br>` tags.\n *\n * Skips `<pre>` and `<code>` elements where whitespace should be preserved.\n */\nfunction normalizeTextNodeWhitespace(element: HTMLElement) {\n  const preserveWSTags = new Set([\"PRE\", \"CODE\"]);\n  const walker = element.ownerDocument.createTreeWalker(\n    element,\n    // NodeFilter.SHOW_TEXT\n    4,\n    {\n      acceptNode(node) {\n        // Skip text nodes inside pre/code elements\n        let parent = node.parentElement;\n        while (parent && parent !== element) {\n          if (preserveWSTags.has(parent.tagName)) {\n            // NodeFilter.FILTER_REJECT\n            return 2;\n          }\n          parent = parent.parentElement;\n        }\n        // NodeFilter.FILTER_ACCEPT\n        return 1;\n      },\n    },\n  );\n\n  const textNodes: Text[] = [];\n  let node: Node | null;\n  while ((node = walker.nextNode())) {\n    textNodes.push(node as Text);\n  }\n\n  for (const textNode of textNodes) {\n    if (textNode.nodeValue && /[\\r\\n]/.test(textNode.nodeValue)) {\n      textNode.nodeValue = textNode.nodeValue.replace(/[ \\t\\r\\n\\f]+/g, \" \");\n    }\n  }\n}\n\n/**\n * Normalizes whitespace in HTML text nodes to match standard CSS\n * white-space:normal behavior. Skipped for Notion HTML which intentionally\n * uses `\\n` for hard breaks.\n */\nexport function preprocessHTMLWhitespace(element: HTMLElement) {\n  if (!isNotionHTML(element)) {\n    normalizeTextNodeWhitespace(element);\n  }\n}\n","import { DOMParser, Schema } from \"prosemirror-model\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\n\nimport { Block } from \"../../../blocks/defaultBlocks.js\";\nimport { nodeToBlock } from \"../../nodeConversions/nodeToBlock.js\";\nimport { nestedListsToBlockNoteStructure } from \"./util/nestedLists.js\";\nimport { preprocessHTMLWhitespace } from \"./util/normalizeWhitespace.js\";\n\nexport function HTMLToBlocks<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(html: string, pmSchema: Schema): Block<BSchema, I, S>[] {\n  const htmlNode = nestedListsToBlockNoteStructure(html);\n  preprocessHTMLWhitespace(htmlNode);\n  const parser = DOMParser.fromSchema(pmSchema);\n\n  // Other approach might be to use\n  // const doc = pmSchema.nodes[\"doc\"].createAndFill()!;\n  // and context: doc.resolve(3),\n\n  const parentNode = parser.parse(htmlNode, {\n    topNode: pmSchema.nodes[\"blockGroup\"].create(),\n  });\n\n  const blocks: Block<BSchema, I, S>[] = [];\n\n  for (let i = 0; i < parentNode.childCount; i++) {\n    blocks.push(nodeToBlock(parentNode.child(i), pmSchema));\n  }\n\n  return blocks;\n}\n","import { isVideoUrl } from \"../../../util/string.js\";\n\n/**\n * Custom markdown-to-HTML converter for BlockNote.\n * Replaces the unified/remark/rehype pipeline with a direct, minimal implementation\n * that handles exactly the markdown features BlockNote needs.\n */\n\n// ─── HTML Escaping ───────────────────────────────────────────────────────────\n\nfunction escapeHtml(str: string): string {\n  return str\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\");\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction isAlphanumeric(char: string | undefined): boolean {\n  if (!char) {\n    return false;\n  }\n  return /\\w/.test(char);\n}\n\n/**\n * Returns true when an underscore delimiter at position `i` is \"intraword\",\n * meaning the characters on both sides are alphanumeric (e.g. `snake_case`).\n * In that case the underscore should NOT be treated as emphasis per CommonMark.\n */\nfunction isIntraword(text: string, i: number, delimLen: number): boolean {\n  const before = i > 0 ? text[i - 1] : undefined;\n  const after =\n    i + delimLen < text.length ? text[i + delimLen] : undefined;\n  return isAlphanumeric(before) && isAlphanumeric(after);\n}\n\n// ─── Inline Parser ───────────────────────────────────────────────────────────\n\ntype InlineTokenizer = (\n  text: string,\n  i: number\n) => { html: string; end: number } | null;\n\nfunction tryBackslashEscape(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"\\\\\" || i + 1 >= text.length) {return null;}\n  const next = text[i + 1];\n  // Hard line break: backslash at end of line\n  if (next === \"\\n\") {\n    return { html: \"<br>\\n\", end: i + 2 };\n  }\n  // Escapable characters\n  if (\"\\\\`*_{}[]()#+-.!~|>\".includes(next)) {\n    return { html: escapeHtml(next), end: i + 2 };\n  }\n  return null;\n}\n\nfunction tryInlineCode(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"`\") {return null;}\n  return parseInlineCode(text, i);\n}\n\nfunction tryImage(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"!\" || text[i + 1] !== \"[\") {return null;}\n  return parseImage(text, i);\n}\n\nfunction tryLink(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"[\") {return null;}\n  return parseLink(text, i);\n}\n\nfunction tryStrikethrough(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"~\" || text[i + 1] !== \"~\") {return null;}\n  return parseDelimited(text, i, \"~~\", \"<del>\", \"</del>\");\n}\n\nfunction tryBoldItalic(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (\n    (text[i] === \"*\" && text[i + 1] === \"*\" && text[i + 2] === \"*\") ||\n    (text[i] === \"_\" &&\n      text[i + 1] === \"_\" &&\n      text[i + 2] === \"_\" &&\n      !isIntraword(text, i, 3))\n  ) {\n    const delimiter = text.substring(i, i + 3);\n    return parseDelimited(text, i, delimiter, \"<strong><em>\", \"</em></strong>\");\n  }\n  return null;\n}\n\nfunction tryBold(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (\n    (text[i] === \"*\" && text[i + 1] === \"*\") ||\n    (text[i] === \"_\" && text[i + 1] === \"_\" && !isIntraword(text, i, 2))\n  ) {\n    const delimiter = text.substring(i, i + 2);\n    return parseDelimited(text, i, delimiter, \"<strong>\", \"</strong>\");\n  }\n  return null;\n}\n\nfunction tryItalic(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] === \"*\" || (text[i] === \"_\" && !isIntraword(text, i, 1))) {\n    return parseDelimited(text, i, text[i], \"<em>\", \"</em>\");\n  }\n  return null;\n}\n\nfunction trySoftBreak(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] === \"\\n\") {\n    return { html: \"<br>\\n\", end: i + 1 };\n  }\n  return null;\n}\n\n// Inline raw HTML: pass through tags, comments, CDATA, processing\n// instructions, and declarations verbatim so authors can mix HTML into\n// markdown (e.g. `text <em>foo</em> more`). Anything that doesn't match\n// these shapes falls through and gets HTML-escaped as plain text.\nconst INLINE_HTML_TAG_RE =\n  /^<\\/?[a-zA-Z][a-zA-Z0-9-]*(?:\\s+[a-zA-Z_:][a-zA-Z0-9_.:-]*(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s\"'=<>`]+))?)*\\s*\\/?>/;\nconst HTML_COMMENT_RE = /^<!--[\\s\\S]*?-->/;\nconst HTML_CDATA_RE = /^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>/;\nconst HTML_PI_RE = /^<\\?[\\s\\S]*?\\?>/;\nconst HTML_DECL_RE = /^<![A-Za-z][\\s\\S]*?>/;\n\nfunction tryInlineHtml(\n  text: string,\n  i: number\n): { html: string; end: number } | null {\n  if (text[i] !== \"<\") {return null;}\n  const rest = text.substring(i);\n  for (const re of [\n    HTML_COMMENT_RE,\n    HTML_CDATA_RE,\n    HTML_PI_RE,\n    HTML_DECL_RE,\n    INLINE_HTML_TAG_RE,\n  ]) {\n    const m = rest.match(re);\n    if (m) {\n      return { html: m[0], end: i + m[0].length };\n    }\n  }\n  return null;\n}\n\n/** Characters that can start an inline syntax token. */\nconst SPECIAL_CHARS = new Set(\"\\\\`![~*_\\n<\");\n\n/**\n * Ordered array of inline tokenizers, tried in priority order.\n * The first match wins.\n */\nconst inlineTokenizers: InlineTokenizer[] = [\n  tryBackslashEscape,\n  tryInlineCode,\n  tryImage,\n  tryLink,\n  tryStrikethrough,\n  tryBoldItalic, // *** / ___\n  tryBold, // ** / __\n  tryItalic, // * / _\n  tryInlineHtml,\n  trySoftBreak,\n];\n\n/**\n * Parse inline markdown syntax and return HTML.\n * Handles: bold, italic, bold+italic, strikethrough, inline code,\n * links, images (with video detection), hard line breaks, backslash escapes.\n */\nfunction parseInline(text: string): string {\n  let result = \"\";\n  let i = 0;\n\n  while (i < text.length) {\n    // Hard line break: 2+ trailing spaces immediately before a newline.\n    // (The other hard-break form, backslash + newline, is handled by\n    // tryBackslashEscape.) Strip the trailing spaces from the accumulated\n    // result before emitting the <br>.\n    if (\n      text[i] === \"\\n\" &&\n      i >= 2 &&\n      text[i - 1] === \" \" &&\n      text[i - 2] === \" \"\n    ) {\n      result = result.replace(/ +$/, \"\");\n      result += \"<br>\\n\";\n      i++;\n      continue;\n    }\n\n    // Try each tokenizer in priority order\n    let matched = false;\n    if (SPECIAL_CHARS.has(text[i])) {\n      for (const tokenizer of inlineTokenizers) {\n        const r = tokenizer(text, i);\n        if (r) {\n          result += r.html;\n          i = r.end;\n          matched = true;\n          break;\n        }\n      }\n    }\n\n    if (!matched) {\n      // Batch consecutive plain-text characters and escape once\n      const runStart = i;\n      i++;\n      while (i < text.length && !SPECIAL_CHARS.has(text[i])) {\n        i++;\n      }\n      result += escapeHtml(text.substring(runStart, i));\n    }\n  }\n\n  return result;\n}\n\nfunction parseInlineCode(\n  text: string,\n  start: number\n): { html: string; end: number } | null {\n  // Count opening backticks\n  let openCount = 0;\n  let i = start;\n  while (i < text.length && text[i] === \"`\") {\n    openCount++;\n    i++;\n  }\n\n  // Find matching closing backticks\n  let j = i;\n  while (j < text.length) {\n    if (text[j] === \"`\") {\n      let closeCount = 0;\n      const closeStart = j;\n      while (j < text.length && text[j] === \"`\") {\n        closeCount++;\n        j++;\n      }\n      if (closeCount === openCount) {\n        let code = text.substring(i, closeStart);\n        // Per CommonMark: line endings inside a code span are converted to\n        // single spaces, then if the result starts AND ends with a space and\n        // is not all-spaces, one leading + trailing space is stripped (so\n        // `` ` `foo` ` `` is `<code>`foo`</code>`).\n        code = code.replace(/\\n/g, \" \");\n        if (\n          code.length >= 2 &&\n          code[0] === \" \" &&\n          code[code.length - 1] === \" \" &&\n          /[^ ]/.test(code)\n        ) {\n          code = code.substring(1, code.length - 1);\n        }\n        return {\n          html: `<code>${escapeHtml(code)}</code>`,\n          end: j,\n        };\n      }\n    } else {\n      j++;\n    }\n  }\n  return null;\n}\n\nfunction parseImage(\n  text: string,\n  start: number\n): { html: string; end: number } | null {\n  // ![alt](url) or ![alt](url \"title\")\n  // Use balanced bracket matching to handle nested/escaped brackets in alt text\n  const altEnd = findClosingBracket(text, start + 1);\n  if (altEnd === -1) {return null;}\n  const altStart = start + 2; // after ![\n\n  if (text[altEnd + 1] !== \"(\") {return null;}\n\n  const urlStart = altEnd + 2;\n  const parenEnd = findClosingParen(text, urlStart - 1);\n  if (parenEnd === -1) {return null;}\n\n  const alt = text.substring(altStart, altEnd);\n  const { url, title } = parseDestinationAndTitle(\n    text.substring(urlStart, parenEnd),\n  );\n\n  if (isVideoUrl(url)) {\n    // Use the alt text as the video's display name (falling back to the\n    // title) so a video link written with the standard `![name](url)` form\n    // round-trips into BlockNote's video block. Captioned videos go through\n    // raw `<figure>` HTML instead, see htmlToMarkdown.serializeMediaFigure.\n    const name = alt || title;\n    return {\n      html: `<video src=\"${escapeHtml(url)}\"${name ? ` data-name=\"${escapeHtml(name)}\"` : \"\"} data-url=\"${escapeHtml(url)}\" controls></video>`,\n      end: parenEnd + 1,\n    };\n  }\n\n  const titleAttr =\n    title !== undefined ? ` title=\"${escapeHtml(title)}\"` : \"\";\n  return {\n    html: `<img src=\"${escapeHtml(url)}\" alt=\"${escapeHtml(alt)}\"${titleAttr}>`,\n    end: parenEnd + 1,\n  };\n}\n\nfunction parseLink(\n  text: string,\n  start: number\n): { html: string; end: number } | null {\n  // [text](url)\n  const textStart = start + 1;\n  const textEnd = findClosingBracket(text, start);\n  if (textEnd === -1) {return null;}\n\n  if (text[textEnd + 1] !== \"(\") {return null;}\n\n  const urlStart = textEnd + 2;\n  const parenEnd = findClosingParen(text, textEnd + 1);\n  if (parenEnd === -1) {return null;}\n\n  const linkText = text.substring(textStart, textEnd);\n  const { url, title } = parseDestinationAndTitle(\n    text.substring(urlStart, parenEnd),\n  );\n\n  const titleAttr =\n    title !== undefined ? ` title=\"${escapeHtml(title)}\"` : \"\";\n  return {\n    html: `<a href=\"${escapeHtml(url)}\"${titleAttr}>${parseInline(linkText)}</a>`,\n    end: parenEnd + 1,\n  };\n}\n\nfunction findClosingBracket(text: string, openPos: number): number {\n  let depth = 0;\n  for (let i = openPos; i < text.length; i++) {\n    if (text[i] === \"\\\\\" && i + 1 < text.length) {\n      i++; // skip escaped\n      continue;\n    }\n    if (text[i] === \"[\") {depth++;}\n    if (text[i] === \"]\") {\n      depth--;\n      if (depth === 0) {return i;}\n    }\n  }\n  return -1;\n}\n\nfunction findClosingParen(text: string, openPos: number): number {\n  let depth = 0;\n  for (let i = openPos; i < text.length; i++) {\n    if (text[i] === \"\\\\\" && i + 1 < text.length) {\n      i++;\n      continue;\n    }\n    if (text[i] === \"(\") {depth++;}\n    if (text[i] === \")\") {\n      depth--;\n      if (depth === 0) {return i;}\n    }\n  }\n  return -1;\n}\n\n/**\n * Parse the inside of `(...)` from a link/image (the URL and optional title).\n * Handles three URL forms:\n *   - bare:           `/uri` or `/uri \"title\"`\n *   - angle-bracket:  `<url>` or `<url> \"title\"` (brackets are stripped)\n * And three title-quote forms:  `\"...\"`, `'...'`, `(...)`.\n */\nfunction parseDestinationAndTitle(raw: string): {\n  url: string;\n  title?: string;\n} {\n  raw = raw.trim();\n  let url: string;\n  let rest: string;\n\n  if (raw.startsWith(\"<\")) {\n    const close = raw.indexOf(\">\");\n    if (close === -1) {\n      // Unmatched `<` — treat the whole thing as the URL minus the `<`.\n      url = raw.substring(1);\n      rest = \"\";\n    } else {\n      url = raw.substring(1, close);\n      rest = raw.substring(close + 1).trim();\n    }\n  } else {\n    // Split at first unescaped whitespace.\n    let split = raw.length;\n    for (let i = 0; i < raw.length; i++) {\n      if (raw[i] === \"\\\\\" && i + 1 < raw.length) {\n        i++;\n        continue;\n      }\n      if (raw[i] === \" \" || raw[i] === \"\\t\" || raw[i] === \"\\n\") {\n        split = i;\n        break;\n      }\n    }\n    url = raw.substring(0, split);\n    rest = raw.substring(split).trim();\n  }\n\n  let title: string | undefined;\n  if (rest.length > 0) {\n    const titleMatch = rest.match(/^\"([^\"]*)\"$|^'([^']*)'$|^\\(([^)]*)\\)$/);\n    if (titleMatch) {\n      title = titleMatch[1] ?? titleMatch[2] ?? titleMatch[3];\n    }\n  }\n\n  return { url, title };\n}\n\nfunction parseDelimited(\n  text: string,\n  start: number,\n  delimiter: string,\n  openTag: string,\n  closeTag: string\n): { html: string; end: number } | null {\n  const len = delimiter.length;\n  const afterOpen = start + len;\n\n  if (afterOpen >= text.length) {return null;}\n\n  // Opening delimiter must not be followed by whitespace\n  if (text[afterOpen] === \" \" || text[afterOpen] === \"\\t\") {return null;}\n\n  // Find closing delimiter\n  let j = afterOpen;\n  while (j < text.length) {\n    // Skip escaped characters\n    if (text[j] === \"\\\\\" && j + 1 < text.length) {\n      j += 2;\n      continue;\n    }\n\n    if (text.substring(j, j + len) === delimiter) {\n      // Closing delimiter must not be preceded by whitespace\n      if (text[j - 1] === \" \" || text[j - 1] === \"\\t\") {\n        j++;\n        continue;\n      }\n\n      // For single-char delimiters, don't accept closer if it's part of a\n      // multi-char run (e.g., don't treat the * in ** as italic closer)\n      if (\n        len === 1 &&\n        ((j > 0 && text[j - 1] === delimiter[0] && !(j >= 2 && text[j - 2] === \"\\\\\")) ||\n          (j + len < text.length && text[j + len] === delimiter[0]))\n      ) {\n        j++;\n        continue;\n      }\n\n      const inner = text.substring(afterOpen, j);\n      if (inner.length === 0) {\n        j++;\n        continue;\n      }\n\n      return {\n        html: openTag + parseInline(inner) + closeTag,\n        end: j + len,\n      };\n    }\n    j++;\n  }\n\n  return null;\n}\n\n// ─── Block-Level Types ───────────────────────────────────────────────────────\n\ninterface BlockToken {\n  type: string;\n}\n\ninterface HeadingToken extends BlockToken {\n  type: \"heading\";\n  level: number;\n  content: string;\n}\n\ninterface ParagraphToken extends BlockToken {\n  type: \"paragraph\";\n  content: string;\n}\n\ninterface CodeBlockToken extends BlockToken {\n  type: \"codeBlock\";\n  language: string;\n  code: string;\n}\n\ninterface BlockquoteToken extends BlockToken {\n  type: \"blockquote\";\n  content: string;\n}\n\ninterface HorizontalRuleToken extends BlockToken {\n  type: \"hr\";\n}\n\ninterface ListItemToken extends BlockToken {\n  type: \"listItem\";\n  listType: \"bullet\" | \"ordered\" | \"task\";\n  indent: number;\n  content: string;\n  start?: number; // for ordered lists\n  checked?: boolean; // for task lists\n  childContent?: string; // recursively parsed content within this item\n}\n\ninterface TableToken extends BlockToken {\n  type: \"table\";\n  headers: string[];\n  rows: string[][];\n  alignments: (\"left\" | \"center\" | \"right\" | null)[];\n}\n\ninterface RawHtmlToken extends BlockToken {\n  type: \"rawHtml\";\n  content: string;\n}\n\ntype Token =\n  | HeadingToken\n  | ParagraphToken\n  | CodeBlockToken\n  | BlockquoteToken\n  | HorizontalRuleToken\n  | ListItemToken\n  | TableToken\n  | RawHtmlToken;\n\n/**\n * HTML block-level tag names (from the CommonMark type-6 list, plus `audio`\n * which BlockNote serializes as raw HTML since markdown has no shorthand\n * for it). When a line starts with `<` followed by one of these tag names,\n * the run of non-blank lines is emitted verbatim as raw HTML rather than\n * wrapped in a paragraph.\n */\nconst HTML_BLOCK_TAGS = new Set([\n  \"address\", \"article\", \"aside\", \"audio\", \"base\", \"basefont\", \"blockquote\",\n  \"body\", \"caption\", \"center\", \"col\", \"colgroup\", \"dd\", \"details\", \"dialog\",\n  \"dir\", \"div\", \"dl\", \"dt\", \"fieldset\", \"figcaption\", \"figure\", \"footer\",\n  \"form\", \"frame\", \"frameset\", \"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\", \"head\",\n  \"header\", \"hr\", \"html\", \"iframe\", \"legend\", \"li\", \"link\", \"main\", \"menu\",\n  \"menuitem\", \"nav\", \"noframes\", \"ol\", \"optgroup\", \"option\", \"p\", \"param\",\n  \"section\", \"source\", \"summary\", \"table\", \"tbody\", \"td\", \"tfoot\", \"th\",\n  \"thead\", \"title\", \"tr\", \"track\", \"ul\",\n]);\n\nfunction isHtmlBlockStart(line: string): boolean {\n  // <!-- ..., <?..., <![CDATA[..., <!DOCTYPE, etc.\n  if (/^ {0,3}<(!--|\\?|![A-Za-z]|!\\[CDATA\\[)/.test(line)) {\n    return true;\n  }\n  const m = line.match(/^ {0,3}<\\/?([a-zA-Z][a-zA-Z0-9-]*)(?:\\s|\\/?>|$)/);\n  if (!m) {return false;}\n  return HTML_BLOCK_TAGS.has(m[1].toLowerCase());\n}\n\n// ─── Block-Level Tokenizer ──────────────────────────────────────────────────\n\nfunction tokenize(markdown: string): Token[] {\n  const lines = markdown.split(\"\\n\");\n  const tokens: Token[] = [];\n  let i = 0;\n  let prevLineWasBlank = true; // treat start of document as after blank\n\n  while (i < lines.length) {\n    const line = lines[i];\n\n    // Blank line — skip\n    if (line.trim() === \"\") {\n      prevLineWasBlank = true;\n      i++;\n      continue;\n    }\n\n    // Fenced code block (0-3 leading spaces allowed per CommonMark)\n    const fenceMatch = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);\n    if (fenceMatch) {\n      const fence = fenceMatch[1];\n      const fenceChar = fence[0];\n      const fenceLen = fence.length;\n      const language = fenceMatch[2].trim();\n      const codeLines: string[] = [];\n      i++;\n      while (i < lines.length) {\n        const closingMatch = lines[i].match(\n          new RegExp(`^ {0,3}${fenceChar}{${fenceLen},}\\\\s*$`)\n        );\n        if (closingMatch) {\n          i++;\n          break;\n        }\n        codeLines.push(lines[i]);\n        i++;\n      }\n      tokens.push({\n        type: \"codeBlock\",\n        language: language || \"\",\n        code: codeLines.join(\"\\n\"),\n      });\n      prevLineWasBlank = false;\n      continue;\n    }\n\n    // ATX Heading.\n    // - Closing `#` sequence requires a preceding space (so `### foo###`\n    //   keeps the trailing #s as text, while `### foo ###` strips them).\n    // - Trailing whitespace is always stripped from the heading content.\n    const headingMatch = line.match(/^(#{1,6})\\s+(.+?)(?:\\s+#+\\s*|\\s*)$/);\n    if (headingMatch) {\n      tokens.push({\n        type: \"heading\",\n        level: headingMatch[1].length,\n        content: headingMatch[2],\n      });\n      prevLineWasBlank = false;\n      i++;\n      continue;\n    }\n\n    // Horizontal rule: ---, ***, ___ (3+ chars, optionally with spaces)\n    if (/^(\\s{0,3})([-*_])\\s*(\\2\\s*){2,}$/.test(line)) {\n      // Setext H2: --- immediately after a paragraph (no blank line between)\n      const prevToken = tokens[tokens.length - 1];\n      if (\n        !prevLineWasBlank &&\n        line.trim().match(/^-+$/) &&\n        prevToken &&\n        prevToken.type === \"paragraph\"\n      ) {\n        const para = prevToken as ParagraphToken;\n        tokens[tokens.length - 1] = {\n          type: \"heading\",\n          level: 2,\n          content: para.content,\n        };\n        prevLineWasBlank = false;\n        i++;\n        continue;\n      }\n      tokens.push({ type: \"hr\" });\n      prevLineWasBlank = false;\n      i++;\n      continue;\n    }\n\n    // Setext heading detection: check if next line is === or ---\n    if (i + 1 < lines.length) {\n      const nextLine = lines[i + 1];\n      if (/^={1,}\\s*$/.test(nextLine) && line.trim().length > 0) {\n        tokens.push({\n          type: \"heading\",\n          level: 1,\n          content: line.trim(),\n        });\n        prevLineWasBlank = false;\n        i += 2;\n        continue;\n      }\n      // Setext H2 --- handled in HR section above\n    }\n\n    // Table: detect by looking for separator row\n    const tableResult = tryParseTable(lines, i);\n    if (tableResult) {\n      tokens.push(tableResult.token);\n      i = tableResult.nextLine;\n      prevLineWasBlank = false;\n      continue;\n    }\n\n    // Blockquote\n    if (/^\\s{0,3}>/.test(line)) {\n      const quoteLines: string[] = [];\n      while (i < lines.length && /^\\s{0,3}>/.test(lines[i])) {\n        // Remove the > prefix\n        quoteLines.push(lines[i].replace(/^\\s{0,3}>\\s?/, \"\"));\n        i++;\n      }\n      // Lazy continuation: collect non-blank lines that don't start a new\n      // block-level element (per CommonMark spec)\n      while (i < lines.length) {\n        const cur = lines[i];\n        if (cur.trim() === \"\") {break;}\n        // Stop on block-level markers\n        if (/^\\s{0,3}>/.test(cur)) {break;} // new blockquote\n        if (/^(#{1,6})\\s/.test(cur)) {break;} // heading\n        if (/^(`{3,}|~{3,})/.test(cur)) {break;} // code fence\n        if (/^(\\s{0,3})([-*_])\\s*(\\2\\s*){2,}$/.test(cur)) {break;} // hr\n        if (/^\\s*([-*+]|\\d+[.)])\\s+/.test(cur)) {break;} // list item\n        if (/^\\s*\\|(.+\\|)+\\s*$/.test(cur)) {break;} // table\n        quoteLines.push(cur);\n        i++;\n      }\n      tokens.push({\n        type: \"blockquote\",\n        content: quoteLines.join(\"\\n\"),\n      });\n      prevLineWasBlank = false;\n      continue;\n    }\n\n    // List item (bullet, ordered, or task)\n    const listItemMatch = line.match(\n      /^(\\s*)([-*+]|\\d+[.)])(\\s+)(\\[[ xX]\\] )?(.*)$/\n    );\n    if (listItemMatch) {\n      const indent = listItemMatch[1].length;\n      const marker = listItemMatch[2];\n      const markerSpaces = listItemMatch[3];\n      const checkbox = listItemMatch[4];\n      const firstLineContent = listItemMatch[5];\n\n      let listType: \"bullet\" | \"ordered\" | \"task\";\n      let start: number | undefined;\n      let checked: boolean | undefined;\n\n      if (checkbox) {\n        listType = \"task\";\n        checked = checkbox.trim() !== \"[ ]\";\n      } else if (/^\\d+[.)]$/.test(marker)) {\n        listType = \"ordered\";\n        start = parseInt(marker, 10);\n      } else {\n        listType = \"bullet\";\n      }\n\n      // Content indent = column where content actually starts\n      const contentIndent =\n        indent +\n        marker.length +\n        markerSpaces.length +\n        (checkbox ? checkbox.length : 0);\n\n      // Minimum indent for child content: anything indented past the marker\n      // (sub-lists can start at indent > marker position)\n      const minChildIndent = indent + 1;\n\n      // Helper to check if a line belongs to this list item\n      const belongsToItem = (lineStr: string): boolean => {\n        if (lineStr.trim() === \"\") {return true;} // blank lines checked separately\n        const lineInd = lineStr.match(/^\\s*/)![0].length;\n        // Lines at contentIndent are continuation text\n        if (lineInd >= contentIndent) {return true;}\n        // Lines between marker and content column that start a sub-list\n        if (\n          lineInd >= minChildIndent &&\n          lineStr.match(/^\\s*([-*+]|\\d+[.)])\\s+/)\n        ) {\n          return true;\n        }\n        return false;\n      }\n\n      // Consume ALL subsequent lines that belong to this list item\n      i++;\n      const subLines: string[] = [];\n      while (i < lines.length) {\n        const cur = lines[i];\n\n        if (cur.trim() === \"\") {\n          // Blank line: include if followed by content that belongs to this item\n          let lookAhead = i + 1;\n          while (lookAhead < lines.length && lines[lookAhead].trim() === \"\") {\n            lookAhead++;\n          }\n          if (lookAhead < lines.length && belongsToItem(lines[lookAhead])) {\n            subLines.push(\"\");\n            i++;\n            continue;\n          }\n          break;\n        }\n\n        if (!belongsToItem(cur)) {break;}\n\n        // Strip indent: for lines at contentIndent+, strip contentIndent chars;\n        // for sub-list lines between minChildIndent and contentIndent, strip minChildIndent\n        const lineIndent = cur.match(/^\\s*/)![0].length;\n        if (lineIndent >= contentIndent) {\n          subLines.push(cur.substring(contentIndent));\n        } else {\n          // Sub-list item between minChildIndent and contentIndent\n          subLines.push(cur.substring(minChildIndent));\n        }\n        i++;\n      }\n\n      // Build the list item token\n      // If there are sub-lines, they become child content (recursively tokenized)\n      // Don't trim — preserve relative indentation of sub-lines\n      const childContent = subLines.join(\"\\n\").replace(/^\\n+|\\n+$/g, \"\");\n      tokens.push({\n        type: \"listItem\",\n        listType,\n        indent,\n        content: firstLineContent.trim(),\n        start,\n        checked,\n        childContent: childContent || undefined,\n      });\n      prevLineWasBlank = false;\n      continue;\n    }\n\n    // Block-level raw HTML: a line starting with `<tag>` (block-level tag),\n    // `<!-- ... -->`, `<?...?>`, `<!DOCTYPE ...>`, or `<![CDATA[...]]>`.\n    // Lines are emitted verbatim until the next blank line.\n    if (isHtmlBlockStart(line)) {\n      const htmlLines: string[] = [];\n      while (i < lines.length && lines[i].trim() !== \"\") {\n        htmlLines.push(lines[i]);\n        i++;\n      }\n      tokens.push({\n        type: \"rawHtml\",\n        content: htmlLines.join(\"\\n\"),\n      });\n      prevLineWasBlank = false;\n      continue;\n    }\n\n    // Paragraph (default)\n    const paraLines: string[] = [line];\n    i++;\n    while (i < lines.length) {\n      const nextLine = lines[i];\n      // Stop paragraph on blank line\n      if (nextLine.trim() === \"\") {break;}\n      // Stop on block-level element\n      if (/^(#{1,6})\\s/.test(nextLine)) {break;}\n      if (/^(`{3,}|~{3,})/.test(nextLine)) {break;}\n      if (/^\\s{0,3}>/.test(nextLine)) {break;}\n      if (/^(\\s{0,3})([-*_])\\s*(\\2\\s*){2,}$/.test(nextLine)) {break;}\n      if (/^\\s*([-*+]|\\d+[.)])\\s+/.test(nextLine)) {break;}\n      if (/^\\s*\\|(.+\\|)+\\s*$/.test(nextLine)) {break;}\n      if (isHtmlBlockStart(nextLine)) {break;}\n      // Check if next-next line is setext marker\n      if (\n        i + 1 < lines.length &&\n        /^[=-]+\\s*$/.test(lines[i + 1]) &&\n        nextLine.trim().length > 0\n      ) {\n        break;\n      }\n      paraLines.push(nextLine);\n      i++;\n    }\n    // CommonMark allows up to 3 leading spaces of indent on paragraph lines.\n    // Also strip trailing whitespace from the final line so a trailing\n    // hard-break sequence (`  \\n` at end of paragraph) doesn't leak as\n    // literal trailing spaces in the rendered output.\n    tokens.push({\n      type: \"paragraph\",\n      content: paraLines\n        .map((l) => l.replace(/^ {1,3}/, \"\"))\n        .join(\"\\n\")\n        .replace(/[ \\t]+$/, \"\"),\n    });\n    prevLineWasBlank = false;\n  }\n\n  return tokens;\n}\n\nfunction tryParseTable(\n  lines: string[],\n  start: number\n): { token: TableToken; nextLine: number } | null {\n  // A table needs at least a header row and a separator row\n  if (start + 1 >= lines.length) {return null;}\n\n  const headerLine = lines[start];\n  const separatorLine = lines[start + 1];\n\n  // Check separator line format: | --- | --- | or --- | --- (outer pipes optional)\n  // Must contain at least one pipe and only dashes, colons, pipes, and whitespace\n  if (\n    !separatorLine.includes(\"|\") ||\n    !/^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/.test(separatorLine)\n  ) {return null;}\n\n  // Check header line has at least one pipe (required to distinguish from plain text)\n  if (!headerLine.includes(\"|\")) {return null;}\n\n  const headers = parsePipeCells(headerLine);\n  const alignments = parseAlignments(separatorLine);\n\n  const rows: string[][] = [];\n  let i = start + 2;\n  while (i < lines.length) {\n    const line = lines[i];\n    if (!line.includes(\"|\")) {break;}\n    rows.push(parsePipeCells(line));\n    i++;\n  }\n\n  return {\n    token: {\n      type: \"table\",\n      headers,\n      rows,\n      alignments,\n    },\n    nextLine: i,\n  };\n}\n\nfunction parsePipeCells(line: string): string[] {\n  // Trim leading/trailing pipes and split\n  const trimmed = line.trim();\n  const withoutOuterPipes = trimmed.startsWith(\"|\")\n    ? trimmed.substring(1)\n    : trimmed;\n  const content = withoutOuterPipes.endsWith(\"|\")\n    ? withoutOuterPipes.substring(0, withoutOuterPipes.length - 1)\n    : withoutOuterPipes;\n\n  // Split by pipes, handling escaped pipes\n  const cells: string[] = [];\n  let current = \"\";\n  for (let i = 0; i < content.length; i++) {\n    if (content[i] === \"\\\\\" && i + 1 < content.length && content[i + 1] === \"|\") {\n      current += \"|\";\n      i++;\n    } else if (content[i] === \"|\") {\n      cells.push(current.trim());\n      current = \"\";\n    } else {\n      current += content[i];\n    }\n  }\n  cells.push(current.trim());\n\n  return cells;\n}\n\nfunction parseAlignments(\n  separatorLine: string\n): (\"left\" | \"center\" | \"right\" | null)[] {\n  const cells = parsePipeCells(separatorLine);\n  return cells.map((cell) => {\n    const trimmed = cell.trim();\n    const left = trimmed.startsWith(\":\");\n    const right = trimmed.endsWith(\":\");\n    if (left && right) {return \"center\";}\n    if (right) {return \"right\";}\n    if (left) {return \"left\";}\n    return null;\n  });\n}\n\n// ─── HTML Emitter ────────────────────────────────────────────────────────────\n\nfunction tokensToHtml(tokens: Token[]): string {\n  let html = \"\";\n  let i = 0;\n\n  while (i < tokens.length) {\n    const token = tokens[i];\n\n    switch (token.type) {\n      case \"heading\": {\n        const t = token as HeadingToken;\n        html += `<h${t.level}>${parseInline(t.content)}</h${t.level}>`;\n        i++;\n        break;\n      }\n\n      case \"paragraph\": {\n        const t = token as ParagraphToken;\n        html += `<p>${parseInline(t.content)}</p>`;\n        i++;\n        break;\n      }\n\n      case \"codeBlock\": {\n        const t = token as CodeBlockToken;\n        const langAttr = t.language\n          ? ` data-language=\"${escapeHtml(t.language)}\"`\n          : \"\";\n        html += `<pre><code${langAttr}>${escapeHtml(t.code)}</code></pre>`;\n        i++;\n        break;\n      }\n\n      case \"blockquote\": {\n        const t = token as BlockquoteToken;\n        // Recursively parse blockquote content as markdown\n        const innerTokens = tokenize(t.content);\n        const innerHtml = tokensToHtml(innerTokens);\n        html += `<blockquote>${innerHtml}</blockquote>`;\n        i++;\n        break;\n      }\n\n      case \"hr\":\n        html += `<hr>`;\n        i++;\n        break;\n\n      case \"listItem\": {\n        // Collect consecutive list items and build nested list structure\n        const listHtml = emitListItems(tokens, i);\n        html += listHtml.html;\n        i = listHtml.nextIndex;\n        break;\n      }\n\n      case \"table\": {\n        const t = token as TableToken;\n        html += emitTable(t);\n        i++;\n        break;\n      }\n\n      case \"rawHtml\": {\n        const t = token as RawHtmlToken;\n        html += t.content;\n        i++;\n        break;\n      }\n\n      default:\n        i++;\n    }\n  }\n\n  return html;\n}\n\nfunction emitListItems(\n  tokens: Token[],\n  startIdx: number\n): { html: string; nextIndex: number } {\n  let html = \"\";\n  let i = startIdx;\n  let currentListType: \"bullet\" | \"ordered\" | null = null;\n\n  while (i < tokens.length && tokens[i].type === \"listItem\") {\n    const item = tokens[i] as ListItemToken;\n    const effectiveType = getEffectiveListType(item.listType);\n\n    // Check if we need to switch list type\n    if (currentListType !== null && currentListType !== effectiveType) {\n      // Close current list, open new one\n      html += `</${currentListType === \"ordered\" ? \"ol\" : \"ul\"}>`;\n      currentListType = null;\n    }\n\n    // Open list if needed\n    if (currentListType === null) {\n      if (effectiveType === \"ordered\") {\n        const startAttr =\n          item.start !== undefined && item.start !== 1\n            ? ` start=\"${item.start}\"`\n            : \"\";\n        html += `<ol${startAttr}>`;\n      } else {\n        html += `<ul>`;\n      }\n      currentListType = effectiveType;\n    }\n\n    // Emit list item\n    if (item.listType === \"task\") {\n      const checkedAttr = item.checked ? \" checked\" : \"\";\n      html += `<li><input type=\"checkbox\" disabled${checkedAttr}><p>${parseInline(item.content)}</p>`;\n    } else {\n      html += `<li><p>${parseInline(item.content)}</p>`;\n    }\n\n    // Render child content (nested items, continuation paragraphs, etc.)\n    if (item.childContent) {\n      const childTokens = tokenize(item.childContent);\n      html += tokensToHtml(childTokens);\n    }\n\n    html += `</li>`;\n    i++;\n  }\n\n  // Close the list\n  if (currentListType !== null) {\n    html += `</${currentListType === \"ordered\" ? \"ol\" : \"ul\"}>`;\n  }\n\n  return { html, nextIndex: i };\n}\n\nfunction getEffectiveListType(\n  listType: \"bullet\" | \"ordered\" | \"task\"\n): \"bullet\" | \"ordered\" {\n  return listType === \"ordered\" ? \"ordered\" : \"bullet\";\n}\n\nfunction emitTable(table: TableToken): string {\n  let html = \"<table>\";\n\n  // BlockNote tables have no required header row, but the markdown table\n  // syntax does. When we serialize a headerless BlockNote table to markdown\n  // we emit an empty header row; on re-parse, treat that empty header as\n  // \"no header\" so the round-trip is stable (issue #739).\n  const headerIsEmpty = table.headers.every((h) => h.trim() === \"\");\n  const colCount = table.headers.length;\n\n  if (!headerIsEmpty) {\n    html += \"<thead><tr>\";\n    for (let c = 0; c < colCount; c++) {\n      const align = table.alignments[c];\n      const alignAttr = align ? ` align=\"${align}\"` : \"\";\n      html += `<th${alignAttr}>${parseInline(table.headers[c])}</th>`;\n    }\n    html += \"</tr></thead>\";\n  }\n\n  if (table.rows.length > 0) {\n    html += \"<tbody>\";\n    for (const row of table.rows) {\n      html += \"<tr>\";\n      for (let c = 0; c < colCount; c++) {\n        const cell = c < row.length ? row[c] : \"\";\n        const align = table.alignments[c];\n        const alignAttr = align ? ` align=\"${align}\"` : \"\";\n        html += `<td${alignAttr}>${parseInline(cell)}</td>`;\n      }\n      html += \"</tr>\";\n    }\n    html += \"</tbody>\";\n  }\n\n  html += \"</table>\";\n  return html;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\n/**\n * Convert a markdown string to an HTML string.\n * This is a direct replacement for the unified/remark/rehype pipeline.\n */\nexport function markdownToHtml(markdown: string): string {\n  const tokens = tokenize(markdown);\n  return tokensToHtml(tokens);\n}\n","import { Schema } from \"prosemirror-model\";\n\nimport { Block } from \"../../../blocks/defaultBlocks.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { HTMLToBlocks } from \"../html/parseHTML.js\";\nimport { markdownToHtml } from \"./markdownToHtml.js\";\n\nexport function markdownToHTML(markdown: string): string {\n  return markdownToHtml(markdown);\n}\n\nexport function markdownToBlocks<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(markdown: string, pmSchema: Schema): Block<BSchema, I, S>[] {\n  const htmlString = markdownToHTML(markdown);\n\n  return HTMLToBlocks(htmlString, pmSchema);\n}\n","import { createExternalHTMLExporter } from \"../../api/exporters/html/externalHTMLExporter.js\";\nimport { createInternalHTMLSerializer } from \"../../api/exporters/html/internalHTMLSerializer.js\";\nimport { blocksToMarkdown } from \"../../api/exporters/markdown/markdownExporter.js\";\nimport { HTMLToBlocks } from \"../../api/parsers/html/parseHTML.js\";\nimport {\n  markdownToBlocks,\n  markdownToHTML,\n} from \"../../api/parsers/markdown/parseMarkdown.js\";\nimport {\n  Block,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  PartialBlock,\n} from \"../../blocks/defaultBlocks.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../schema/index.js\";\nimport { BlockNoteEditor } from \"../BlockNoteEditor.js\";\n\nexport class ExportManager<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> {\n  constructor(private editor: BlockNoteEditor<BSchema, ISchema, SSchema>) {}\n\n  /**\n   * Exports blocks into a simplified HTML string. To better conform to HTML standards, children of blocks which aren't list\n   * items are un-nested in the output HTML.\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public blocksToHTMLLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.editor.document,\n  ): string {\n    const exporter = createExternalHTMLExporter(\n      this.editor.pmSchema,\n      this.editor,\n    );\n    return exporter.exportBlocks(blocks, {});\n  }\n\n  /**\n   * Serializes blocks into an HTML string in the format that would normally be rendered by the editor.\n   *\n   * Use this method if you want to server-side render HTML (for example, a blog post that has been edited in BlockNote)\n   * and serve it to users without loading the editor on the client (i.e.: displaying the blog post)\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public blocksToFullHTML(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.editor.document,\n  ): string {\n    const exporter = createInternalHTMLSerializer(\n      this.editor.pmSchema,\n      this.editor,\n    );\n    return exporter.serializeBlocks(blocks, {});\n  }\n\n  /**\n   * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and\n   * `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote\n   * doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.\n   * @param html The HTML string to parse blocks from.\n   * @returns The blocks parsed from the HTML string.\n   */\n  public tryParseHTMLToBlocks(\n    html: string,\n  ): Block<BSchema, ISchema, SSchema>[] {\n    return HTMLToBlocks(html, this.editor.pmSchema);\n  }\n\n  /**\n   * Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of\n   * BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.\n   * @param blocks An array of blocks that should be serialized into Markdown.\n   * @returns The blocks, serialized as a Markdown string.\n   */\n  public blocksToMarkdownLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.editor.document,\n  ): string {\n    return blocksToMarkdown(blocks, this.editor.pmSchema, this.editor, {});\n  }\n\n  /**\n   * Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on\n   * Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it\n   * as text.\n   * @param markdown The Markdown string to parse blocks from.\n   * @returns The blocks parsed from the Markdown string.\n   */\n  public tryParseMarkdownToBlocks(\n    markdown: string,\n  ): Block<BSchema, ISchema, SSchema>[] {\n    return markdownToBlocks(markdown, this.editor.pmSchema);\n  }\n\n  /**\n   * Paste HTML into the editor. Defaults to converting HTML to BlockNote HTML.\n   * @param html The HTML to paste.\n   * @param raw Whether to paste the HTML as is, or to convert it to BlockNote HTML.\n   */\n  public pasteHTML(html: string, raw = false) {\n    let htmlToPaste = html;\n    if (!raw) {\n      const blocks = this.tryParseHTMLToBlocks(html);\n      htmlToPaste = this.blocksToFullHTML(blocks);\n    }\n    if (!htmlToPaste) {\n      return;\n    }\n    this.editor.prosemirrorView?.pasteHTML(htmlToPaste);\n  }\n\n  /**\n   * Paste text into the editor. Defaults to interpreting text as markdown.\n   * @param text The text to paste.\n   */\n  public pasteText(text: string) {\n    return this.editor.prosemirrorView?.pasteText(text);\n  }\n\n  /**\n   * Paste markdown into the editor.\n   * @param markdown The markdown to paste.\n   */\n  public pasteMarkdown(markdown: string) {\n    const html = markdownToHTML(markdown);\n    return this.pasteHTML(html);\n  }\n}\n","import type { Node } from \"prosemirror-model\";\nimport {\n  NodeSelection,\n  TextSelection,\n  type Transaction,\n} from \"prosemirror-state\";\nimport type { TextCursorPosition } from \"../../../editor/cursorPositionTypes.js\";\nimport type {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { UnreachableCaseError } from \"../../../util/typescript.js\";\nimport {\n  getBlockInfo,\n  getBlockInfoFromTransaction,\n} from \"../../getBlockInfoFromPos.js\";\nimport { nodeToBlock } from \"../../nodeConversions/nodeToBlock.js\";\nimport { getNodeById } from \"../../nodeUtil.js\";\nimport { getBlockNoteSchema, getPmSchema } from \"../../pmUtil.js\";\n\nexport function getTextCursorPosition<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(tr: Transaction): TextCursorPosition<BSchema, I, S> {\n  const { bnBlock } = getBlockInfoFromTransaction(tr);\n  const pmSchema = getPmSchema(tr.doc);\n\n  const resolvedPos = tr.doc.resolve(bnBlock.beforePos);\n  // Gets previous blockContainer node at the same nesting level, if the current node isn't the first child.\n  const prevNode = resolvedPos.nodeBefore;\n\n  // Gets next blockContainer node at the same nesting level, if the current node isn't the last child.\n  const nextNode = tr.doc.resolve(bnBlock.afterPos).nodeAfter;\n\n  // Gets parent blockContainer node, if the current node is nested.\n  let parentNode: Node | undefined = undefined;\n  if (resolvedPos.depth > 1) {\n    // for nodes nested in bnBlocks\n    parentNode = resolvedPos.node();\n    if (!parentNode.type.isInGroup(\"bnBlock\")) {\n      // for blockGroups, we need to go one level up\n      parentNode = resolvedPos.node(resolvedPos.depth - 1);\n    }\n  }\n\n  return {\n    block: nodeToBlock(bnBlock.node, pmSchema),\n    prevBlock: prevNode === null ? undefined : nodeToBlock(prevNode, pmSchema),\n    nextBlock: nextNode === null ? undefined : nodeToBlock(nextNode, pmSchema),\n    parentBlock:\n      parentNode === undefined ? undefined : nodeToBlock(parentNode, pmSchema),\n  };\n}\n\nexport function setTextCursorPosition(\n  tr: Transaction,\n  targetBlock: BlockIdentifier,\n  placement: \"start\" | \"end\" = \"start\",\n) {\n  const id = typeof targetBlock === \"string\" ? targetBlock : targetBlock.id;\n  const pmSchema = getPmSchema(tr.doc);\n  const schema = getBlockNoteSchema(pmSchema);\n\n  const posInfo = getNodeById(id, tr.doc);\n  if (!posInfo) {\n    throw new Error(`Block with ID ${id} not found`);\n  }\n\n  const info = getBlockInfo(posInfo);\n\n  const contentType: \"none\" | \"inline\" | \"table\" =\n    schema.blockSchema[info.blockNoteType]!.content;\n\n  if (info.isBlockContainer) {\n    const blockContent = info.blockContent;\n    if (contentType === \"none\") {\n      tr.setSelection(NodeSelection.create(tr.doc, blockContent.beforePos));\n      return;\n    }\n\n    if (contentType === \"inline\") {\n      if (placement === \"start\") {\n        tr.setSelection(\n          TextSelection.create(tr.doc, blockContent.beforePos + 1),\n        );\n      } else {\n        tr.setSelection(\n          TextSelection.create(tr.doc, blockContent.afterPos - 1),\n        );\n      }\n    } else if (contentType === \"table\") {\n      if (placement === \"start\") {\n        // Need to offset the position as we have to get through the `tableRow`\n        // and `tableCell` nodes to get to the `tableParagraph` node we want to\n        // set the selection in.\n        tr.setSelection(\n          TextSelection.create(tr.doc, blockContent.beforePos + 4),\n        );\n      } else {\n        tr.setSelection(\n          TextSelection.create(tr.doc, blockContent.afterPos - 4),\n        );\n      }\n    } else {\n      throw new UnreachableCaseError(contentType);\n    }\n  } else {\n    const child =\n      placement === \"start\"\n        ? info.childContainer.node.firstChild!\n        : info.childContainer.node.lastChild!;\n\n    setTextCursorPosition(tr, child.attrs.id, placement);\n  }\n}\n","// THIS FILE IS AUTO-GENERATED. DO NOT EDIT DIRECTLY.\n// Source: https://data.iana.org/TLD/tlds-alpha-by-domain.txt\n// Regenerate with: pnpm --filter @blocknote/core update-tlds\n// Encoding format ported from linkifyjs (MIT) — trie collapsed into ASCII.\n\nexport const ENCODED_TLDS =\n  \"aaa1rp3bb0ott3vie4c1le2ogado5udhabi7c0ademy5centure6ountant0s9o1tor4d0s1ult4e0g1ro2tna4f0l1rica5g0akhan5ency5i0g1rbus3force5tel5kdn3l0ibaba4pay4lfinanz6state5y2sace3tom5m0azon4ericanexpress7family11x2fam3ica3sterdam8nalytics7droid5quan4z2o0l2partments8p0le4q0uarelle8r0ab1mco4chi3my2pa2t0e3s0da2ia2sociates9t0hleta5torney7u0ction5di0ble3o3spost5thor3o0s4w0s2x0a2z0ure5ba0by2idu3namex4d1k2r0celona5laycard4s5efoot5gains6seball5ketball8uhaus5yern5b0c1t1va3cg1n2d1e0ats2uty4er2rlin4st0buy5t2f1g1h0arti5i0ble3d1ke2ng0o3o1z2j1lack0friday9ockbuster8g1omberg7ue3m0s1w2n0pparibas9o0ats3ehringer8fa2m1nd2o0k0ing5sch2tik2on4t1utique6x2r0adesco6idgestone9oadway5ker3ther5ussels7s1t1uild0ers6siness6y1zz3v1w1y1z0h3ca0b1fe2l0l1vinklein9m0era3p2non3petown5ital0one8r0avan4ds2e0er0s4s2sa1e1h1ino4t0ering5holic7ba1n1re3c1d1enter4o1rn3f0a1d2g1h0anel2nel4rity4se2t2eap3intai5ristmas6ome4urch5i0priani6rcle4sco3tadel4i0c2y3k1l0aims4eaning6ick2nic1que6othing5ud3ub0med6m1n1o0ach3des3ffee4llege4ogne5m0mbank4unity6pany2re3uter5sec4ndos3struction8ulting7tact3ractors9oking4l1p2rsica5untry4pon0s4rses6pa2r0edit0card4union9icket5own3s1uise0s6u0isinella9v1w1x1y0mru3ou3z2dad1nce3ta1e1ing3sun4y2clk3ds2e0al0er2s3gree4livery5l1oitte5ta3mocrat6ntal2ist5si0gn4v2hl2iamonds6et2gital5rect0ory7scount3ver5h2y2j1k1m1np2o0cs1tor4g1mains5t1wnload7rive4tv2ubai3pont4rban5vag2r2z2earth3t2c0o2deka3u0cation8e1g1mail3erck5nergy4gineer0ing9terprises10pson4quipment8r0icsson6ni3s0q1tate5t1u0rovision8s2vents5xchange6pert3osed4ress5traspace10fage2il1rwinds6th3mily4n0s2rm0ers5shion4t3edex3edback6rrari3ero6i0delity5o2lm2nal1nce1ial7re0stone6mdale6sh0ing5t0ness6j1k1lickr3ghts4r2orist4wers5y2m1o0o0d1tball6rd1ex2sale4um3undation8x2r0ee1senius7l1ogans4ntier7tr2ujitsu5n0d2rniture7tbol5yi3ga0l0lery3o1up4me0s3p1rden4y2b0iz3d0n2e0a1nt0ing5orge5f1g0ee3h1i0ft0s3ves2ing5l0ass3e1obal2o4m0ail3bh2o1x2n1odaddy5ld0point6f2odyear5g0le4p1t1v2p1q1r0ainger5phics5tis4een3ipe3ocery4up4s1t1u0cci3ge2ide2tars5ru3w1y2hair2mburg5ngout5us3bo2dfc0bank7ealth0care8lp1sinki6re1mes5iphop4samitsu7tachi5v2k0t2m1n1ockey4ldings5iday5medepot5goods5s0ense7nda3rse3spital5t0ing5t0els3mail5use3w2r1sbc3t1u0ghes5yatt3undai7ibm2cbc2e1u2d1e0ee3fm2kano4l1m0amat4db2mo0bilien9n0c1dustries8finiti5o2g1k1stitute6urance4e4t0ernational10uit4vestments10o1piranga7q1r0ish4s0maili5t0anbul7t0au2v3jaguar4va3cb2e0ep2tzt3welry6io2ll2m0p2nj2o0bs1urg4t1y2p0morgan6rs3uegos4niper7kaufen5ddi3e0rryhotels6properties14fh2g1h1i0a1ds2m1ndle4tchen5wi3m1n1oeln3matsu5sher5p0mg2n2r0d1ed3uokgroup8w1y0oto4z2la0caixa5mborghini8er3nd0rover6xess5salle5t0ino3robe5w0yer5b1c1ds2ease3clerc5frak4gal2o2xus4gbt3i0dl2fe0insurance9style7ghting6ke2lly3mited4o2ncoln4k2ve1ing5k1lc1p2oan0s3cker3us3l1ndon4tte1o3ve3pl0financial11r1s1t0d0a3u0ndbeck6xe1ury5v1y2ma0drid4if1son4keup4n0agement7go3p1rket0ing3s4riott5shalls7ttel5ba2c0kinsey7d1e0d0ia3et2lbourne7me1orial6n0u2rck0msd7g1h1iami3crosoft7l1ni1t2t0subishi9k1l0b1s2m0a2n1o0bi0le4da2e1i1m1nash3ey2ster5rmon3tgage6scow4to0rcycles9v0ie4p1q1r1s0d2t0n1r2u0seum3ic4v1w1x1y1z2na0b1goya4me2vy3ba2c1e0c1t0bank4flix4work5ustar5w0s2xt0direct7us4f0l2g0o2hk2i0co2ke1on3nja3ssan1y5l1o0kia3rton4w0ruz3tv4p1r0a1w2tt2u1yc2z2obi1server7ffice5kinawa6layan0group9lo3m0ega4ne1g1l0ine5oo2pen3racle3nge4g0anic5igins6saka4tsuka4t2vh3pa0ge2nasonic7ris2s1tners4s1y3y2ccw3e0t2f0izer5g1h0armacy6d1ilips5one2to0graphy6s4ysio5ics1tet2ures6d1n0g1k2oneer5zza4k1l0ace2y0station9umbing5s3m1n0c2ohl2ker3litie5rn2st3r0axi3ess3ime3o0d0uctions8f1gressive8mo2perties3y5tection8u0dential9s1t1ub2w0c2y2qa1pon3uebec3st5racing4dio4e0ad1lestate6tor2y4cipes5d0umbrella9hab3ise0n3t2liance6n0t0als5pair3ort3ublican8st0aurant8view0s5xroth6ich0ardli6oh3l1o1p2o0cks3deo3gers4om3s0vp3u0gby3hr2n2w0e2yukyu6sa0arland6fe0ty4kura4le1on3msclub4ung5ndvik0coromant12ofi4p1rl2s1ve2xo3b0i1s2c0b1haeffler7midt4olarships8ol3ule3warz5ience5ot3d1e0arch3t2cure1ity6ek2lect4ner3rvices6ven3w1x0y3fr2g1h0angrila6rp3ell3ia1ksha5oes2p0ping5uji3w3i0lk2na1gles5te3j1k0i0n2y0pe4l0ing4m0art3ile4n0cf3o0ccer3ial4ftbank4ware6hu2lar2utions7ng1y2y2pa0ce3ort2t3r0l2s1t0ada2ples4r1tebank4farm7c0group6ockholm6rage3e3ream4udio2y3yle4u0cks3pplies3y2ort5rf1gery5zuki5v1watch4iss4x1y0dney4stems6z2tab1ipei4lk2obao4rget4tamotors6r2too4x0i3c0i2d0k2eam2ch0nology8l1masek5nnis4va3f1g1h0d1eater2re6iaa2ckets5enda4ps2res2ol4j0maxx4x2k0maxx5l1m0all4n1o0day3kyo3ols3p1ray3shiba5tal3urs3wn2yota3s3r0ade1ing4ining5vel0ers0insurance16ust3v2t1ube2i1nes3shu4v0s2w1z2ua1bank3s2g1k1nicom3versity8o2ol2ps2s1y1z2va0cations7na1guard7c1e0gas3ntures6risign5sicherung10t2g1i0ajes4deo3g1king4llas4n1p1rgin4sa1ion4va1o3laanderen9n1odka3lvo3te1ing3o2yage5u2wales2mart4ter4ng0gou5tch0es6eather0channel12bcam3er2site5d0ding5ibo2r3f1hoswho6ien2ki2lliamhill9n0dows4e1ners6me2oodside6rk0s2ld3w2s1tc1f3xbox3erox4ihuan4n2xx2yz3yachts4hoo3maxun5ndex5e1odobashi7ga2kohama6u0tube6t1un3za0ppos4ra3ero3ip2m1one3uerich6w2\";\n","/**\n * Lightweight URL detection module replacing linkifyjs.\n *\n * Provides two functions:\n * - findLinks(): find all URLs/emails in arbitrary text (replaces linkifyjs find())\n * - tokenizeLink(): tokenize a single word for autolink validation (replaces linkifyjs tokenize())\n */\n\nimport { ENCODED_TLDS } from \"./tlds.js\";\n\nexport interface LinkMatch {\n  type: string;\n  value: string;\n  isLink: boolean;\n  href: string;\n  start: number;\n  end: number;\n}\n\n// ---------------------------------------------------------------------------\n// TLD set – used only for schemeless URL validation.\n// Protocol URLs (http://, https://, etc.) skip TLD checks.\n// Decoded once at module load from the trie-encoded IANA list in tlds.ts.\n// ---------------------------------------------------------------------------\n\nfunction decodeTlds(encoded: string): string[] {\n  const words: string[] = [];\n  const stack: string[] = [];\n  let i = 0;\n  while (i < encoded.length) {\n    let popDigitCount = 0;\n    while (\n      i + popDigitCount < encoded.length &&\n      encoded.charCodeAt(i + popDigitCount) >= 48 &&\n      encoded.charCodeAt(i + popDigitCount) <= 57\n    ) {\n      popDigitCount++;\n    }\n    if (popDigitCount > 0) {\n      words.push(stack.join(\"\"));\n      let popCount = parseInt(encoded.substring(i, i + popDigitCount), 10);\n      while (popCount-- > 0) {\n        stack.pop();\n      }\n      i += popDigitCount;\n    } else {\n      stack.push(encoded[i]);\n      i++;\n    }\n  }\n  return words;\n}\n\nconst TLD_SET = new Set(decodeTlds(ENCODED_TLDS));\n\n// Special hostnames recognized without a TLD\nconst SPECIAL_HOSTS = new Set([\"localhost\"]);\n\n// ---------------------------------------------------------------------------\n// Regex building blocks\n// ---------------------------------------------------------------------------\n\n// Characters that are unlikely to be part of a URL when they appear at the end\nconst TRAILING_PUNCT = /[.,;:!?\"']+$/;\n\n// Protocol URLs: http:// https:// ftp:// ftps://\nconst PROTOCOL_RE =\n  /(?:https?|ftp|ftps):\\/\\/[^\\s]+/g;\n\n// Mailto URLs: mailto:...\nconst MAILTO_RE = /mailto:[^\\s]+/g;\n\n// Bare email addresses: user@domain.tld\nconst EMAIL_RE =\n  /[a-zA-Z0-9._%+-]+@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}/g;\n\n// Schemeless URLs: domain.tld with optional port and path\n// Hostname: one or more labels separated by dots, TLD is alpha-only 2+ chars\nconst SCHEMELESS_RE =\n  /(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}(?::\\d{1,5})?(?:[/?#][^\\s]*)?/g;\n\n// ---------------------------------------------------------------------------\n// Post-processing helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Trim trailing punctuation and unbalanced closing brackets from a URL match.\n */\nfunction trimTrailing(value: string): string {\n  let v = value;\n\n  // Iteratively trim trailing punctuation and unbalanced brackets\n  let changed = true;\n  while (changed) {\n    changed = false;\n\n    // Trim trailing punctuation chars\n    const before = v;\n    v = v.replace(TRAILING_PUNCT, \"\");\n    if (v !== before) {\n      changed = true;\n    }\n\n    // Trim unbalanced closing brackets from the end\n    for (const [open, close] of [\n      [\"(\", \")\"],\n      [\"[\", \"]\"],\n    ] as const) {\n      while (v.endsWith(close)) {\n        const openCount = countChar(v, open);\n        const closeCount = countChar(v, close);\n        if (closeCount > openCount) {\n          v = v.slice(0, -1);\n          changed = true;\n        } else {\n          break;\n        }\n      }\n    }\n  }\n\n  return v;\n}\n\nfunction countChar(str: string, ch: string): number {\n  let count = 0;\n  for (let i = 0; i < str.length; i++) {\n    if (str[i] === ch) {\n      count++;\n    }\n  }\n  return count;\n}\n\n/**\n * Extract the TLD from a hostname string.\n * Returns the last dot-separated segment.\n */\nfunction extractTld(hostname: string): string {\n  const parts = hostname.split(\".\");\n  return parts[parts.length - 1].toLowerCase();\n}\n\nfunction isValidTld(hostname: string): boolean {\n  const tld = extractTld(hostname);\n  return TLD_SET.has(tld);\n}\n\n/**\n * Build the href for a URL value, prepending the default protocol if needed.\n */\nfunction buildHref(\n  value: string,\n  type: string,\n  defaultProtocol: string\n): string {\n  if (type === \"email\") {\n    return \"mailto:\" + value;\n  }\n  if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(value) || /^mailto:/i.test(value)) {\n    // Already has a protocol\n    return value;\n  }\n  return defaultProtocol + \"://\" + value;\n}\n\n// ---------------------------------------------------------------------------\n// findLinks()\n// ---------------------------------------------------------------------------\n\nexport interface FindOptions {\n  defaultProtocol?: string;\n}\n\ninterface RawMatch {\n  type: string;\n  value: string;\n  start: number;\n  end: number;\n}\n\n/**\n * Find all URLs and email addresses in the given text.\n * Drop-in replacement for linkifyjs find().\n */\nexport function findLinks(\n  text: string,\n  options?: FindOptions\n): LinkMatch[] {\n  if (!text) {\n    return [];\n  }\n\n  const defaultProtocol = options?.defaultProtocol || \"http\";\n  const rawMatches: RawMatch[] = [];\n\n  // 1. Protocol URLs\n  for (const m of text.matchAll(PROTOCOL_RE)) {\n    rawMatches.push({\n      type: \"url\",\n      value: m[0],\n      start: m.index!,\n      end: m.index! + m[0].length,\n    });\n  }\n\n  // 2. Mailto URLs\n  for (const m of text.matchAll(MAILTO_RE)) {\n    rawMatches.push({\n      type: \"url\",\n      value: m[0],\n      start: m.index!,\n      end: m.index! + m[0].length,\n    });\n  }\n\n  // 3. Bare email addresses\n  for (const m of text.matchAll(EMAIL_RE)) {\n    rawMatches.push({\n      type: \"email\",\n      value: m[0],\n      start: m.index!,\n      end: m.index! + m[0].length,\n    });\n  }\n\n  // 4. Schemeless URLs\n  for (const m of text.matchAll(SCHEMELESS_RE)) {\n    rawMatches.push({\n      type: \"url\",\n      value: m[0],\n      start: m.index!,\n      end: m.index! + m[0].length,\n    });\n  }\n\n  // Sort by start position\n  rawMatches.sort((a, b) => a.start - b.start || b.end - a.end);\n\n  // Deduplicate overlapping matches (prefer earlier & longer)\n  const deduped: RawMatch[] = [];\n  let lastEnd = -1;\n  for (const match of rawMatches) {\n    if (match.start >= lastEnd) {\n      deduped.push(match);\n      lastEnd = match.end;\n    }\n  }\n\n  // Post-process each match\n  const results: LinkMatch[] = [];\n  for (const raw of deduped) {\n    const value = trimTrailing(raw.value);\n    if (!value) {\n      continue;\n    }\n\n    const start = raw.start;\n    const end = start + value.length;\n\n    // For schemeless URLs, validate TLD\n    if (raw.type === \"url\" && !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value)) {\n      const hostname = new URL(\"http://\" + value).hostname;\n      if (!isValidTld(hostname)) {\n        continue;\n      }\n    }\n\n    // For emails, validate TLD\n    if (raw.type === \"email\") {\n      const hostname = value.split(\"@\")[1];\n      if (!isValidTld(hostname)) {\n        continue;\n      }\n    }\n\n    const href = buildHref(value, raw.type, defaultProtocol);\n\n    results.push({\n      type: raw.type,\n      value,\n      isLink: true,\n      href,\n      start,\n      end,\n    });\n  }\n\n  return results;\n}\n\n// ---------------------------------------------------------------------------\n// tokenizeLink()\n// ---------------------------------------------------------------------------\n\n/**\n * Tokenize a single word for autolink validation.\n * Drop-in replacement for: tokenize(word).map(t => t.toObject(defaultProtocol))\n *\n * Returns an array of LinkMatch tokens. The autolink code checks:\n * - 1 token with isLink=true → valid single link\n * - 3 tokens with middle isLink=true and outer brackets → valid wrapped link\n */\nexport function tokenizeLink(\n  text: string,\n  defaultProtocol = \"http\"\n): LinkMatch[] {\n  if (!text) {\n    return [nonLinkToken(text, 0, 0)];\n  }\n\n  // Check for bracket wrapping: (url), [url], {url}\n  const brackets: Array<[string, string]> = [\n    [\"(\", \")\"],\n    [\"[\", \"]\"],\n    [\"{\", \"}\"],\n  ];\n  for (const [open, close] of brackets) {\n    if (text.startsWith(open) && text.endsWith(close) && text.length > 2) {\n      const inner = text.slice(1, -1);\n      if (isSingleUrl(inner)) {\n        return [\n          nonLinkToken(open, 0, 1),\n          linkToken(inner, 1, 1 + inner.length, defaultProtocol),\n          nonLinkToken(close, 1 + inner.length, text.length),\n        ];\n      }\n    }\n  }\n\n  // Check for trailing punctuation (e.g., \"example.com.\" → link + dot)\n  if (text.endsWith(\".\") && text.length > 1) {\n    const withoutDot = text.slice(0, -1);\n    if (isSingleUrl(withoutDot)) {\n      return [\n        linkToken(withoutDot, 0, withoutDot.length, defaultProtocol),\n        nonLinkToken(\".\", withoutDot.length, text.length),\n      ];\n    }\n  }\n\n  // Check if the whole text is a single URL\n  if (isSingleUrl(text)) {\n    return [linkToken(text, 0, text.length, defaultProtocol)];\n  }\n\n  // Not a link\n  return [nonLinkToken(text, 0, text.length)];\n}\n\n/**\n * Check if a string is a single complete URL (no extra chars).\n */\nfunction isSingleUrl(text: string): boolean {\n  // Protocol URLs\n  if (/^(?:https?|ftp|ftps):\\/\\/[^\\s]+$/.test(text)) {\n    return true;\n  }\n\n  // Mailto URLs\n  if (/^mailto:[^\\s]+$/.test(text)) {\n    return true;\n  }\n\n  // Special hosts (e.g., localhost)\n  if (SPECIAL_HOSTS.has(text.toLowerCase())) {\n    return true;\n  }\n\n  // Schemeless URLs: hostname.tld with optional port and path\n  const schemelessFull =\n    /^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\\.)+([a-zA-Z]{2,})(?::\\d{1,5})?(?:[/?#][^\\s]*)?$/;\n  const match = text.match(schemelessFull);\n  if (match) {\n    const tld = match[1].toLowerCase();\n    // TLD must be a-z only (no numbers) and recognized\n    if (TLD_SET.has(tld)) {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nfunction linkToken(\n  value: string,\n  start: number,\n  end: number,\n  defaultProtocol: string\n): LinkMatch {\n  const type =\n    value.includes(\"@\") && !value.includes(\"://\") && !value.startsWith(\"mailto:\")\n      ? \"email\"\n      : \"url\";\n  return {\n    type,\n    value,\n    isLink: true,\n    href: buildHref(value, type, defaultProtocol),\n    start,\n    end,\n  };\n}\n\nfunction nonLinkToken(value: string, start: number, end: number): LinkMatch {\n  return {\n    type: \"text\",\n    value,\n    isLink: false,\n    href: value,\n    start,\n    end,\n  };\n}\n","// From DOMPurify\n// https://github.com/cure53/DOMPurify/blob/main/src/regexp.ts\nexport const UNICODE_WHITESPACE_PATTERN =\n  \"[\\u0000-\\u0020\\u00A0\\u1680\\u180E\\u2000-\\u2029\\u205F\\u3000]\";\n\nexport const UNICODE_WHITESPACE_REGEX = new RegExp(UNICODE_WHITESPACE_PATTERN);\nexport const UNICODE_WHITESPACE_REGEX_END = new RegExp(\n  `${UNICODE_WHITESPACE_PATTERN}$`\n);\nexport const UNICODE_WHITESPACE_REGEX_GLOBAL = new RegExp(\n  UNICODE_WHITESPACE_PATTERN,\n  \"g\"\n);\n","import type { NodeWithPos } from \"@tiptap/core\";\nimport {\n  combineTransactionSteps,\n  findChildrenInRange,\n  getChangedRanges,\n  getMarksBetween,\n} from \"@tiptap/core\";\nimport type { MarkType } from \"@tiptap/pm/model\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { LinkMatch } from \"./linkDetector.js\";\nimport { tokenizeLink } from \"./linkDetector.js\";\n\nimport {\n  UNICODE_WHITESPACE_REGEX,\n  UNICODE_WHITESPACE_REGEX_END,\n} from \"./whitespace.js\";\n\n/**\n * Check if the provided tokens form a valid link structure, which can either be a single link token\n * or a link token surrounded by parentheses or square brackets.\n */\nfunction isValidLinkStructure(tokens: LinkMatch[]) {\n  if (tokens.length === 1) {\n    return tokens[0].isLink;\n  }\n\n  if (tokens.length === 3 && tokens[1].isLink) {\n    return [\"()\", \"[]\"].includes(tokens[0].value + tokens[2].value);\n  }\n\n  return false;\n}\n\ntype AutolinkOptions = {\n  type: MarkType;\n  defaultProtocol: string;\n  validate: (url: string) => boolean;\n  shouldAutoLink: (url: string) => boolean;\n};\n\n/**\n * Plugin that automatically adds link marks when typing URLs.\n */\nexport function autolink(options: AutolinkOptions): Plugin {\n  return new Plugin({\n    key: new PluginKey(\"autolink\"),\n    appendTransaction: (transactions, oldState, newState) => {\n      const docChanges =\n        transactions.some((transaction) => transaction.docChanged) &&\n        !oldState.doc.eq(newState.doc);\n\n      const preventAutolink = transactions.some((transaction) =>\n        transaction.getMeta(\"preventAutolink\")\n      );\n\n      if (!docChanges || preventAutolink) {\n        return;\n      }\n\n      const { tr } = newState;\n      const transform = combineTransactionSteps(oldState.doc, [\n        ...transactions,\n      ]);\n      const changes = getChangedRanges(transform);\n\n      changes.forEach(({ newRange }) => {\n        const nodesInChangedRanges = findChildrenInRange(\n          newState.doc,\n          newRange,\n          (node) => node.isTextblock\n        );\n\n        let textBlock: NodeWithPos | undefined;\n        let textBeforeWhitespace: string | undefined;\n\n        if (nodesInChangedRanges.length > 1) {\n          textBlock = nodesInChangedRanges[0];\n          textBeforeWhitespace = newState.doc.textBetween(\n            textBlock.pos,\n            textBlock.pos + textBlock.node.nodeSize,\n            undefined,\n            \" \"\n          );\n        } else if (nodesInChangedRanges.length) {\n          const endText = newState.doc.textBetween(\n            newRange.from,\n            newRange.to,\n            \" \",\n            \" \"\n          );\n          if (!UNICODE_WHITESPACE_REGEX_END.test(endText)) {\n            return;\n          }\n          textBlock = nodesInChangedRanges[0];\n          textBeforeWhitespace = newState.doc.textBetween(\n            textBlock.pos,\n            newRange.to,\n            undefined,\n            \" \"\n          );\n        }\n\n        if (textBlock && textBeforeWhitespace) {\n          const wordsBeforeWhitespace = textBeforeWhitespace\n            .split(UNICODE_WHITESPACE_REGEX)\n            .filter(Boolean);\n\n          if (wordsBeforeWhitespace.length <= 0) {\n            return;\n          }\n\n          const lastWordBeforeSpace =\n            wordsBeforeWhitespace[wordsBeforeWhitespace.length - 1];\n          const lastWordAndBlockOffset =\n            textBlock.pos +\n            textBeforeWhitespace.lastIndexOf(lastWordBeforeSpace);\n\n          if (!lastWordBeforeSpace) {\n            return;\n          }\n\n          const linksBeforeSpace = tokenizeLink(\n            lastWordBeforeSpace,\n            options.defaultProtocol\n          );\n\n          if (!isValidLinkStructure(linksBeforeSpace)) {\n            return;\n          }\n\n          linksBeforeSpace\n            .filter((link) => link.isLink)\n            .map((link) => ({\n              ...link,\n              from: lastWordAndBlockOffset + link.start + 1,\n              to: lastWordAndBlockOffset + link.end + 1,\n            }))\n            // ignore link inside code mark\n            .filter((link) => {\n              if (!newState.schema.marks.code) {\n                return true;\n              }\n\n              return !newState.doc.rangeHasMark(\n                link.from,\n                link.to,\n                newState.schema.marks.code\n              );\n            })\n            .filter((link) => options.validate(link.value))\n            .filter((link) => options.shouldAutoLink(link.value))\n            .forEach((link) => {\n              if (\n                getMarksBetween(link.from, link.to, newState.doc).some(\n                  (item) => item.mark.type === options.type\n                )\n              ) {\n                return;\n              }\n\n              tr.addMark(\n                link.from,\n                link.to,\n                options.type.create({\n                  href: link.href,\n                })\n              );\n            });\n        }\n      });\n\n      if (!tr.steps.length) {\n        return;\n      }\n\n      return tr;\n    },\n  });\n}\n","import type { Editor } from \"@tiptap/core\";\nimport { getAttributes } from \"@tiptap/core\";\nimport type { MarkType } from \"@tiptap/pm/model\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport type { BlockNoteEditor } from \"../../../../editor/BlockNoteEditor.js\";\n\ntype ClickHandlerOptions = {\n  type: MarkType;\n  tiptapEditor: Editor;\n  editor?: BlockNoteEditor<any, any, any>;\n  onClick?: (\n    event: MouseEvent,\n    editor: BlockNoteEditor<any, any, any>,\n  ) => boolean | void;\n};\n\nexport function clickHandler(options: ClickHandlerOptions): Plugin {\n  return new Plugin({\n    key: new PluginKey(\"handleClickLink\"),\n    props: {\n      handleClick: (view, _pos, event) => {\n        if (event.button !== 0) {\n          return false;\n        }\n\n        if (!view.editable) {\n          return false;\n        }\n\n        let link: HTMLAnchorElement | null = null;\n\n        if (\n          event.target instanceof HTMLAnchorElement &&\n          // Differentiate between link inline content and read-only links.\n          event.target.getAttribute(\"data-inline-content-type\") === \"link\"\n        ) {\n          link = event.target;\n        } else {\n          const target = event.target as HTMLElement | null;\n          if (!target) {\n            return false;\n          }\n\n          const root = options.tiptapEditor.view.dom;\n\n          // Intentionally limit the lookup to the editor root.\n          // Using tag names like DIV as boundaries breaks with custom NodeViews,\n          link = target.closest<HTMLAnchorElement>(\n            'a[data-inline-content-type=\"link\"]',\n          );\n\n          if (link && !root.contains(link)) {\n            link = null;\n          }\n        }\n\n        if (!link) {\n          return false;\n        }\n\n        if (options.onClick) {\n          if (!options.editor) {\n            throw new Error(\"BlockNoteEditor not found in Link click handler\");\n          }\n          const result = options.onClick(event, options.editor);\n          return result ?? true;\n        }\n\n        const attrs = getAttributes(view.state, options.type.name);\n        const href = link.href ?? attrs.href;\n        const target = link.target ?? attrs.target;\n\n        if (href) {\n          window.open(href, target);\n          return true;\n        }\n\n        return false;\n      },\n    },\n  });\n}\n","import type { Editor } from \"@tiptap/core\";\nimport type { MarkType } from \"@tiptap/pm/model\";\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport { findLinks } from \"./linkDetector.js\";\n\ntype PasteHandlerOptions = {\n  editor: Editor;\n  defaultProtocol: string;\n  type: MarkType;\n  shouldAutoLink?: (url: string) => boolean;\n  isValidLink: (href: string) => boolean;\n};\n\nexport function pasteHandler(options: PasteHandlerOptions): Plugin {\n  return new Plugin({\n    key: new PluginKey(\"handlePasteLink\"),\n    props: {\n      handlePaste: (view, _event, slice) => {\n        const { shouldAutoLink, isValidLink } = options;\n        const { state } = view;\n        const { selection } = state;\n        const { empty } = selection;\n\n        if (empty) {\n          return false;\n        }\n\n        let textContent = \"\";\n\n        slice.content.forEach((node) => {\n          textContent += node.textContent;\n        });\n\n        const link = findLinks(textContent, {\n          defaultProtocol: options.defaultProtocol,\n        }).find((item) => item.isLink && item.value === textContent);\n\n        if (\n          !textContent ||\n          !link ||\n          !isValidLink(link.value) ||\n          (shouldAutoLink !== undefined && !shouldAutoLink(link.value))\n        ) {\n          return false;\n        }\n\n        return options.editor.commands.setMark(options.type, {\n          href: link.href,\n        });\n      },\n    },\n  });\n}\n","import type { PasteRuleMatch } from \"@tiptap/core\";\nimport { Mark, markPasteRule, mergeAttributes } from \"@tiptap/core\";\nimport type { Plugin } from \"@tiptap/pm/state\";\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport { createExtension } from \"../../../editor/BlockNoteExtension.js\";\nimport { autolink } from \"./helpers/autolink.js\";\nimport { findLinks } from \"./helpers/linkDetector.js\";\nimport { clickHandler } from \"./helpers/clickHandler.js\";\nimport { pasteHandler } from \"./helpers/pasteHandler.js\";\nimport { UNICODE_WHITESPACE_REGEX_GLOBAL } from \"./helpers/whitespace.js\";\n\nconst DEFAULT_PROTOCOL = \"https\";\n\n// Pre-compiled regex for URI protocol validation.\n// Allows: http, https, ftp, ftps, mailto, tel, callto, sms, cid, xmpp\nconst ALLOWED_URI_REGEX =\n  // eslint-disable-next-line no-useless-escape\n  /^(?:(?:http|https|ftp|ftps|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z0-9+.\\-]+(?:[^a-z+.\\-:]|$))/i;\n\nexport function isAllowedUri(uri: string | undefined): boolean {\n  if (!uri) {\n    return true;\n  }\n  const cleaned = uri.replace(UNICODE_WHITESPACE_REGEX_GLOBAL, \"\");\n  return ALLOWED_URI_REGEX.test(cleaned);\n}\n\n/**\n * Determine whether a detected URL should be auto-linked.\n * URLs with explicit protocols are always auto-linked.\n * Bare hostnames must have a TLD (no IP addresses or single words).\n */\nfunction shouldAutoLink(url: string): boolean {\n  const hasProtocol = /^[a-z][a-z0-9+.-]*:\\/\\//i.test(url);\n  const hasMaybeProtocol = /^[a-z][a-z0-9+.-]*:/i.test(url);\n\n  if (hasProtocol || (hasMaybeProtocol && !url.includes(\"@\"))) {\n    return true;\n  }\n  // Strip userinfo (user:pass@) if present, then extract hostname\n  const urlWithoutUserinfo = url.includes(\"@\") ? url.split(\"@\").pop()! : url;\n  const hostname = urlWithoutUserinfo.split(/[/?#:]/)[0];\n\n  // Don't auto-link IP addresses without protocol\n  if (/^\\d{1,3}(\\.\\d{1,3}){3}$/.test(hostname)) {\n    return false;\n  }\n  // Don't auto-link single-word hostnames without TLD (e.g., \"localhost\")\n  if (!/\\./.test(hostname)) {\n    return false;\n  }\n  return true;\n}\n\nexport type LinkOptions = {\n  HTMLAttributes: Record<string, any>;\n  editor?: BlockNoteEditor<any, any, any>;\n  onClick?: (\n    event: MouseEvent,\n    editor: BlockNoteEditor<any, any, any>,\n  ) => boolean | void;\n  isValidLink: (href: string) => boolean;\n};\n\n/**\n * BlockNote Link mark extension.\n */\nexport const Link = Mark.create<LinkOptions>({\n  name: \"link\",\n\n  keepOnSplit: false,\n\n  exitable: true,\n\n  inclusive: false,\n\n  addOptions() {\n    return {\n      HTMLAttributes: {\n        target: \"_blank\",\n        rel: \"noopener noreferrer nofollow\",\n        className: \"bn-inline-content-section\",\n        \"data-inline-content-type\": \"link\",\n      },\n      editor: undefined,\n      onClick: undefined,\n      isValidLink: isAllowedUri,\n    };\n  },\n\n  addAttributes() {\n    return {\n      href: {\n        default: null,\n        parseHTML(element) {\n          return element.getAttribute(\"href\");\n        },\n      },\n    };\n  },\n\n  parseHTML() {\n    const isValidLink = this.options.isValidLink;\n    return [\n      {\n        tag: \"a[href]\",\n        getAttrs: (dom) => {\n          const href = (dom as HTMLElement).getAttribute(\"href\");\n          if (!href || !isValidLink(href)) {\n            return false;\n          }\n          return null;\n        },\n      },\n    ];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    if (!this.options.isValidLink(HTMLAttributes.href)) {\n      return [\n        \"a\",\n        mergeAttributes(\n          {\n            ...HTMLAttributes,\n            href: \"\",\n          },\n          this.options.HTMLAttributes,\n        ),\n        0,\n      ];\n    }\n\n    return [\n      \"a\",\n      mergeAttributes(HTMLAttributes, this.options.HTMLAttributes),\n      0,\n    ];\n  },\n\n  addPasteRules() {\n    const isValidLink = this.options.isValidLink;\n    return [\n      markPasteRule({\n        find: (text) => {\n          const foundLinks: PasteRuleMatch[] = [];\n\n          if (text) {\n            const links = findLinks(text, {\n              defaultProtocol: DEFAULT_PROTOCOL,\n            }).filter((item) => item.isLink && isValidLink(item.value));\n\n            for (const link of links) {\n              if (!shouldAutoLink(link.value)) {\n                continue;\n              }\n\n              foundLinks.push({\n                text: link.value,\n                data: { href: link.href },\n                index: link.start,\n              });\n            }\n          }\n\n          return foundLinks;\n        },\n        type: this.type,\n        getAttributes: (match) => ({\n          href: match.data?.href,\n        }),\n      }),\n    ];\n  },\n\n  addProseMirrorPlugins() {\n    const plugins: Plugin[] = [];\n\n    plugins.push(\n      autolink({\n        type: this.type,\n        defaultProtocol: DEFAULT_PROTOCOL,\n        validate: this.options.isValidLink,\n        shouldAutoLink,\n      }),\n    );\n\n    plugins.push(\n      clickHandler({\n        type: this.type,\n        tiptapEditor: this.editor,\n        editor: this.options.editor,\n        onClick: this.options.onClick,\n      }),\n    );\n\n    plugins.push(\n      pasteHandler({\n        editor: this.editor,\n        defaultProtocol: DEFAULT_PROTOCOL,\n        type: this.type,\n        shouldAutoLink,\n        isValidLink: this.options.isValidLink,\n      }),\n    );\n\n    return plugins;\n  },\n});\n\ntype LinkExtensionOptions = {\n  HTMLAttributes?: Record<string, any>;\n  onClick?: (\n    event: MouseEvent,\n    editor: BlockNoteEditor<any, any, any>,\n  ) => boolean | void;\n  isValidLink?: (href: string) => boolean;\n};\n\n/**\n * BlockNote extension wrapping the {@link Link} TipTap mark. Wrapping the mark\n * lets other extensions order their click handlers relative to the link click\n * handler via `runsBefore: [\"link\"]`.\n */\nexport const LinkExtension = createExtension<any, LinkExtensionOptions>(\n  ({ editor, options }) => {\n    return {\n      key: \"link\",\n      tiptapExtensions: [\n        Link.configure({\n          HTMLAttributes: options.HTMLAttributes ?? {},\n          editor,\n          onClick: options.onClick,\n          ...(options.isValidLink ? { isValidLink: options.isValidLink } : {}),\n        }),\n      ],\n    } as const;\n  },\n);\n","export const acceptedMIMETypes = [\n  \"vscode-editor-data\",\n  \"blocknote/html\",\n  \"text/markdown\",\n  \"text/html\",\n  \"text/plain\",\n  \"Files\",\n] as const;\n","import { Block, PartialBlock } from \"../../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { getNearestBlockPos } from \"../../getBlockInfoFromPos.js\";\nimport { acceptedMIMETypes } from \"./acceptedMIMETypes.js\";\n\nfunction checkFileExtensionsMatch(\n  fileExtension1: string,\n  fileExtension2: string,\n) {\n  if (!fileExtension1.startsWith(\".\") || !fileExtension2.startsWith(\".\")) {\n    throw new Error(`The strings provided are not valid file extensions.`);\n  }\n\n  return fileExtension1 === fileExtension2;\n}\n\nfunction checkMIMETypesMatch(mimeType1: string, mimeType2: string) {\n  const types1 = mimeType1.split(\"/\");\n  const types2 = mimeType2.split(\"/\");\n\n  if (types1.length !== 2) {\n    throw new Error(`The string ${mimeType1} is not a valid MIME type.`);\n  }\n  if (types2.length !== 2) {\n    throw new Error(`The string ${mimeType2} is not a valid MIME type.`);\n  }\n\n  if (types1[1] === \"*\" || types2[1] === \"*\") {\n    return types1[0] === types2[0];\n  }\n  if (types1[0] === \"*\" || types2[0] === \"*\") {\n    return types1[1] === types2[1];\n  }\n\n  return types1[0] === types2[0] && types1[1] === types2[1];\n}\n\nfunction insertOrUpdateBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n  referenceBlock: Block<BSchema, I, S>,\n  newBlock: PartialBlock<BSchema, I, S>,\n  placement: \"before\" | \"after\" = \"after\",\n) {\n  let insertedBlockId: string | undefined;\n\n  if (\n    Array.isArray(referenceBlock.content) &&\n    referenceBlock.content.length === 0\n  ) {\n    insertedBlockId = editor.updateBlock(referenceBlock, newBlock).id;\n  } else {\n    insertedBlockId = editor.insertBlocks(\n      [newBlock],\n      referenceBlock,\n      placement,\n    )[0].id;\n  }\n\n  return insertedBlockId;\n}\n\nexport async function handleFileInsertion<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(event: DragEvent | ClipboardEvent, editor: BlockNoteEditor<BSchema, I, S>) {\n  if (!editor.uploadFile) {\n    // eslint-disable-next-line no-console\n    console.warn(\n      \"Attempted ot insert file, but uploadFile is not set in the BlockNote editor options\",\n    );\n    return;\n  }\n\n  const dataTransfer =\n    \"dataTransfer\" in event ? event.dataTransfer : event.clipboardData;\n  if (dataTransfer === null) {\n    return;\n  }\n\n  let format: (typeof acceptedMIMETypes)[number] | null = null;\n  for (const mimeType of acceptedMIMETypes) {\n    if (dataTransfer.types.includes(mimeType)) {\n      format = mimeType;\n      break;\n    }\n  }\n  if (format !== \"Files\") {\n    return;\n  }\n\n  const items = dataTransfer.items;\n  if (!items) {\n    return;\n  }\n\n  event.preventDefault();\n\n  for (let i = 0; i < items.length; i++) {\n    // Gets file block corresponding to MIME type.\n    let fileBlockType = \"file\";\n    for (const blockSpec of Object.values(editor.schema.blockSpecs)) {\n      for (const mimeType of blockSpec.implementation.meta?.fileBlockAccept ||\n        []) {\n        const isFileExtension = mimeType.startsWith(\".\");\n        const file = items[i].getAsFile();\n\n        if (file) {\n          if (\n            (!isFileExtension &&\n              file.type &&\n              checkMIMETypesMatch(items[i].type, mimeType)) ||\n            (isFileExtension &&\n              checkFileExtensionsMatch(\n                \".\" + file.name.split(\".\").pop(),\n                mimeType,\n              ))\n          ) {\n            fileBlockType = blockSpec.config.type;\n            break;\n          }\n        }\n      }\n    }\n\n    const file = items[i].getAsFile();\n    if (file) {\n      const fileBlock = {\n        type: fileBlockType,\n        props: {\n          name: file.name,\n        },\n      } as PartialBlock<BSchema, I, S>;\n\n      let insertedBlockId: string | undefined = undefined;\n\n      if (event.type === \"paste\") {\n        const currentBlock = editor.getTextCursorPosition().block;\n        insertedBlockId = insertOrUpdateBlock(editor, currentBlock, fileBlock);\n      } else if (event.type === \"drop\") {\n        const coords = {\n          left: (event as DragEvent).clientX,\n          top: (event as DragEvent).clientY,\n        };\n\n        const pos = editor.prosemirrorView.posAtCoords(coords);\n\n        if (!pos) {\n          return;\n        }\n\n        insertedBlockId = editor.transact((tr) => {\n          const posInfo = getNearestBlockPos(tr.doc, pos.pos);\n          const blockElement = editor.domElement?.querySelector(\n            `[data-id=\"${posInfo.node.attrs.id}\"]`,\n          );\n\n          const blockRect = blockElement?.getBoundingClientRect();\n\n          return insertOrUpdateBlock(\n            editor,\n            editor.getBlock(posInfo.node.attrs.id)!,\n            fileBlock,\n            blockRect && (blockRect.top + blockRect.bottom) / 2 > coords.top\n              ? \"before\"\n              : \"after\",\n          );\n        });\n      } else {\n        return;\n      }\n\n      const updateData = await editor.uploadFile(file, insertedBlockId);\n\n      const updatedFileBlock =\n        typeof updateData === \"string\"\n          ? ({\n              props: {\n                url: updateData,\n              },\n            } as PartialBlock<BSchema, I, S>)\n          : { ...updateData };\n\n      editor.updateBlock(insertedBlockId, updatedFileBlock);\n    }\n  }\n}\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin } from \"prosemirror-state\";\n\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { acceptedMIMETypes } from \"./acceptedMIMETypes.js\";\nimport { handleFileInsertion } from \"./handleFileInsertion.js\";\n\nexport const createDropFileExtension = <\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n) =>\n  Extension.create<{ editor: BlockNoteEditor<BSchema, I, S> }, undefined>({\n    name: \"dropFile\",\n    addProseMirrorPlugins() {\n      return [\n        new Plugin({\n          props: {\n            handleDOMEvents: {\n              drop(_view, event) {\n                if (!editor.isEditable) {\n                  return;\n                }\n\n                let format: (typeof acceptedMIMETypes)[number] | null = null;\n                for (const mimeType of acceptedMIMETypes) {\n                  if (event.dataTransfer!.types.includes(mimeType)) {\n                    format = mimeType;\n                    break;\n                  }\n                }\n                if (format === null) {\n                  return true;\n                }\n\n                if (format === \"Files\") {\n                  handleFileInsertion(event, editor);\n                  return true;\n                }\n\n                return false;\n              },\n            },\n          },\n        }),\n      ];\n    },\n  });\n","// Headings H1-H6.\nconst h1 = /(^|\\n) {0,3}#{1,6} {1,8}[^\\n]{1,64}\\r?\\n\\r?\\n\\s{0,32}\\S/;\n\n// Bold, italic, underline, strikethrough, highlight.\nconst bold =\n  /(_|__|\\*|\\*\\*|~~|==|\\+\\+)(?!\\s)(?:[^\\s](?:.{0,62}[^\\s])?|\\S)(?=\\1)/;\n\n// Basic inline link (also captures images).\nconst link = /\\[[^\\]]{1,128}\\]\\(https?:\\/\\/\\S{1,999}\\)/;\n\n// Inline code.\nconst code = /(?:\\s|^)`(?!\\s)(?:[^\\s`](?:[^`]{0,46}[^\\s`])?|[^\\s`])`([^\\w]|$)/;\n\n// Unordered list.\nconst ul = /(?:^|\\n)\\s{0,5}-\\s{1}[^\\n]+\\n\\s{0,15}-\\s/;\n\n// Ordered list.\nconst ol = /(?:^|\\n)\\s{0,5}\\d+\\.\\s{1}[^\\n]+\\n\\s{0,15}\\d+\\.\\s/;\n\n// Horizontal rule.\nconst hr = /\\n{2} {0,3}-{2,48}\\n{2}/;\n\n// Fenced code block.\nconst fences =\n  /(?:\\n|^)(```|~~~|\\$\\$)(?!`|~)[^\\s]{0,64} {0,64}[^\\n]{0,64}\\n[\\s\\S]{0,9999}?\\s*\\1 {0,64}(?:\\n+|$)/;\n\n// Classical underlined H1 and H2 headings.\nconst title = /(?:\\n|^)(?!\\s)\\w[^\\n]{0,64}\\r?\\n(-|=)\\1{0,64}\\n\\n\\s{0,64}(\\w|$)/;\n\n// Blockquote.\nconst blockquote =\n  /(?:^|(\\r?\\n\\r?\\n))( {0,3}>[^\\n]{1,333}\\n){1,999}($|(\\r?\\n))/;\n\n// Table Header\nconst tableHeader = /^\\s*\\|(.+\\|)+\\s*$/m;\n\n// Table Divider\nconst tableDivider = /^\\s*\\|(\\s*[-:]+[-:]\\s*\\|)+\\s*$/m;\n\n// Table Row\nconst tableRow = /^\\s*\\|(.+\\|)+\\s*$/m;\n\n/**\n * Returns `true` if the source text might be a markdown document.\n *\n * @param src Source text to analyze.\n */\nexport const isMarkdown = (src: string): boolean =>\n  h1.test(src) ||\n  bold.test(src) ||\n  link.test(src) ||\n  code.test(src) ||\n  ul.test(src) ||\n  ol.test(src) ||\n  hr.test(src) ||\n  fences.test(src) ||\n  title.test(src) ||\n  blockquote.test(src) ||\n  tableHeader.test(src) ||\n  tableDivider.test(src) ||\n  tableRow.test(src);\n","import { EditorView } from \"prosemirror-view\";\n\nexport function handleVSCodePaste(event: ClipboardEvent, view: EditorView) {\n  const { schema } = view.state;\n\n  if (!event.clipboardData) {\n    return false;\n  }\n\n  const text = event.clipboardData!.getData(\"text/plain\");\n\n  if (!text) {\n    return false;\n  }\n\n  if (!schema.nodes.codeBlock) {\n    return false;\n  }\n\n  const vscode = event.clipboardData!.getData(\"vscode-editor-data\");\n  const vscodeData = vscode ? JSON.parse(vscode) : undefined;\n  const language = vscodeData?.mode;\n\n  if (!language) {\n    return false;\n  }\n\n  // strip carriage return chars from text pasted as code\n  // see: https://github.com/ProseMirror/prosemirror-view/commit/a50a6bcceb4ce52ac8fcc6162488d8875613aacd\n  view.pasteHTML(\n    `<pre><code class=\"language-${language}\">${text.replace(\n      /\\r\\n?/g,\n      \"\\n\",\n    )}</code></pre>`,\n  );\n\n  return true;\n}\n","import { Extension } from \"@tiptap/core\";\nimport { Plugin } from \"prosemirror-state\";\n\nimport type {\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n} from \"../../../editor/BlockNoteEditor\";\nimport { isMarkdown } from \"../../parsers/markdown/detectMarkdown.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { acceptedMIMETypes } from \"./acceptedMIMETypes.js\";\nimport { handleFileInsertion } from \"./handleFileInsertion.js\";\nimport { handleVSCodePaste } from \"./handleVSCodePaste.js\";\n\nfunction defaultPasteHandler({\n  event,\n  editor,\n  prioritizeMarkdownOverHTML,\n  plainTextAsMarkdown,\n}: {\n  event: ClipboardEvent;\n  editor: BlockNoteEditor<any, any, any>;\n  prioritizeMarkdownOverHTML: boolean;\n  plainTextAsMarkdown: boolean;\n}) {\n  // Special case for code blocks, as they do not support any rich text\n  // formatting, so we force pasting plain text.\n  const isInCodeBlock = editor.transact(\n    (tr) =>\n      tr.selection.$from.parent.type.spec.code &&\n      tr.selection.$to.parent.type.spec.code,\n  );\n\n  if (isInCodeBlock) {\n    const data = event.clipboardData?.getData(\"text/plain\");\n\n    if (data) {\n      editor.pasteText(data);\n\n      return true;\n    }\n  }\n\n  let format: (typeof acceptedMIMETypes)[number] | undefined;\n  for (const mimeType of acceptedMIMETypes) {\n    if (event.clipboardData!.types.includes(mimeType)) {\n      format = mimeType;\n      break;\n    }\n  }\n\n  if (!format) {\n    return true;\n  }\n\n  if (format === \"vscode-editor-data\") {\n    // If VSCode clipboard data cannot be parsed as a code block, try parsing\n    // `text/plain` as a fallback.\n    if (handleVSCodePaste(event, editor.prosemirrorView)) {\n      return true;\n    }\n\n    format = \"text/plain\";\n  }\n\n  if (format === \"Files\") {\n    handleFileInsertion(event, editor);\n    return true;\n  }\n\n  const data = event.clipboardData!.getData(format);\n\n  if (format === \"blocknote/html\") {\n    // Is blocknote/html, so no need to convert it\n    editor.pasteHTML(data, true);\n    return true;\n  }\n\n  if (format === \"text/markdown\") {\n    editor.pasteMarkdown(data);\n    return true;\n  }\n\n  if (prioritizeMarkdownOverHTML) {\n    // Use plain text instead of HTML if it looks like Markdown\n    const plainText = event.clipboardData!.getData(\"text/plain\");\n\n    if (isMarkdown(plainText)) {\n      editor.pasteMarkdown(plainText);\n      return true;\n    }\n  }\n\n  if (format === \"text/html\") {\n    editor.pasteHTML(data);\n    return true;\n  }\n\n  if (plainTextAsMarkdown) {\n    editor.pasteMarkdown(data);\n    return true;\n  }\n\n  editor.pasteText(data);\n  return true;\n}\n\nexport const createPasteFromClipboardExtension = <\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n  pasteHandler: Exclude<\n    BlockNoteEditorOptions<any, any, any>[\"pasteHandler\"],\n    undefined\n  >,\n) =>\n  Extension.create({\n    name: \"pasteFromClipboard\",\n    addProseMirrorPlugins() {\n      return [\n        new Plugin({\n          props: {\n            handleDOMEvents: {\n              paste(_view, event) {\n                event.preventDefault();\n\n                if (!editor.isEditable) {\n                  return;\n                }\n\n                return pasteHandler({\n                  event,\n                  editor,\n                  defaultPasteHandler: ({\n                    prioritizeMarkdownOverHTML = true,\n                    plainTextAsMarkdown = true,\n                  } = {}) => {\n                    return defaultPasteHandler({\n                      event,\n                      editor,\n                      prioritizeMarkdownOverHTML,\n                      plainTextAsMarkdown,\n                    });\n                  },\n                });\n              },\n            },\n          },\n        }),\n      ];\n    },\n  });\n","import { Extension } from \"@tiptap/core\";\nimport { Fragment, Node } from \"prosemirror-model\";\nimport { NodeSelection, Plugin } from \"prosemirror-state\";\nimport { CellSelection } from \"prosemirror-tables\";\nimport type { EditorView } from \"prosemirror-view\";\n\nimport type { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { createExternalHTMLExporter } from \"../../exporters/html/externalHTMLExporter.js\";\nimport { cleanHTMLToMarkdown } from \"../../exporters/markdown/markdownExporter.js\";\nimport { fragmentToBlocks } from \"../../nodeConversions/fragmentToBlocks.js\";\nimport {\n  contentNodeToInlineContent,\n  contentNodeToTableContent,\n} from \"../../nodeConversions/nodeToBlock.js\";\n\nfunction fragmentToExternalHTML<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  view: EditorView,\n  selectedFragment: Fragment,\n  editor: BlockNoteEditor<BSchema, I, S>,\n) {\n  let isWithinBlockContent = false;\n  const isWithinTable = view.state.selection instanceof CellSelection;\n\n  if (!isWithinTable) {\n    // Checks whether block ancestry should be included when creating external\n    // HTML. If the selection is within a block content node, the block ancestry\n    // is excluded as we only care about the inline content.\n    const fragmentWithoutParents = view.state.doc.slice(\n      view.state.selection.from,\n      view.state.selection.to,\n      false,\n    ).content;\n\n    const children = [];\n    for (let i = 0; i < fragmentWithoutParents.childCount; i++) {\n      children.push(fragmentWithoutParents.child(i));\n    }\n\n    isWithinBlockContent =\n      children.find(\n        (child) =>\n          child.type.isInGroup(\"bnBlock\") ||\n          child.type.name === \"blockGroup\" ||\n          child.type.spec.group === \"blockContent\",\n      ) === undefined;\n    if (isWithinBlockContent) {\n      selectedFragment = fragmentWithoutParents;\n    }\n  }\n\n  let externalHTML: string;\n\n  const externalHTMLExporter = createExternalHTMLExporter(\n    view.state.schema,\n    editor,\n  );\n\n  if (isWithinTable) {\n    if (selectedFragment.firstChild?.type.name === \"table\") {\n      // contentNodeToTableContent expects the fragment of the content of a table, not the table node itself\n      // but cellselection.content() returns the table node itself if all cells and columns are selected\n      selectedFragment = selectedFragment.firstChild.content;\n    }\n\n    // first convert selection to blocknote-style table content, and then\n    // pass this to the exporter\n    const ic = contentNodeToTableContent(\n      selectedFragment as any,\n      editor.schema.inlineContentSchema,\n      editor.schema.styleSchema,\n    );\n\n    // Wrap in table to ensure correct parsing by spreadsheet applications\n    externalHTML = `<table>${externalHTMLExporter.exportInlineContent(\n      ic as any,\n      {},\n    )}</table>`;\n  } else if (isWithinBlockContent) {\n    // first convert selection to blocknote-style inline content, and then\n    // pass this to the exporter\n    const ic = contentNodeToInlineContent(\n      selectedFragment as any,\n      editor.schema.inlineContentSchema,\n      editor.schema.styleSchema,\n    );\n    externalHTML = externalHTMLExporter.exportInlineContent(ic, {});\n  } else {\n    const blocks = fragmentToBlocks<BSchema, I, S>(selectedFragment);\n    externalHTML = externalHTMLExporter.exportBlocks(blocks, {});\n  }\n  return externalHTML;\n}\n\nexport function selectedFragmentToHTML<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  view: EditorView,\n  editor: BlockNoteEditor<BSchema, I, S>,\n): {\n  clipboardHTML: string;\n  externalHTML: string;\n  markdown: string;\n} {\n  // Checks if a `blockContent` node is being copied and expands\n  // the selection to the parent `blockContainer` node. This is\n  // for the use-case in which only a block without content is\n  // selected, e.g. an image block.\n  if (\n    \"node\" in view.state.selection &&\n    (view.state.selection.node as Node).type.spec.group === \"blockContent\"\n  ) {\n    editor.transact((tr) =>\n      tr.setSelection(\n        new NodeSelection(tr.doc.resolve(view.state.selection.from - 1)),\n      ),\n    );\n  }\n\n  // Uses default ProseMirror clipboard serialization.\n  const clipboardHTML: string = view.serializeForClipboard(\n    view.state.selection.content(),\n  ).dom.innerHTML;\n\n  const selectedFragment = view.state.selection.content().content;\n\n  const externalHTML = fragmentToExternalHTML<BSchema, I, S>(\n    view,\n    selectedFragment,\n    editor,\n  );\n\n  // Code blocks are treated differently for copying: text/plain is the raw\n  // selected text instead of markdown.\n  const { $from, $to } = view.state.selection;\n  const parentBlockType = $from.parent.type.name;\n  const parentBlockSpec = editor.blockImplementations[parentBlockType as any];\n  const isPurelyInsideCodeBlock =\n    $from.sameParent($to) &&\n    parentBlockSpec?.implementation.meta?.code === true;\n\n  const markdown = isPurelyInsideCodeBlock\n    ? view.state.doc.textBetween($from.pos, $to.pos)\n    : cleanHTMLToMarkdown(externalHTML);\n\n  return { clipboardHTML, externalHTML, markdown };\n}\n\nconst checkIfSelectionInNonEditableBlock = (view: EditorView) => {\n  // Use ProseMirror's internal selection state to check for empty selection.\n  // window.getSelection() returns null or a collapsed selection inside Shadow\n  // DOM (Firefox, Safari, and Chromium edge cases), causing this guard to\n  // misfire and silently skip clipboard writes. view.state.selection is always\n  // accurate regardless of DOM mode.\n  if (view.state.selection.empty) {\n    return true;\n  }\n\n  // Let browser handle event if it's within a non-editable\n  // \"island\". This means it's in selectable content within a\n  // non-editable block. We only need to check one node as it's\n  // not possible for the browser selection to start in an\n  // editable block and end in a non-editable one.\n  const selection = window.getSelection();\n  if (selection && !selection.isCollapsed) {\n    let node = selection.focusNode;\n    while (node) {\n      if (\n        node instanceof HTMLElement &&\n        node.getAttribute(\"contenteditable\") === \"false\"\n      ) {\n        return true;\n      }\n\n      node = node.parentElement;\n    }\n  }\n\n  return false;\n};\n\nconst copyToClipboard = <\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n  view: EditorView,\n  event: ClipboardEvent,\n) => {\n  // Stops the default browser copy behaviour.\n  event.preventDefault();\n  event.clipboardData!.clearData();\n\n  const { clipboardHTML, externalHTML, markdown } = selectedFragmentToHTML(\n    view,\n    editor,\n  );\n\n  // TODO: Writing to other MIME types not working in Safari for\n  //  some reason.\n  event.clipboardData!.setData(\"blocknote/html\", clipboardHTML);\n  event.clipboardData!.setData(\"text/html\", externalHTML);\n  event.clipboardData!.setData(\"text/plain\", markdown);\n};\n\nexport const createCopyToClipboardExtension = <\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, I, S>,\n) =>\n  Extension.create<{ editor: BlockNoteEditor<BSchema, I, S> }, undefined>({\n    name: \"copyToClipboard\",\n    addProseMirrorPlugins() {\n      return [\n        new Plugin({\n          props: {\n            handleDOMEvents: {\n              copy(view, event) {\n                if (checkIfSelectionInNonEditableBlock(view)) {\n                  return true;\n                }\n\n                copyToClipboard(editor, view, event);\n                // Prevent default PM handler to be called\n                return true;\n              },\n              cut(view, event) {\n                if (checkIfSelectionInNonEditableBlock(view)) {\n                  return true;\n                }\n\n                copyToClipboard(editor, view, event);\n                if (view.editable) {\n                  view.dispatch(view.state.tr.deleteSelection());\n                }\n                // Prevent default PM handler to be called\n                return true;\n              },\n              // This is for the use-case in which only a block without content\n              // is selected, e.g. an image block, and dragged (not using the\n              // drag handle).\n              dragstart(view, event) {\n                // Checks if a `NodeSelection` is active.\n                if (!(\"node\" in view.state.selection)) {\n                  return;\n                }\n\n                // Checks if a `blockContent` node is being dragged.\n                if (\n                  (view.state.selection.node as Node).type.spec.group !==\n                  \"blockContent\"\n                ) {\n                  return;\n                }\n\n                // Expands the selection to the parent `blockContainer` node.\n                editor.transact((tr) =>\n                  tr.setSelection(\n                    new NodeSelection(\n                      tr.doc.resolve(view.state.selection.from - 1),\n                    ),\n                  ),\n                );\n\n                // Stops the default browser drag start behaviour.\n                event.preventDefault();\n                event.dataTransfer!.clearData();\n\n                const { clipboardHTML, externalHTML, markdown } =\n                  selectedFragmentToHTML(view, editor);\n\n                // TODO: Writing to other MIME types not working in Safari for\n                //  some reason.\n                event.dataTransfer!.setData(\"blocknote/html\", clipboardHTML);\n                event.dataTransfer!.setData(\"text/html\", externalHTML);\n                event.dataTransfer!.setData(\"text/plain\", markdown);\n\n                // Prevent default PM handler to be called\n                return true;\n              },\n            },\n          },\n        }),\n      ];\n    },\n  });\n","import { Extension } from \"@tiptap/core\";\nimport { getBackgroundColorAttribute } from \"../../../blocks/defaultProps.js\";\n\nexport const BackgroundColorExtension = Extension.create({\n  name: \"blockBackgroundColor\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"tableCell\", \"tableHeader\"],\n        attributes: {\n          backgroundColor: getBackgroundColorAttribute(),\n        },\n      },\n    ];\n  },\n});\n","// Stripped down version of the TipTap HardBreak extension:\n// https://github.com/ueberdosis/tiptap/blob/f3258d9ee5fb7979102fe63434f6ea4120507311/packages/extension-hard-break/src/hard-break.ts#L80\n// Changes:\n// - Removed options\n// - Removed keyboard shortcuts & moved them to the `KeyboardShortcutsExtension`\n// - Removed `setHardBreak` command (added a simpler version in the Shift+Enter\n// handler in `KeyboardShortcutsExtension`).\n// - Added priority\nimport { mergeAttributes, Node } from \"@tiptap/core\";\n\nexport const HardBreak = Node.create({\n  name: \"hardBreak\",\n\n  inline: true,\n\n  group: \"inline\",\n\n  selectable: false,\n\n  linebreakReplacement: true,\n\n  priority: 10,\n\n  parseHTML() {\n    return [{ tag: \"br\" }];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    return [\"br\", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];\n  },\n\n  renderText() {\n    return \"\\n\";\n  },\n});\n","import { Node } from \"prosemirror-model\";\nimport { EditorState } from \"prosemirror-state\";\n\nimport {\n  BlockInfo,\n  getBlockInfoFromResolvedPos,\n} from \"../../../getBlockInfoFromPos.js\";\n\n/**\n * Returns the block info from the parent block\n * or undefined if we're at the root\n */\nexport const getParentBlockInfo = (\n  doc: Node,\n  beforePos: number,\n): BlockInfo | undefined => {\n  const $pos = doc.resolve(beforePos);\n  const depth = $pos.depth - 1;\n\n  if (depth < 1) {\n    return undefined;\n  }\n\n  const parentBeforePos = $pos.before(depth);\n  const parentNode = doc.resolve(parentBeforePos).nodeAfter;\n\n  if (!parentNode) {\n    return undefined;\n  }\n\n  if (!parentNode.type.spec.group?.includes(\"bnBlock\")) {\n    return getParentBlockInfo(doc, parentBeforePos);\n  }\n\n  const parentBlockInfo = getBlockInfoFromResolvedPos(\n    doc.resolve(parentBeforePos),\n  );\n\n  return parentBlockInfo;\n};\n\n/**\n * Returns the block info from the sibling block before (above) the given block,\n * or undefined if the given block is the first sibling.\n */\nexport const getPrevBlockInfo = (doc: Node, beforePos: number) => {\n  const $pos = doc.resolve(beforePos);\n\n  const indexInParent = $pos.index();\n\n  if (indexInParent === 0) {\n    return undefined;\n  }\n\n  const prevBlockBeforePos = $pos.posAtIndex(indexInParent - 1);\n\n  const prevBlockInfo = getBlockInfoFromResolvedPos(\n    doc.resolve(prevBlockBeforePos),\n  );\n  return prevBlockInfo;\n};\n\n/**\n * Returns the block info from the sibling block after (below) the given block,\n * or undefined if the given block is the last sibling.\n */\nexport const getNextBlockInfo = (doc: Node, beforePos: number) => {\n  const $pos = doc.resolve(beforePos);\n\n  const indexInParent = $pos.index();\n\n  if (indexInParent === $pos.node().childCount - 1) {\n    return undefined;\n  }\n\n  const nextBlockBeforePos = $pos.posAtIndex(indexInParent + 1);\n\n  const nextBlockInfo = getBlockInfoFromResolvedPos(\n    doc.resolve(nextBlockBeforePos),\n  );\n  return nextBlockInfo;\n};\n\n/**\n * If a block has children like this:\n * A\n * - B\n * - C\n * -- D\n *\n * Then the bottom nested block returned is D.\n */\nexport const getBottomNestedBlockInfo = (doc: Node, blockInfo: BlockInfo) => {\n  while (blockInfo.childContainer) {\n    const group = blockInfo.childContainer.node;\n\n    const newPos = doc\n      .resolve(blockInfo.childContainer.beforePos + 1)\n      .posAtIndex(group.childCount - 1);\n    blockInfo = getBlockInfoFromResolvedPos(doc.resolve(newPos));\n  }\n\n  return blockInfo;\n};\n\nconst canMerge = (prevBlockInfo: BlockInfo, nextBlockInfo: BlockInfo) => {\n  return (\n    prevBlockInfo.isBlockContainer &&\n    prevBlockInfo.blockContent.node.type.spec.content === \"inline*\" &&\n    prevBlockInfo.blockContent.node.childCount > 0 &&\n    nextBlockInfo.isBlockContainer &&\n    nextBlockInfo.blockContent.node.type.spec.content === \"inline*\"\n  );\n};\n\nconst mergeBlocks = (\n  state: EditorState,\n  dispatch: ((args?: any) => any) | undefined,\n  prevBlockInfo: BlockInfo,\n  nextBlockInfo: BlockInfo,\n) => {\n  // Un-nests all children of the next block.\n  if (!nextBlockInfo.isBlockContainer) {\n    throw new Error(\n      `Attempted to merge block at position ${nextBlockInfo.bnBlock.beforePos} into previous block at position ${prevBlockInfo.bnBlock.beforePos}, but next block is not a block container`,\n    );\n  }\n\n  // Removes a level of nesting all children of the next block by 1 level, if it contains both content and block\n  // group nodes.\n  if (nextBlockInfo.childContainer) {\n    const childBlocksStart = state.doc.resolve(\n      nextBlockInfo.childContainer.beforePos + 1,\n    );\n    const childBlocksEnd = state.doc.resolve(\n      nextBlockInfo.childContainer.afterPos - 1,\n    );\n    const childBlocksRange = childBlocksStart.blockRange(childBlocksEnd);\n\n    if (dispatch) {\n      const pos = state.doc.resolve(nextBlockInfo.bnBlock.beforePos);\n      state.tr.lift(childBlocksRange!, pos.depth);\n    }\n  }\n\n  // Deletes the boundary between the two blocks. Can be thought of as\n  // removing the closing tags of the first block and the opening tags of the\n  // second one to stitch them together.\n  if (dispatch) {\n    if (!prevBlockInfo.isBlockContainer) {\n      throw new Error(\n        `Attempted to merge block at position ${nextBlockInfo.bnBlock.beforePos} into previous block at position ${prevBlockInfo.bnBlock.beforePos}, but previous block is not a block container`,\n      );\n    }\n\n    // TODO: test merging between a columnList and paragraph, between two columnLists, and v.v.\n    dispatch(\n      state.tr.delete(\n        prevBlockInfo.blockContent.afterPos - 1,\n        nextBlockInfo.blockContent.beforePos + 1,\n      ),\n    );\n  }\n\n  return true;\n};\n\nexport const mergeBlocksCommand =\n  (posBetweenBlocks: number) =>\n  ({\n    state,\n    dispatch,\n  }: {\n    state: EditorState;\n    dispatch: ((args?: any) => any) | undefined;\n  }) => {\n    const $pos = state.doc.resolve(posBetweenBlocks);\n    const nextBlockInfo = getBlockInfoFromResolvedPos($pos);\n\n    const prevBlockInfo = getPrevBlockInfo(\n      state.doc,\n      nextBlockInfo.bnBlock.beforePos,\n    );\n\n    if (!prevBlockInfo) {\n      return false;\n    }\n\n    const bottomNestedBlockInfo = getBottomNestedBlockInfo(\n      state.doc,\n      prevBlockInfo,\n    );\n\n    if (!canMerge(bottomNestedBlockInfo, nextBlockInfo)) {\n      return false;\n    }\n\n    return mergeBlocks(state, dispatch, bottomNestedBlockInfo, nextBlockInfo);\n  };\n","import { Extension } from \"@tiptap/core\";\nimport { Fragment, Node } from \"prosemirror-model\";\nimport { TextSelection } from \"prosemirror-state\";\n\nimport {\n  getBottomNestedBlockInfo,\n  getNextBlockInfo,\n  getParentBlockInfo,\n  getPrevBlockInfo,\n  mergeBlocksCommand,\n} from \"../../../api/blockManipulation/commands/mergeBlocks/mergeBlocks.js\";\nimport {\n  liftItem,\n  nestBlock,\n  unnestBlock,\n} from \"../../../api/blockManipulation/commands/nestBlock/nestBlock.js\";\nimport { fixColumnList } from \"../../../api/blockManipulation/commands/replaceBlocks/util/fixColumnList.js\";\nimport { splitBlockCommand } from \"../../../api/blockManipulation/commands/splitBlock/splitBlock.js\";\nimport { updateBlockCommand } from \"../../../api/blockManipulation/commands/updateBlock/updateBlock.js\";\nimport {\n  getBlockInfoFromResolvedPos,\n  getBlockInfoFromSelection,\n} from \"../../../api/getBlockInfoFromPos.js\";\nimport { BlockNoteEditor } from \"../../../editor/BlockNoteEditor.js\";\nimport { FormattingToolbarExtension } from \"../../FormattingToolbar/FormattingToolbar.js\";\nimport { FilePanelExtension } from \"../../FilePanel/FilePanel.js\";\n\nexport const KeyboardShortcutsExtension = Extension.create<{\n  editor: BlockNoteEditor<any, any, any>;\n  tabBehavior: \"prefer-navigate-ui\" | \"prefer-indent\";\n}>({\n  priority: 50,\n\n  // TODO: The shortcuts need a refactor. Do we want to use a command priority\n  //  design as there is now, or clump the logic into a single function?\n  addKeyboardShortcuts() {\n    // handleBackspace is partially adapted from https://github.com/ueberdosis/tiptap/blob/ed56337470efb4fd277128ab7ef792b37cfae992/packages/core/src/extensions/keymap.ts\n    const handleBackspace = () =>\n      this.editor.commands.first(({ chain, commands }) => [\n        // Deletes the selection if it's not empty.\n        () => commands.deleteSelection(),\n        // Undoes an input rule if one was triggered in the last editor state change.\n        () => commands.undoInputRule(),\n        // Reverts block content type to a paragraph if the selection is at the start of the block.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockStart =\n              state.selection.from === blockInfo.blockContent.beforePos + 1;\n            const isParagraph =\n              blockInfo.blockContent.node.type.name === \"paragraph\";\n\n            if (selectionAtBlockStart && !isParagraph) {\n              return commands.command(\n                updateBlockCommand(blockInfo.bnBlock.beforePos, {\n                  type: \"paragraph\",\n                  props: {},\n                }),\n              );\n            }\n\n            return false;\n          }),\n        // Removes a level of nesting if the block is indented if the selection is at the start of the block.\n        () =>\n          commands.command(({ state, tr }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { blockContent } = blockInfo;\n\n            const selectionAtBlockStart =\n              state.selection.from === blockContent.beforePos + 1;\n\n            if (selectionAtBlockStart) {\n              return liftItem(\n                tr,\n                tr.doc.type.schema.nodes[\"blockContainer\"],\n                tr.doc.type.schema.nodes[\"blockGroup\"],\n              );\n            }\n\n            return false;\n          }),\n        // Merges block with the previous one if it isn't indented, and the selection is at the start of the\n        // block. The target block for merging must contain inline content.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { bnBlock: blockContainer, blockContent } = blockInfo;\n\n            const prevBlockInfo = getPrevBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n            // If the previous block has no inline content, it can't be merged.\n            // It's instead deleted, which is done later in the chan, so we\n            // return early here.\n            if (\n              !prevBlockInfo ||\n              !prevBlockInfo.isBlockContainer ||\n              prevBlockInfo.blockContent.node.type.spec.content !== \"inline*\"\n            ) {\n              return false;\n            }\n\n            const selectionAtBlockStart =\n              state.selection.from === blockContent.beforePos + 1;\n            const selectionEmpty = state.selection.empty;\n\n            const posBetweenBlocks = blockContainer.beforePos;\n\n            if (selectionAtBlockStart && selectionEmpty) {\n              return chain()\n                .command(mergeBlocksCommand(posBetweenBlocks))\n                .scrollIntoView()\n                .run();\n            }\n\n            return false;\n          }),\n        // If the previous block is a columnList, moves the current block to\n        // the end of the last column in it.\n        () =>\n          commands.command(({ state, tr, dispatch }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockStart =\n              state.selection.from ===\n              blockInfo.blockContent.beforePos + 1;\n            if (!selectionAtBlockStart) {\n              return false;\n            }\n\n            const prevBlockInfo = getPrevBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n            if (!prevBlockInfo || prevBlockInfo.isBlockContainer) {\n              return false;\n            }\n\n            if (dispatch) {\n              const columnAfterPos = prevBlockInfo.bnBlock.afterPos - 1;\n              const $blockAfterPos = tr.doc.resolve(columnAfterPos - 1);\n\n              tr.delete(\n                blockInfo.bnBlock.beforePos,\n                blockInfo.bnBlock.afterPos,\n              );\n              tr.insert($blockAfterPos.pos, blockInfo.bnBlock.node);\n              tr.setSelection(\n                TextSelection.near(tr.doc.resolve($blockAfterPos.pos + 1)),\n              );\n\n              return true;\n            }\n\n            return false;\n          }),\n        // If the block is the first in a column, moves it to the end of the\n        // previous column. If there is no previous column, moves it above the\n        // columnList.\n        () =>\n          commands.command(({ state, tr, dispatch }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockStart =\n              tr.selection.from === blockInfo.blockContent.beforePos + 1;\n            if (!selectionAtBlockStart) {\n              return false;\n            }\n\n            const $pos = tr.doc.resolve(blockInfo.bnBlock.beforePos);\n\n            const prevBlock = $pos.nodeBefore;\n            if (prevBlock) {\n              return false;\n            }\n\n            const parentBlock = $pos.node();\n            if (parentBlock.type.name !== \"column\") {\n              return false;\n            }\n\n            const $blockPos = tr.doc.resolve(blockInfo.bnBlock.beforePos);\n            const $columnPos = tr.doc.resolve($blockPos.before());\n            const columnListPos = $columnPos.before();\n\n            if (dispatch) {\n              tr.delete(\n                blockInfo.bnBlock.beforePos,\n                blockInfo.bnBlock.afterPos,\n              );\n              fixColumnList(tr, columnListPos);\n\n              if ($columnPos.pos === columnListPos + 1) {\n                tr.insert(columnListPos, blockInfo.bnBlock.node);\n                tr.setSelection(\n                  TextSelection.near(tr.doc.resolve(columnListPos)),\n                );\n              } else {\n                tr.insert($columnPos.pos - 1, blockInfo.bnBlock.node);\n                tr.setSelection(\n                  TextSelection.near(tr.doc.resolve($columnPos.pos)),\n                );\n              }\n            }\n\n            return true;\n          }),\n        // Deletes the current block if it's an empty block with inline content,\n        // and moves the selection to the previous block.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const blockEmpty =\n              blockInfo.blockContent.node.childCount === 0 &&\n              blockInfo.blockContent.node.type.spec.content === \"inline*\";\n\n            if (blockEmpty) {\n              const prevBlockInfo = getPrevBlockInfo(\n                state.doc,\n                blockInfo.bnBlock.beforePos,\n              );\n              if (!prevBlockInfo) {\n                return false;\n              }\n              const bottomNestedPrevBlockInfo = getBottomNestedBlockInfo(\n                state.doc,\n                prevBlockInfo,\n              );\n              if (!bottomNestedPrevBlockInfo.isBlockContainer) {\n                return false;\n              }\n              if (\n                !bottomNestedPrevBlockInfo ||\n                !bottomNestedPrevBlockInfo.isBlockContainer\n              ) {\n                return false;\n              }\n\n              let chainedCommands = chain();\n\n              // Moves the children the current block.\n              if (blockInfo.childContainer) {\n                chainedCommands.insertContentAt(\n                  blockInfo.bnBlock.afterPos,\n                  blockInfo.childContainer?.node.content,\n                );\n              }\n\n              if (\n                bottomNestedPrevBlockInfo.blockContent.node.type.spec\n                  .content === \"tableRow+\"\n              ) {\n                const tableBlockEndPos = blockInfo.bnBlock.beforePos - 1;\n                const tableBlockContentEndPos = tableBlockEndPos - 1;\n                const lastRowEndPos = tableBlockContentEndPos - 1;\n                const lastCellEndPos = lastRowEndPos - 1;\n                const lastCellParagraphEndPos = lastCellEndPos - 1;\n\n                chainedCommands = chainedCommands.setTextSelection(\n                  lastCellParagraphEndPos,\n                );\n              } else if (\n                bottomNestedPrevBlockInfo.blockContent.node.type.spec\n                  .content === \"\"\n              ) {\n                chainedCommands = chainedCommands.setNodeSelection(\n                  bottomNestedPrevBlockInfo.blockContent.beforePos,\n                );\n              } else {\n                const blockContentEndPos =\n                  bottomNestedPrevBlockInfo.blockContent.afterPos - 1;\n\n                chainedCommands =\n                  chainedCommands.setTextSelection(blockContentEndPos);\n              }\n\n              return chainedCommands\n                .deleteRange({\n                  from: blockInfo.bnBlock.beforePos,\n                  to: blockInfo.bnBlock.afterPos,\n                })\n                .scrollIntoView()\n                .run();\n            }\n\n            return false;\n          }),\n        // Deletes previous block if it contains no content and isn't a table,\n        // when the selection is empty and at the start of the block. Moves the\n        // current block into the deleted block's place.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockStart =\n              state.selection.from === blockInfo.blockContent.beforePos + 1;\n            const selectionEmpty = state.selection.empty;\n\n            const prevBlockInfo = getPrevBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n\n            if (prevBlockInfo && selectionAtBlockStart && selectionEmpty) {\n              const bottomBlock = getBottomNestedBlockInfo(\n                state.doc,\n                prevBlockInfo,\n              );\n\n              if (!bottomBlock.isBlockContainer) {\n                return false;\n              }\n\n              const prevBlockNotTableAndNoContent =\n                bottomBlock.blockContent.node.type.spec.content === \"\" ||\n                (bottomBlock.blockContent.node.type.spec.content ===\n                  \"inline*\" &&\n                  bottomBlock.blockContent.node.childCount === 0);\n\n              if (prevBlockNotTableAndNoContent) {\n                return chain()\n                  .cut(\n                    {\n                      from: blockInfo.bnBlock.beforePos,\n                      to: blockInfo.bnBlock.afterPos,\n                    },\n                    bottomBlock.bnBlock.afterPos,\n                  )\n                  .deleteRange({\n                    from: bottomBlock.bnBlock.beforePos,\n                    to: bottomBlock.bnBlock.afterPos,\n                  })\n                  .run();\n              }\n            }\n\n            return false;\n          }),\n      ]);\n\n    const handleDelete = () =>\n      this.editor.commands.first(({ chain, commands }) => [\n        // Deletes the selection if it's not empty.\n        () => commands.deleteSelection(),\n        // Deletes the first child block and un-nests its children, if the\n        // selection is empty and at the end of the current block. If both the\n        // parent and child blocks have inline content, the child block's\n        // content is appended to the parent's. The child block's own children\n        // are unindented before it's deleted.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer || !blockInfo.childContainer) {\n              return false;\n            }\n            const { blockContent, childContainer } = blockInfo;\n\n            const selectionAtBlockEnd =\n              state.selection.from === blockContent.afterPos - 1;\n            const selectionEmpty = state.selection.empty;\n\n            const firstChildBlockInfo = getBlockInfoFromResolvedPos(\n              state.doc.resolve(childContainer.beforePos + 1),\n            );\n            if (!firstChildBlockInfo.isBlockContainer) {\n              return false;\n            }\n\n            if (selectionAtBlockEnd && selectionEmpty) {\n              const firstChildBlockContent =\n                firstChildBlockInfo.blockContent.node;\n              const firstChildBlockHasInlineContent =\n                firstChildBlockContent.type.spec.content === \"inline*\";\n              const blockHasInlineContent =\n                blockContent.node.type.spec.content === \"inline*\";\n\n              return (\n                chain()\n                  // Un-nests child block's children if necessary.\n                  .insertContentAt(\n                    firstChildBlockInfo.bnBlock.afterPos,\n                    firstChildBlockInfo.childContainer?.node.content ||\n                      Fragment.empty,\n                  )\n                  .deleteRange(\n                    // Deletes whole child container if there's only one child.\n                    childContainer.node.childCount === 1\n                      ? {\n                          from: childContainer.beforePos,\n                          to: childContainer.afterPos,\n                        }\n                      : {\n                          from: firstChildBlockInfo.bnBlock.beforePos,\n                          to: firstChildBlockInfo.bnBlock.afterPos,\n                        },\n                  )\n                  // Appends inline content from child block if possible.\n                  .insertContentAt(\n                    state.selection.from,\n                    firstChildBlockHasInlineContent && blockHasInlineContent\n                      ? firstChildBlockContent.content\n                      : null,\n                  )\n                  .setTextSelection(state.selection.from)\n                  .scrollIntoView()\n                  .run()\n              );\n            }\n\n            return false;\n          }),\n        // Merges block with the next one (at the same nesting level or lower),\n        // if one exists, the block has no children, and the selection is at the\n        // end of the block.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { bnBlock: blockContainer, blockContent } = blockInfo;\n\n            const nextBlockInfo = getNextBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n            if (!nextBlockInfo || !nextBlockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockEnd =\n              state.selection.from === blockContent.afterPos - 1;\n            const selectionEmpty = state.selection.empty;\n\n            const posBetweenBlocks = blockContainer.afterPos;\n\n            if (selectionAtBlockEnd && selectionEmpty) {\n              return chain()\n                .command(mergeBlocksCommand(posBetweenBlocks))\n                .scrollIntoView()\n                .run();\n            }\n\n            return false;\n          }),\n        // If the next block is a columnList, moves the first block from its\n        // first column to after the current block.\n        () =>\n          commands.command(({ state, tr, dispatch }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockEnd =\n              state.selection.from ===\n              blockInfo.blockContent.afterPos - 1;\n            if (!selectionAtBlockEnd) {\n              return false;\n            }\n\n            const nextBlockInfo = getNextBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n            if (!nextBlockInfo || nextBlockInfo.isBlockContainer) {\n              return false;\n            }\n\n            if (dispatch) {\n              const columnBeforePos = nextBlockInfo.bnBlock.beforePos + 1;\n              const $blockBeforePos = tr.doc.resolve(columnBeforePos + 1);\n\n              tr.delete(\n                $blockBeforePos.pos,\n                $blockBeforePos.pos + $blockBeforePos.nodeAfter!.nodeSize,\n              );\n              fixColumnList(tr, nextBlockInfo.bnBlock.beforePos);\n              tr.insert(blockInfo.bnBlock.afterPos, $blockBeforePos.nodeAfter!);\n              tr.setSelection(\n                TextSelection.near(tr.doc.resolve($blockBeforePos.pos)),\n              );\n\n              return true;\n            }\n\n            return false;\n          }),\n        // If the block is the last in a column, moves it to the start of the\n        // next column. If there is no next column, moves it below the\n        // columnList.\n        () =>\n          commands.command(({ state, tr, dispatch }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockEnd =\n              tr.selection.from === blockInfo.blockContent.afterPos - 1;\n            if (!selectionAtBlockEnd) {\n              return false;\n            }\n\n            const $pos = tr.doc.resolve(blockInfo.bnBlock.afterPos);\n\n            const nextBlock = $pos.nodeAfter;\n            if (nextBlock) {\n              return false;\n            }\n\n            const parentBlock = $pos.node();\n            if (parentBlock.type.name !== \"column\") {\n              return false;\n            }\n\n            const $blockEndPos = tr.doc.resolve(blockInfo.bnBlock.afterPos);\n            const $columnEndPos = tr.doc.resolve($blockEndPos.after());\n            const columnListEndPos = $columnEndPos.after();\n\n            if (dispatch) {\n              // Position before first block in next column, or first block\n              // after columnList if there is no next column.\n              const nextBlockBeforePos =\n                $columnEndPos.pos === columnListEndPos - 1\n                  ? columnListEndPos\n                  : $columnEndPos.pos + 1;\n              const nextBlockInfo = getBlockInfoFromResolvedPos(\n                tr.doc.resolve(nextBlockBeforePos),\n              );\n\n              tr.delete(\n                nextBlockInfo.bnBlock.beforePos,\n                nextBlockInfo.bnBlock.afterPos,\n              );\n              fixColumnList(\n                tr,\n                columnListEndPos - $columnEndPos.node().nodeSize,\n              );\n              tr.insert($blockEndPos.pos, nextBlockInfo.bnBlock.node);\n              tr.setSelection(\n                TextSelection.near(tr.doc.resolve(nextBlockBeforePos)),\n              );\n            }\n\n            return true;\n          }),\n        // Deletes the next block at either the same or lower nesting level, if\n        // the selection is empty and at the end of the block. If both the\n        // current and next blocks have inline content, the next block's\n        // content is appended to the current block's. The next block's own\n        // children are unindented before it's deleted.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { blockContent } = blockInfo;\n\n            const selectionAtBlockEnd =\n              state.selection.from === blockContent.afterPos - 1;\n            const selectionEmpty = state.selection.empty;\n\n            if (selectionAtBlockEnd && selectionEmpty) {\n              const getNextBlockInfoAtAnyLevel = (\n                doc: Node,\n                beforePos: number,\n              ) => {\n                const nextBlockInfo = getNextBlockInfo(doc, beforePos);\n                if (nextBlockInfo) {\n                  return nextBlockInfo;\n                }\n\n                const parentBlockInfo = getParentBlockInfo(doc, beforePos);\n                if (!parentBlockInfo) {\n                  return undefined;\n                }\n\n                return getNextBlockInfoAtAnyLevel(\n                  doc,\n                  parentBlockInfo.bnBlock.beforePos,\n                );\n              };\n\n              const nextBlockInfo = getNextBlockInfoAtAnyLevel(\n                state.doc,\n                blockInfo.bnBlock.beforePos,\n              );\n              if (!nextBlockInfo || !nextBlockInfo.isBlockContainer) {\n                return false;\n              }\n\n              const nextBlockContent = nextBlockInfo.blockContent.node;\n              const nextBlockHasInlineContent =\n                nextBlockContent.type.spec.content === \"inline*\";\n              const blockHasInlineContent =\n                blockContent.node.type.spec.content === \"inline*\";\n\n              return (\n                chain()\n                  // Un-nests next block's children if necessary.\n                  .insertContentAt(\n                    nextBlockInfo.bnBlock.afterPos,\n                    nextBlockInfo.childContainer?.node.content ||\n                      Fragment.empty,\n                  )\n                  .deleteRange({\n                    from: nextBlockInfo.bnBlock.beforePos,\n                    to: nextBlockInfo.bnBlock.afterPos,\n                  })\n                  // Appends inline content from child block if possible.\n                  .insertContentAt(\n                    state.selection.from,\n                    nextBlockHasInlineContent && blockHasInlineContent\n                      ? nextBlockContent.content\n                      : null,\n                  )\n                  .setTextSelection(state.selection.from)\n                  .scrollIntoView()\n                  .run()\n              );\n            }\n\n            return false;\n          }),\n        // Deletes the current block if it's an empty block with inline content,\n        // and moves the selection to the next block.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const blockEmpty =\n              blockInfo.blockContent.node.childCount === 0 &&\n              blockInfo.blockContent.node.type.spec.content === \"inline*\";\n\n            if (blockEmpty) {\n              const nextBlockInfo = getNextBlockInfo(\n                state.doc,\n                blockInfo.bnBlock.beforePos,\n              );\n              if (!nextBlockInfo || !nextBlockInfo.isBlockContainer) {\n                return false;\n              }\n\n              let chainedCommands = chain();\n\n              if (\n                nextBlockInfo.blockContent.node.type.spec.content ===\n                \"tableRow+\"\n              ) {\n                const tableBlockStartPos = blockInfo.bnBlock.afterPos + 1;\n                const tableBlockContentStartPos = tableBlockStartPos + 1;\n                const firstRowStartPos = tableBlockContentStartPos + 1;\n                const firstCellStartPos = firstRowStartPos + 1;\n                const firstCellParagraphStartPos = firstCellStartPos + 1;\n\n                chainedCommands = chainedCommands.setTextSelection(\n                  firstCellParagraphStartPos,\n                );\n              } else if (\n                nextBlockInfo.blockContent.node.type.spec.content === \"\"\n              ) {\n                chainedCommands = chainedCommands.setNodeSelection(\n                  nextBlockInfo.blockContent.beforePos,\n                );\n              } else {\n                chainedCommands = chainedCommands.setTextSelection(\n                  nextBlockInfo.blockContent.beforePos + 1,\n                );\n              }\n\n              return chainedCommands\n                .deleteRange({\n                  from: blockInfo.bnBlock.beforePos,\n                  to: blockInfo.bnBlock.afterPos,\n                })\n                .scrollIntoView()\n                .run();\n            }\n\n            return false;\n          }),\n        // Deletes next block if it contains no content and isn't a table,\n        // when the selection is empty and at the end of the block. Moves the\n        // current block into the deleted block's place.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n\n            const selectionAtBlockEnd =\n              state.selection.from === blockInfo.blockContent.afterPos - 1;\n            const selectionEmpty = state.selection.empty;\n\n            const nextBlockInfo = getNextBlockInfo(\n              state.doc,\n              blockInfo.bnBlock.beforePos,\n            );\n            if (!nextBlockInfo) {\n              return false;\n            }\n            if (!nextBlockInfo.isBlockContainer) {\n              return false;\n            }\n\n            if (nextBlockInfo && selectionAtBlockEnd && selectionEmpty) {\n              const nextBlockNotTableAndNoContent =\n                nextBlockInfo.blockContent.node.type.spec.content === \"\" ||\n                (nextBlockInfo.blockContent.node.type.spec.content ===\n                  \"inline*\" &&\n                  nextBlockInfo.blockContent.node.childCount === 0);\n\n              if (nextBlockNotTableAndNoContent) {\n                const childBlocks =\n                  nextBlockInfo.bnBlock.node.lastChild!.content;\n                return chain()\n                  .deleteRange({\n                    from: nextBlockInfo.bnBlock.beforePos,\n                    to: nextBlockInfo.bnBlock.afterPos,\n                  })\n                  .insertContentAt(\n                    blockInfo.bnBlock.afterPos,\n                    nextBlockInfo.bnBlock.node.childCount === 2\n                      ? childBlocks\n                      : null,\n                  )\n                  .run();\n              }\n            }\n\n            return false;\n          }),\n      ]);\n\n    const handleEnter = (withShift = false) => {\n      return this.editor.commands.first(({ commands, tr }) => [\n        // Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start\n        // of the block.\n        () =>\n          commands.command(({ state, tr }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { bnBlock: blockContainer, blockContent } = blockInfo;\n\n            const { depth } = state.doc.resolve(blockContainer.beforePos);\n\n            const selectionAtBlockStart =\n              state.selection.$anchor.parentOffset === 0;\n            const selectionEmpty =\n              state.selection.anchor === state.selection.head;\n            const blockEmpty = blockContent.node.childCount === 0;\n            const blockIndented = depth > 1;\n\n            if (\n              selectionAtBlockStart &&\n              selectionEmpty &&\n              blockEmpty &&\n              blockIndented\n            ) {\n              return liftItem(\n                tr,\n                tr.doc.type.schema.nodes[\"blockContainer\"],\n                tr.doc.type.schema.nodes[\"blockGroup\"],\n              );\n            }\n\n            return false;\n          }),\n        // Creates a hard break if block is configured to do so.\n        () =>\n          commands.command(({ state }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n\n            const blockHardBreakShortcut =\n              this.options.editor.schema.blockSchema[\n                blockInfo.blockNoteType as keyof typeof this.options.editor.schema.blockSchema\n              ].meta?.hardBreakShortcut ?? \"shift+enter\";\n\n            if (blockHardBreakShortcut === \"none\") {\n              return false;\n            }\n\n            if (\n              // If shortcut is not configured, or is configured as \"shift+enter\",\n              // create a hard break for shift+enter, but not for enter.\n              (blockHardBreakShortcut === \"shift+enter\" && withShift) ||\n              // If shortcut is configured as \"enter\", create a hard break for\n              // both enter and shift+enter.\n              blockHardBreakShortcut === \"enter\"\n            ) {\n              const marks =\n                tr.storedMarks ||\n                tr.selection.$head\n                  .marks()\n                  .filter((m) =>\n                    this.editor.extensionManager.splittableMarks.includes(\n                      m.type.name,\n                    ),\n                  );\n\n              tr.insert(\n                tr.selection.head,\n                tr.doc.type.schema.nodes.hardBreak.create(),\n              ).ensureMarks(marks);\n              return true;\n            }\n\n            return false;\n          }),\n        // Creates a new block and moves the selection to it if the current one is empty, while the selection is also\n        // empty & at the start of the block.\n        () =>\n          commands.command(({ state, dispatch, tr }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { bnBlock: blockContainer, blockContent } = blockInfo;\n\n            const selectionAtBlockStart =\n              state.selection.$anchor.parentOffset === 0;\n            const selectionEmpty =\n              state.selection.anchor === state.selection.head;\n            const blockEmpty = blockContent.node.childCount === 0;\n\n            if (selectionAtBlockStart && selectionEmpty && blockEmpty) {\n              const newBlockInsertionPos = blockContainer.afterPos;\n              const newBlockContentPos = newBlockInsertionPos + 2;\n\n              if (dispatch) {\n                // Creates a new block with the children of the current block,\n                // if it has any.\n                const newBlock = state.schema.nodes[\n                  \"blockContainer\"\n                ].createAndFill(\n                  undefined,\n                  [\n                    state.schema.nodes[\"paragraph\"].createAndFill() ||\n                      undefined,\n                    blockInfo.childContainer?.node,\n                  ].filter((node) => node !== undefined),\n                )!;\n\n                // Inserts the new block and moves the selection to it.\n                tr.insert(newBlockInsertionPos, newBlock)\n                  .setSelection(\n                    new TextSelection(tr.doc.resolve(newBlockContentPos)),\n                  )\n                  .scrollIntoView();\n\n                // Deletes old block's children, as they have been moved to\n                // the new one.\n                if (blockInfo.childContainer) {\n                  tr.delete(\n                    blockInfo.childContainer.beforePos,\n                    blockInfo.childContainer.afterPos,\n                  );\n                }\n              }\n\n              return true;\n            }\n\n            return false;\n          }),\n        // Splits the current block, moving content inside that's after the cursor to a new text block below. Also\n        // deletes the selection beforehand, if it's not empty.\n        () =>\n          commands.command(({ state, chain }) => {\n            const blockInfo = getBlockInfoFromSelection(state);\n            if (!blockInfo.isBlockContainer) {\n              return false;\n            }\n            const { blockContent } = blockInfo;\n\n            const selectionAtBlockStart =\n              state.selection.$anchor.parentOffset === 0;\n            const blockEmpty = blockContent.node.childCount === 0;\n\n            if (!blockEmpty) {\n              chain()\n                .deleteSelection()\n                .command(\n                  splitBlockCommand(\n                    state.selection.from,\n                    selectionAtBlockStart,\n                    selectionAtBlockStart,\n                  ),\n                )\n                .run();\n\n              return true;\n            }\n\n            return false;\n          }),\n      ]);\n    };\n\n    return {\n      Backspace: handleBackspace,\n      Delete: handleDelete,\n      Enter: () => handleEnter(),\n      \"Shift-Enter\": () => handleEnter(true),\n      // Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the\n      // editor since the browser will try to use tab for keyboard navigation.\n      Tab: () => {\n        if (\n          this.options.tabBehavior !== \"prefer-indent\" &&\n          (this.options.editor.getExtension(FormattingToolbarExtension)?.store\n            .state ||\n            this.options.editor.getExtension(FilePanelExtension)?.store\n              .state !== undefined)\n          // TODO need to check if the link toolbar is open or another alternative entirely\n        ) {\n          // don't handle tabs if a toolbar is shown, so we can tab into / out of it\n          return false;\n        }\n        return nestBlock(this.options.editor);\n      },\n      \"Shift-Tab\": () => {\n        if (\n          this.options.tabBehavior !== \"prefer-indent\" &&\n          (this.options.editor.getExtension(FormattingToolbarExtension)?.store\n            .state ||\n            this.options.editor.getExtension(FilePanelExtension)?.store\n              .state !== undefined)\n          // TODO need to check if the link toolbar is open or another alternative entirely\n          // other menu types?\n        ) {\n          // don't handle tabs if a toolbar is shown, so we can tab into / out of it\n          return false;\n        }\n        return unnestBlock(this.options.editor);\n      },\n      \"Shift-Mod-ArrowUp\": () => {\n        this.options.editor.moveBlocksUp();\n        return true;\n      },\n      \"Shift-Mod-ArrowDown\": () => {\n        this.options.editor.moveBlocksDown();\n        return true;\n      },\n      \"Mod-z\": () => this.options.editor.undo(),\n      \"Mod-y\": () => this.options.editor.redo(),\n      \"Shift-Mod-z\": () => this.options.editor.redo(),\n    };\n  },\n});\n","import { Mark } from \"@tiptap/core\";\nimport { MarkSpec } from \"prosemirror-model\";\n\n// This copies the marks from @handlewithcare/prosemirror-suggest-changes,\n// but uses the Tiptap Mark API instead so we can use them in BlockNote\n\n// The ideal solution would be to not depend on tiptap nodes / marks, but be able to use prosemirror nodes / marks directly\n// this way we could directly use the exported marks from @handlewithcare/prosemirror-suggest-changes\nexport const SuggestionAddMark = Mark.create({\n  name: \"insertion\",\n  inclusive: false,\n  excludes: \"deletion modification insertion\",\n  addAttributes() {\n    return {\n      id: { default: null, validate: \"number\" }, // note: validate is supported in prosemirror but not in tiptap, so this doesn't actually work (considered not critical)\n    };\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== \"insertion\") {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n      inclusive: false,\n\n      toDOM(mark, inline) {\n        return [\n          \"ins\",\n          {\n            \"data-id\": String(mark.attrs[\"id\"]),\n            \"data-inline\": String(inline),\n            ...(!inline && { style: \"display: contents\" }), // changed to \"contents\" to make this work for table rows\n          },\n          0,\n        ];\n      },\n      parseDOM: [\n        {\n          tag: \"ins\",\n          getAttrs(node) {\n            if (!node.dataset[\"id\"]) {\n              return false;\n            }\n            return {\n              id: parseInt(node.dataset[\"id\"], 10),\n            };\n          },\n        },\n      ],\n    } satisfies MarkSpec;\n  },\n});\n\nexport const SuggestionDeleteMark = Mark.create({\n  name: \"deletion\",\n  inclusive: false,\n  excludes: \"insertion modification deletion\",\n  addAttributes() {\n    return {\n      id: { default: null, validate: \"number\" }, // note: validate is supported in prosemirror but not in tiptap\n    };\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== \"deletion\") {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n      inclusive: false,\n\n      // attrs: {\n      //   id: { validate: \"number\" },\n      // },\n      toDOM(mark, inline) {\n        return [\n          \"del\",\n          {\n            \"data-id\": String(mark.attrs[\"id\"]),\n            \"data-inline\": String(inline),\n            ...(!inline && { style: \"display: contents\" }), // changed to \"contents\" to make this work for table rows\n          },\n          0,\n        ];\n      },\n      parseDOM: [\n        {\n          tag: \"del\",\n          getAttrs(node) {\n            if (!node.dataset[\"id\"]) {\n              return false;\n            }\n            return {\n              id: parseInt(node.dataset[\"id\"], 10),\n            };\n          },\n        },\n      ],\n    } satisfies MarkSpec;\n  },\n});\n\nexport const SuggestionModificationMark = Mark.create({\n  name: \"modification\",\n  inclusive: false,\n  excludes: \"deletion insertion\",\n  addAttributes() {\n    // note: validate is supported in prosemirror but not in tiptap\n    return {\n      id: { default: null, validate: \"number\" },\n      type: { validate: \"string\" },\n      attrName: { default: null, validate: \"string|null\" },\n      previousValue: { default: null },\n      newValue: { default: null },\n    };\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== \"modification\") {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n      inclusive: false,\n      // attrs: {\n      //   id: { validate: \"number\" },\n      //   type: { validate: \"string\" },\n      //   attrName: { default: null, validate: \"string|null\" },\n      //   previousValue: { default: null },\n      //   newValue: { default: null },\n      // },\n      toDOM(mark, inline) {\n        return [\n          inline ? \"span\" : \"div\",\n          {\n            \"data-type\": \"modification\",\n            \"data-id\": String(mark.attrs[\"id\"]),\n            \"data-mod-type\": mark.attrs[\"type\"] as string,\n            \"data-mod-prev-val\": JSON.stringify(mark.attrs[\"previousValue\"]),\n            // TODO: Try to serialize marks with toJSON?\n            \"data-mod-new-val\": JSON.stringify(mark.attrs[\"newValue\"]),\n          },\n          0,\n        ];\n      },\n      parseDOM: [\n        {\n          tag: \"span[data-type='modification']\",\n          getAttrs(node) {\n            if (!node.dataset[\"id\"]) {\n              return false;\n            }\n            return {\n              id: parseInt(node.dataset[\"id\"], 10),\n              type: node.dataset[\"modType\"],\n              previousValue: node.dataset[\"modPrevVal\"],\n              newValue: node.dataset[\"modNewVal\"],\n            };\n          },\n        },\n        {\n          tag: \"div[data-type='modification']\",\n          getAttrs(node) {\n            if (!node.dataset[\"id\"]) {\n              return false;\n            }\n            return {\n              id: parseInt(node.dataset[\"id\"], 10),\n              type: node.dataset[\"modType\"],\n              previousValue: node.dataset[\"modPrevVal\"],\n            };\n          },\n        },\n      ],\n    } satisfies MarkSpec;\n  },\n});\n","import { Extension } from \"@tiptap/core\";\n\nexport const TextAlignmentExtension = Extension.create({\n  name: \"textAlignment\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        // Generally text alignment is handled through props using the custom\n        // blocks API. Tables are the only blocks that are created as TipTap\n        // nodes and ported to blocks, so we need to add text alignment in a\n        // separate extension.\n        types: [\"tableCell\", \"tableHeader\"],\n        attributes: {\n          textAlignment: {\n            default: \"left\",\n            parseHTML: (element) => {\n              return element.getAttribute(\"data-text-alignment\");\n            },\n            renderHTML: (attributes) => {\n              if (attributes.textAlignment === \"left\") {\n                return {};\n              }\n              return {\n                \"data-text-alignment\": attributes.textAlignment,\n              };\n            },\n          },\n        },\n      },\n    ];\n  },\n});\n","import { Extension } from \"@tiptap/core\";\nimport { getTextColorAttribute } from \"../../../blocks/defaultProps.js\";\n\nexport const TextColorExtension = Extension.create({\n  name: \"blockTextColor\",\n\n  addGlobalAttributes() {\n    return [\n      {\n        types: [\"table\", \"tableCell\", \"tableHeader\"],\n        attributes: {\n          textColor: getTextColorAttribute(),\n        },\n      },\n    ];\n  },\n});\n","import { Node } from \"@tiptap/core\";\n\nimport type { BlockNoteEditor } from \"../editor/BlockNoteEditor.js\";\nimport { BlockNoteDOMAttributes } from \"../schema/index.js\";\nimport { mergeCSSClasses } from \"../util/browser.js\";\n\n// Object containing all possible block attributes.\nconst BlockAttributes: Record<string, string> = {\n  blockColor: \"data-block-color\",\n  blockStyle: \"data-block-style\",\n  id: \"data-id\",\n  depth: \"data-depth\",\n  depthChange: \"data-depth-change\",\n};\n\n/**\n * The main \"Block node\" documents consist of\n */\nexport const BlockContainer = Node.create<{\n  domAttributes?: BlockNoteDOMAttributes;\n  editor: BlockNoteEditor<any, any, any>;\n}>({\n  name: \"blockContainer\",\n  group: \"blockGroupChild bnBlock\",\n  // A block always contains content, and optionally a blockGroup which contains nested blocks\n  content: \"blockContent blockGroup?\",\n  // Ensures content-specific keyboard handlers trigger first.\n  priority: 50,\n  defining: true,\n  marks: \"insertion modification deletion\",\n  parseHTML() {\n    return [\n      {\n        tag: \"div[data-node-type=\" + this.name + \"]\",\n        getAttrs: (element) => {\n          if (typeof element === \"string\") {\n            return false;\n          }\n\n          const attrs: Record<string, string> = {};\n          for (const [nodeAttr, HTMLAttr] of Object.entries(BlockAttributes)) {\n            if (element.getAttribute(HTMLAttr)) {\n              attrs[nodeAttr] = element.getAttribute(HTMLAttr)!;\n            }\n          }\n\n          return attrs;\n        },\n      },\n      // Ignore `blockOuter` divs, but parse the `blockContainer` divs inside them.\n      {\n        tag: `div[data-node-type=\"blockOuter\"]`,\n        skip: true,\n      },\n    ];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    const blockOuter = document.createElement(\"div\");\n    blockOuter.className = \"bn-block-outer\";\n    blockOuter.setAttribute(\"data-node-type\", \"blockOuter\");\n    for (const [attribute, value] of Object.entries(HTMLAttributes)) {\n      if (attribute !== \"class\") {\n        blockOuter.setAttribute(attribute, value);\n      }\n    }\n\n    const blockHTMLAttributes = {\n      ...(this.options.domAttributes?.block || {}),\n      ...HTMLAttributes,\n    };\n    const block = document.createElement(\"div\");\n    block.className = mergeCSSClasses(\"bn-block\", blockHTMLAttributes.class);\n    block.setAttribute(\"data-node-type\", this.name);\n    for (const [attribute, value] of Object.entries(blockHTMLAttributes)) {\n      if (attribute !== \"class\") {\n        block.setAttribute(attribute, value);\n      }\n    }\n\n    blockOuter.appendChild(block);\n\n    return {\n      dom: blockOuter,\n      contentDOM: block,\n    };\n  },\n});\n","import { Node } from \"@tiptap/core\";\nimport { BlockNoteDOMAttributes } from \"../schema/index.js\";\nimport { mergeCSSClasses } from \"../util/browser.js\";\n\nexport const BlockGroup = Node.create<{\n  domAttributes?: BlockNoteDOMAttributes;\n}>({\n  name: \"blockGroup\",\n  group: \"childContainer\",\n  content: \"blockGroupChild+\",\n  marks: \"deletion insertion modification\",\n  parseHTML() {\n    return [\n      {\n        tag: \"div\",\n        getAttrs: (element) => {\n          if (typeof element === \"string\") {\n            return false;\n          }\n\n          if (element.getAttribute(\"data-node-type\") === \"blockGroup\") {\n            // Null means the element matches, but we don't want to add any attributes to the node.\n            return null;\n          }\n\n          return false;\n        },\n      },\n    ];\n  },\n\n  renderHTML({ HTMLAttributes }) {\n    const blockGroupHTMLAttributes = {\n      ...(this.options.domAttributes?.blockGroup || {}),\n      ...HTMLAttributes,\n    };\n    const blockGroup = document.createElement(\"div\");\n    blockGroup.className = mergeCSSClasses(\n      \"bn-block-group\",\n      blockGroupHTMLAttributes.class,\n    );\n    blockGroup.setAttribute(\"data-node-type\", \"blockGroup\");\n    for (const [attribute, value] of Object.entries(blockGroupHTMLAttributes)) {\n      if (attribute !== \"class\") {\n        blockGroup.setAttribute(attribute, value);\n      }\n    }\n\n    return {\n      dom: blockGroup,\n      contentDOM: blockGroup,\n    };\n  },\n});\n","import { Node } from \"@tiptap/core\";\n\nexport const Doc = Node.create({\n  name: \"doc\",\n  topNode: true,\n  content: \"blockGroup\",\n  marks: \"insertion modification deletion\",\n});\n","import type * as Y from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { ForkYDocExtension } from \"./ForkYDoc.js\";\nimport { SchemaMigration } from \"./schemaMigration/SchemaMigration.js\";\nimport { YCursorExtension } from \"./YCursorPlugin.js\";\nimport { YSyncExtension } from \"./YSync.js\";\nimport { YUndoExtension } from \"./YUndo.js\";\n\nexport type CollaborationOptions = {\n  /**\n   * The Yjs XML fragment that's used for collaboration.\n   */\n  fragment: Y.XmlFragment;\n  /**\n   * The user info for the current user that's shown to other collaborators.\n   */\n  user: {\n    name: string;\n    color: string;\n  };\n  /**\n   * A Yjs provider (used for awareness / cursor information)\n   */\n  provider?: { awareness?: Awareness };\n  /**\n   * Optional function to customize how cursors of users are rendered\n   */\n  renderCursor?: (user: any) => HTMLElement;\n  /**\n   * Optional flag to set when the user label should be shown with the default\n   * collaboration cursor. Setting to \"always\" will always show the label,\n   * while \"activity\" will only show the label when the user moves the cursor\n   * or types. Defaults to \"activity\".\n   */\n  showCursorLabels?: \"always\" | \"activity\";\n};\n\nexport const CollaborationExtension = createExtension(\n  ({ options }: ExtensionOptions<CollaborationOptions>) => {\n    return {\n      key: \"collaboration\",\n      blockNoteExtensions: [\n        ForkYDocExtension(options),\n        YCursorExtension(options),\n        YSyncExtension(options),\n        YUndoExtension(),\n        SchemaMigration(options),\n      ],\n    } as const;\n  },\n);\n","import {\n  AnyExtension as AnyTiptapExtension,\n  extensions,\n  Node,\n  Extension as TiptapExtension,\n} from \"@tiptap/core\";\nimport { Gapcursor } from \"@tiptap/extensions/gap-cursor\";\nimport { LinkExtension } from \"../../../extensions/tiptap-extensions/Link/link.js\";\nimport { Text } from \"@tiptap/extension-text\";\nimport { createDropFileExtension } from \"../../../api/clipboard/fromClipboard/fileDropExtension.js\";\nimport { createPasteFromClipboardExtension } from \"../../../api/clipboard/fromClipboard/pasteExtension.js\";\nimport { createCopyToClipboardExtension } from \"../../../api/clipboard/toClipboard/copyExtension.js\";\nimport {\n  BlockChangeExtension,\n  DropCursorExtension,\n  FilePanelExtension,\n  FormattingToolbarExtension,\n  HistoryExtension,\n  LinkToolbarExtension,\n  NodeSelectionKeyboardExtension,\n  PlaceholderExtension,\n  PreviousBlockTypeExtension,\n  ShowSelectionExtension,\n  SideMenuExtension,\n  SuggestionMenu,\n  TableHandlesExtension,\n  TrailingNodeExtension,\n} from \"../../../extensions/index.js\";\nimport {\n  BackgroundColorExtension,\n  HardBreak,\n  KeyboardShortcutsExtension,\n  SuggestionAddMark,\n  SuggestionDeleteMark,\n  SuggestionModificationMark,\n  TextAlignmentExtension,\n  TextColorExtension,\n  UniqueID,\n} from \"../../../extensions/tiptap-extensions/index.js\";\nimport { BlockContainer, BlockGroup, Doc } from \"../../../pm-nodes/index.js\";\nimport {\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n} from \"../../BlockNoteEditor.js\";\nimport { ExtensionFactoryInstance } from \"../../BlockNoteExtension.js\";\nimport { CollaborationExtension } from \"../../../extensions/Collaboration/Collaboration.js\";\n\n/**\n * Get all the Tiptap extensions BlockNote is configured with by default\n */\nexport function getDefaultTiptapExtensions(\n  editor: BlockNoteEditor<any, any, any>,\n  options: BlockNoteEditorOptions<any, any, any>,\n) {\n  const tiptapExtensions: AnyTiptapExtension[] = [\n    extensions.ClipboardTextSerializer,\n    extensions.Commands,\n    extensions.Editable,\n    extensions.FocusEvents,\n    extensions.Tabindex,\n    Gapcursor,\n\n    UniqueID.configure({\n      // everything from bnBlock group (nodes that represent a BlockNote block should have an id)\n      types: [\"blockContainer\", \"columnList\", \"column\"],\n      setIdAttribute: options.setIdAttribute,\n      isWithinEditor: editor.isWithinEditor,\n    }),\n    HardBreak,\n    Text,\n\n    // marks:\n    SuggestionAddMark,\n    SuggestionDeleteMark,\n    SuggestionModificationMark,\n    ...(Object.values(editor.schema.styleSpecs).map((styleSpec) => {\n      return styleSpec.implementation.mark.configure({\n        editor: editor,\n      });\n    }) as any[]),\n\n    TextColorExtension,\n\n    BackgroundColorExtension,\n    TextAlignmentExtension,\n\n    // make sure escape blurs editor, so that we can tab to other elements in the host page (accessibility)\n    TiptapExtension.create({\n      name: \"OverrideEscape\",\n      addKeyboardShortcuts: () => {\n        return {\n          Escape: () => {\n            if (editor.getExtension(SuggestionMenu)?.shown()) {\n              // escape should close the suggestion menu, but not blur the editor\n              return false;\n            }\n            editor.blur();\n            return true;\n          },\n        };\n      },\n    }),\n\n    // nodes\n    Doc,\n    BlockContainer.configure({\n      editor: editor,\n      domAttributes: options.domAttributes,\n    }),\n    KeyboardShortcutsExtension.configure({\n      editor: editor,\n      tabBehavior: options.tabBehavior,\n    }),\n    BlockGroup.configure({\n      domAttributes: options.domAttributes,\n    }),\n    ...Object.values(editor.schema.inlineContentSpecs)\n      .filter((a) => a.config !== \"link\" && a.config !== \"text\")\n      .map((inlineContentSpec) => {\n        return inlineContentSpec.implementation!.node.configure({\n          editor: editor,\n        });\n      }),\n\n    ...Object.values(editor.schema.blockSpecs).flatMap((blockSpec) => {\n      return [\n        // the node extension implementations\n        ...(\"node\" in blockSpec.implementation\n          ? [\n              (blockSpec.implementation.node as Node).configure({\n                editor: editor,\n                domAttributes: options.domAttributes,\n              }),\n            ]\n          : []),\n      ];\n    }),\n    createCopyToClipboardExtension(editor),\n    createPasteFromClipboardExtension(\n      editor,\n      options.pasteHandler ||\n        ((context: {\n          defaultPasteHandler: (context?: {\n            prioritizeMarkdownOverHTML?: boolean;\n            plainTextAsMarkdown?: boolean;\n          }) => boolean | undefined;\n        }) => context.defaultPasteHandler()),\n    ),\n    createDropFileExtension(editor),\n  ];\n\n  return tiptapExtensions;\n}\n\nexport function getDefaultExtensions(\n  editor: BlockNoteEditor<any, any, any>,\n  options: BlockNoteEditorOptions<any, any, any>,\n) {\n  const extensions = [\n    BlockChangeExtension(),\n    DropCursorExtension(options),\n    FilePanelExtension(options),\n    FormattingToolbarExtension(options),\n    LinkExtension({\n      HTMLAttributes: options.links?.HTMLAttributes ?? {},\n      onClick: options.links?.onClick,\n      ...(options.links?.isValidLink\n        ? { isValidLink: options.links.isValidLink }\n        : {}),\n    }),\n    LinkToolbarExtension(options),\n    NodeSelectionKeyboardExtension(),\n    PlaceholderExtension(options),\n    ShowSelectionExtension(options),\n    SideMenuExtension(options),\n    SuggestionMenu(options),\n    ...(options.trailingBlock !== false ? [TrailingNodeExtension()] : []),\n  ] as ExtensionFactoryInstance[];\n\n  if (options.collaboration) {\n    extensions.push(CollaborationExtension(options.collaboration));\n  } else {\n    // YUndo is not compatible with ProseMirror's history plugin\n    extensions.push(HistoryExtension());\n  }\n\n  if (\"table\" in editor.schema.blockSpecs) {\n    extensions.push(TableHandlesExtension(options));\n  }\n\n  if (options.animations !== false) {\n    extensions.push(PreviousBlockTypeExtension());\n  }\n\n  return extensions;\n}\n","import {\n  InputRule,\n  inputRules as inputRulesPlugin,\n} from \"@handlewithcare/prosemirror-inputrules\";\nimport {\n  AnyExtension as AnyTiptapExtension,\n  Extension as TiptapExtension,\n} from \"@tiptap/core\";\nimport { keymap } from \"@tiptap/pm/keymap\";\nimport { Plugin, TextSelection } from \"prosemirror-state\";\nimport { updateBlockTr } from \"../../../api/blockManipulation/commands/updateBlock/updateBlock.js\";\nimport { setTextCursorPosition } from \"../../../api/blockManipulation/selections/textCursorPosition.js\";\nimport { getBlockInfoFromTransaction } from \"../../../api/getBlockInfoFromPos.js\";\nimport { sortByDependencies } from \"../../../util/topo-sort.js\";\nimport type {\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n} from \"../../BlockNoteEditor.js\";\nimport type {\n  Extension,\n  ExtensionFactoryInstance,\n  ExtensionFactory,\n} from \"../../BlockNoteExtension.js\";\nimport { originalFactorySymbol } from \"./symbol.js\";\nimport {\n  getDefaultExtensions,\n  getDefaultTiptapExtensions,\n} from \"./extensions.js\";\n\nexport class ExtensionManager {\n  /**\n   * A set of extension keys which are disabled by the options\n   */\n  private disabledExtensions = new Set<string>();\n  /**\n   * A list of all the extensions that are registered to the editor\n   */\n  private extensions: Extension[] = [];\n  /**\n   * A map of all the abort controllers for each extension that has an init method defined\n   */\n  private abortMap = new Map<Extension, AbortController>();\n  /**\n   * A map of all the extension factories that are registered to the editor\n   */\n  private extensionFactories = new Map<ExtensionFactory, Extension>();\n  /**\n   * Because a single blocknote extension can both have it's own prosemirror plugins & additional generated ones (e.g. keymap & input rules plugins)\n   * We need to keep track of all the plugins for each extension, so that we can remove them when the extension is unregistered\n   */\n  private extensionPlugins: Map<Extension, Plugin[]> = new Map();\n\n  constructor(\n    private editor: BlockNoteEditor<any, any, any>,\n    private options: BlockNoteEditorOptions<any, any, any>,\n  ) {\n    /**\n     * When the editor is first mounted, we need to initialize all the extensions\n     */\n    editor.onMount(() => {\n      for (const extension of this.extensions) {\n        // If the extension has an init function, we can initialize it, otherwise, it is already added to the editor\n        if (extension.mount) {\n          // We create an abort controller for each extension, so that we can abort the extension when the editor is unmounted\n          const abortController = new window.AbortController();\n          const unmountCallback = extension.mount({\n            dom: editor.prosemirrorView.dom,\n            root: editor.prosemirrorView.root,\n            signal: abortController.signal,\n          });\n          // If the extension returns a method to unmount it, we can register it to be called when the abort controller is aborted\n          if (unmountCallback) {\n            abortController.signal.addEventListener(\"abort\", () => {\n              unmountCallback();\n            });\n          }\n          // Keep track of the abort controller for each extension, so that we can abort it when the editor is unmounted\n          this.abortMap.set(extension, abortController);\n        }\n      }\n    });\n\n    /**\n     * When the editor is unmounted, we need to abort all the extensions' abort controllers\n     */\n    editor.onUnmount(() => {\n      for (const [extension, abortController] of this.abortMap.entries()) {\n        // No longer track the abort controller for this extension\n        this.abortMap.delete(extension);\n        // Abort each extension's abort controller\n        abortController.abort();\n      }\n    });\n\n    // TODO do disabled extensions need to be only for editor base extensions? Or all of them?\n    this.disabledExtensions = new Set(options.disableExtensions || []);\n\n    // Add the default extensions\n    for (const extension of getDefaultExtensions(this.editor, this.options)) {\n      this.addExtension(extension);\n    }\n\n    // Add the extensions from the options\n    for (const extension of this.options.extensions ?? []) {\n      this.addExtension(extension);\n    }\n\n    // Add the extensions from blocks specs\n    for (const block of Object.values(this.editor.schema.blockSpecs)) {\n      for (const extension of block.extensions ?? []) {\n        this.addExtension(extension);\n      }\n    }\n  }\n\n  /**\n   * Register one or more extensions to the editor after the editor is initialized.\n   *\n   * This allows users to switch on & off extensions \"at runtime\".\n   */\n  public registerExtension(\n    extension:\n      | Extension\n      | ExtensionFactoryInstance\n      | (Extension | ExtensionFactoryInstance)[],\n  ): void {\n    const extensions = ([] as (Extension | ExtensionFactoryInstance)[])\n      .concat(extension)\n      .filter(Boolean) as (Extension | ExtensionFactoryInstance)[];\n\n    if (!extensions.length) {\n      // eslint-disable-next-line no-console\n      console.warn(`No extensions found to register`, extension);\n      return;\n    }\n\n    const registeredExtensions = extensions\n      .map((extension) => this.addExtension(extension))\n      .filter(Boolean) as Extension[];\n\n    const pluginsToAdd = new Set<Plugin>();\n    for (const extension of registeredExtensions) {\n      if (extension?.tiptapExtensions) {\n        // This is necessary because this can only switch out prosemirror plugins at runtime,\n        // it can't switch out Tiptap extensions since that can have more widespread effects (since a Tiptap extension can even add/remove to the schema).\n\n        // eslint-disable-next-line no-console\n        console.warn(\n          `Extension ${extension.key} has tiptap extensions, but these cannot be changed after initializing the editor. Please separate the extension into multiple extensions if you want to add them, or re-initialize the editor.`,\n          extension,\n        );\n      }\n\n      if (extension?.inputRules?.length) {\n        // This is necessary because input rules are defined in a single prosemirror plugin which cannot be re-initialized.\n        // eslint-disable-next-line no-console\n        console.warn(\n          `Extension ${extension.key} has input rules, but these cannot be changed after initializing the editor. Please separate the extension into multiple extensions if you want to add them, or re-initialize the editor.`,\n          extension,\n        );\n      }\n\n      this.getProsemirrorPluginsFromExtension(extension).plugins.forEach(\n        (plugin) => {\n          pluginsToAdd.add(plugin);\n        },\n      );\n    }\n\n    // TODO there isn't a great way to do sorting right now. This is something that should be improved in the future.\n    // So, we just append to the end of the list for now.\n    this.updatePlugins((plugins) => [...plugins, ...pluginsToAdd]);\n  }\n\n  /**\n   * Register an extension to the editor\n   * @param extension - The extension to register\n   * @returns The extension instance\n   */\n  private addExtension(\n    extension: Extension | ExtensionFactoryInstance,\n  ): Extension | undefined {\n    let instance: Extension;\n    if (typeof extension === \"function\") {\n      instance = extension({ editor: this.editor });\n    } else {\n      instance = extension;\n    }\n\n    if (!instance || this.disabledExtensions.has(instance.key)) {\n      return undefined as any;\n    }\n\n    // Now that we know that the extension is not disabled, we can add it to the extension factories\n    if (typeof extension === \"function\") {\n      const originalFactory = (instance as any)[originalFactorySymbol] as (\n        ...args: any[]\n      ) => ExtensionFactoryInstance;\n\n      if (typeof originalFactory === \"function\") {\n        this.extensionFactories.set(originalFactory, instance);\n      }\n    }\n\n    this.extensions.push(instance);\n\n    if (instance.blockNoteExtensions) {\n      for (const extension of instance.blockNoteExtensions) {\n        this.addExtension(extension);\n      }\n    }\n\n    return instance as any;\n  }\n\n  /**\n   * Resolve an extension or a list of extensions into a list of extension instances\n   * @param toResolve - The extension or list of extensions to resolve\n   * @returns A list of extension instances\n   */\n  private resolveExtensions(\n    toResolve:\n      | undefined\n      | string\n      | Extension\n      | ExtensionFactory\n      | (Extension | ExtensionFactory | string | undefined)[],\n  ): Extension[] {\n    const extensions = [] as Extension[];\n    if (typeof toResolve === \"function\") {\n      const instance = this.extensionFactories.get(toResolve);\n      if (instance) {\n        extensions.push(instance);\n      }\n    } else if (Array.isArray(toResolve)) {\n      for (const extension of toResolve) {\n        extensions.push(...this.resolveExtensions(extension));\n      }\n    } else if (typeof toResolve === \"object\" && \"key\" in toResolve) {\n      extensions.push(toResolve);\n    } else if (typeof toResolve === \"string\") {\n      const instance = this.extensions.find((e) => e.key === toResolve);\n      if (instance) {\n        extensions.push(instance);\n      }\n    }\n    return extensions;\n  }\n\n  /**\n   * Unregister an extension from the editor\n   * @param toUnregister - The extension to unregister\n   * @returns void\n   */\n  public unregisterExtension(\n    toUnregister:\n      | undefined\n      | string\n      | Extension\n      | ExtensionFactory\n      | (Extension | ExtensionFactory | string | undefined)[],\n  ): void {\n    const extensions = this.resolveExtensions(toUnregister);\n\n    if (!extensions.length) {\n      // eslint-disable-next-line no-console\n      console.warn(`No extensions found to unregister`, toUnregister);\n      return;\n    }\n    let didWarn = false;\n\n    const pluginsToRemove = new Set<Plugin>();\n    for (const extension of extensions) {\n      this.extensions = this.extensions.filter((e) => e !== extension);\n      this.extensionFactories.forEach((instance, factory) => {\n        if (instance === extension) {\n          this.extensionFactories.delete(factory);\n        }\n      });\n      this.abortMap.get(extension)?.abort();\n      this.abortMap.delete(extension);\n\n      const plugins = this.extensionPlugins.get(extension);\n      plugins?.forEach((plugin) => {\n        pluginsToRemove.add(plugin);\n      });\n      this.extensionPlugins.delete(extension);\n\n      if (extension.tiptapExtensions && !didWarn) {\n        didWarn = true;\n        // eslint-disable-next-line no-console\n        console.warn(\n          `Extension ${extension.key} has tiptap extensions, but they will not be removed. Please separate the extension into multiple extensions if you want to remove them, or re-initialize the editor.`,\n          toUnregister,\n        );\n      }\n    }\n\n    this.updatePlugins((plugins) =>\n      plugins.filter((plugin) => !pluginsToRemove.has(plugin)),\n    );\n  }\n\n  /**\n   * Allows resetting the current prosemirror state's plugins\n   * @param update - A function that takes the current plugins and returns the new plugins\n   * @returns void\n   */\n  private updatePlugins(update: (plugins: Plugin[]) => Plugin[]): void {\n    const currentState = this.editor.prosemirrorState;\n\n    const state = currentState.reconfigure({\n      plugins: update(currentState.plugins.slice()),\n    });\n\n    this.editor.prosemirrorView.updateState(state);\n  }\n\n  /**\n   * Get all the extensions that are registered to the editor\n   */\n  public getTiptapExtensions(): AnyTiptapExtension[] {\n    // Start with the default tiptap extensions\n    const tiptapExtensions = getDefaultTiptapExtensions(\n      this.editor,\n      this.options,\n    ).filter((extension) => !this.disabledExtensions.has(extension.name));\n\n    const getPriority = sortByDependencies(this.extensions);\n\n    const inputRulesByPriority = new Map<number, InputRule[]>();\n    for (const extension of this.extensions) {\n      if (extension.tiptapExtensions) {\n        tiptapExtensions.push(...extension.tiptapExtensions);\n      }\n\n      const priority = getPriority(extension.key);\n\n      const { plugins: prosemirrorPlugins, inputRules } =\n        this.getProsemirrorPluginsFromExtension(extension);\n      // Sometimes a blocknote extension might need to make additional prosemirror plugins, so we generate them here\n      if (prosemirrorPlugins.length) {\n        tiptapExtensions.push(\n          TiptapExtension.create({\n            name: extension.key,\n            priority,\n            addProseMirrorPlugins: () => prosemirrorPlugins,\n          }),\n        );\n      }\n      if (inputRules.length) {\n        if (!inputRulesByPriority.has(priority)) {\n          inputRulesByPriority.set(priority, []);\n        }\n        inputRulesByPriority.get(priority)!.push(...inputRules);\n      }\n    }\n\n    // Collect all input rules into 1 extension to reduce conflicts\n    tiptapExtensions.push(\n      TiptapExtension.create({\n        name: \"blocknote-input-rules\",\n        addProseMirrorPlugins() {\n          const rules = [] as InputRule[];\n          Array.from(inputRulesByPriority.keys())\n            // We sort the rules by their priority (the key)\n            .sort()\n            .reverse()\n            .forEach((priority) => {\n              // Append in reverse priority order\n              rules.push(...inputRulesByPriority.get(priority)!);\n            });\n          const inputRules = inputRulesPlugin({ rules });\n          // Sidecar plugin: triggers the same input rules on Enter by\n          // delegating to the inputRules plugin's handleTextInput with a\n          // synthetic \"\\n\" insertion. The handlewithcare regex `\\s$` already\n          // matches `\\n`, so any rule that fires on space fires on Enter too.\n          // We call its handleTextInput directly (rather than via\n          // view.someProp) so other plugins don't observe the synthetic input,\n          // and so the rule's undo metadata is keyed to the same plugin\n          // instance that Tiptap's `commands.undoInputRule` reads from.\n          const inputRulesEnter = new Plugin({\n            props: {\n              handleKeyDown(view, event) {\n                if (event.key !== \"Enter\") {\n                  return false;\n                }\n                // Only trigger on plain Enter — modifier combos like\n                // Shift/Cmd/Ctrl/Alt+Enter are reserved for other handlers\n                // (e.g. soft-break, submit) and should fall through.\n                if (\n                  event.shiftKey ||\n                  event.ctrlKey ||\n                  event.metaKey ||\n                  event.altKey\n                ) {\n                  return false;\n                }\n                const { $cursor } = view.state.selection as TextSelection;\n                if (!$cursor) {\n                  return false;\n                }\n                return !!inputRules.props.handleTextInput?.call(\n                  inputRules,\n                  view,\n                  $cursor.pos,\n                  $cursor.pos,\n                  \"\\n\",\n                  () =>\n                    view.state.tr.insertText(\"\\n\", $cursor.pos, $cursor.pos),\n                );\n              },\n            },\n          });\n          return [inputRules, inputRulesEnter];\n        },\n      }),\n    );\n\n    // Add any tiptap extensions from the `_tiptapOptions`\n    for (const extension of this.options._tiptapOptions?.extensions ?? []) {\n      tiptapExtensions.push(extension);\n    }\n\n    return tiptapExtensions;\n  }\n\n  /**\n   * This maps a blocknote extension into an array of Prosemirror plugins if it has any of the following:\n   * - plugins\n   * - keyboard shortcuts\n   * - input rules\n   */\n  private getProsemirrorPluginsFromExtension(extension: Extension): {\n    plugins: Plugin[];\n    inputRules: InputRule[];\n  } {\n    const plugins: Plugin[] = [...(extension.prosemirrorPlugins ?? [])];\n    const inputRules: InputRule[] = [];\n    if (\n      !extension.prosemirrorPlugins?.length &&\n      !Object.keys(extension.keyboardShortcuts || {}).length &&\n      !extension.inputRules?.length\n    ) {\n      // We can bail out early if the extension has no features to add to the tiptap editor\n      return { plugins, inputRules };\n    }\n\n    this.extensionPlugins.set(extension, plugins);\n\n    if (extension.inputRules?.length) {\n      inputRules.push(\n        ...extension.inputRules.map((inputRule) => {\n          return new InputRule(\n            inputRule.find,\n            (state, match, start, end) => {\n              const replaceWith = inputRule.replace({\n                match,\n                range: { from: start, to: end },\n                editor: this.editor,\n              });\n              if (replaceWith) {\n                const tr = state.tr;\n                const blockInfo = getBlockInfoFromTransaction(tr);\n\n                if (\n                  !blockInfo.isBlockContainer ||\n                  this.editor.schema.blockSchema[blockInfo.blockNoteType]\n                    ?.content !== \"inline\"\n                ) {\n                  return null;\n                }\n\n                tr.deleteRange(start, end);\n                updateBlockTr(tr, blockInfo.bnBlock.beforePos, replaceWith);\n                // updateBlockTr's replaceWith path leaves the selection after\n                // the new block when the content is replaced wholesale (e.g.\n                // when the rule returns content: []). Move the cursor back\n                // inside the new block so the user can keep typing.\n                const blockId = blockInfo.bnBlock.node.attrs.id;\n                if (blockId) {\n                  setTextCursorPosition(tr, blockId, \"start\");\n                }\n                return tr;\n              }\n              return null;\n            },\n            { undoable: true },\n          );\n        }),\n      );\n    }\n\n    if (Object.keys(extension.keyboardShortcuts || {}).length) {\n      plugins.push(\n        keymap(\n          Object.fromEntries(\n            Object.entries(extension.keyboardShortcuts!).map(([key, value]) => [\n              key,\n              () => value({ editor: this.editor }),\n            ]),\n          ),\n        ),\n      );\n    }\n\n    return { plugins, inputRules };\n  }\n\n  /**\n   * Get all extensions\n   */\n  public getExtensions(): Map<string, Extension> {\n    return new Map(\n      this.extensions.map((extension) => [extension.key, extension]),\n    );\n  }\n\n  /**\n   * Get a specific extension by it's instance\n   */\n  public getExtension<\n    const Ext extends Extension | ExtensionFactory = Extension,\n  >(\n    extension: string,\n  ):\n    | (Ext extends Extension\n        ? Ext\n        : Ext extends ExtensionFactory\n          ? ReturnType<ReturnType<Ext>>\n          : never)\n    | undefined;\n  public getExtension<const T extends ExtensionFactory>(\n    extension: T,\n  ): ReturnType<ReturnType<T>> | undefined;\n  public getExtension<const T extends ExtensionFactory | string = string>(\n    extension: T,\n  ):\n    | (T extends ExtensionFactory\n        ? ReturnType<ReturnType<T>>\n        : T extends string\n          ? Extension\n          : never)\n    | undefined {\n    if (typeof extension === \"string\") {\n      const instance = this.extensions.find((e) => e.key === extension);\n      if (!instance) {\n        return undefined;\n      }\n      return instance as any;\n    } else if (typeof extension === \"function\") {\n      const instance = this.extensionFactories.get(extension);\n      if (!instance) {\n        return undefined;\n      }\n      return instance as any;\n    }\n    throw new Error(`Invalid extension type: ${typeof extension}`);\n  }\n\n  /**\n   * Check if an extension exists\n   */\n  public hasExtension(key: string | Extension | ExtensionFactory): boolean {\n    if (typeof key === \"string\") {\n      return this.extensions.some((e) => e.key === key);\n    } else if (typeof key === \"object\" && \"key\" in key) {\n      return this.extensions.some((e) => e.key === key.key);\n    } else if (typeof key === \"function\") {\n      return this.extensionFactories.has(key);\n    }\n    return false;\n  }\n}\n","import type { Node, ResolvedPos } from \"prosemirror-model\";\n\n/**\n * Expands a range (start to end) to include the rest of the word if it starts or ends within a word\n */\nexport function expandPMRangeToWords(\n  doc: Node,\n  range: { $from: ResolvedPos; $to: ResolvedPos },\n) {\n  let { $from, $to } = range;\n\n  // Expand Start\n  // If the selection starts with a word character or punctuation, check if we need to expand left to include the rest of the word\n  if ($from.pos > $from.start() && $from.pos < doc.content.size) {\n    const charAfterStart = doc.textBetween($from.pos, $from.pos + 1);\n    if (/^[\\w\\p{P}]$/u.test(charAfterStart)) {\n      const textBefore = doc.textBetween($from.start(), $from.pos);\n      const wordMatch = textBefore.match(/[\\w\\p{P}]+$/u);\n      if (wordMatch) {\n        $from = doc.resolve($from.pos - wordMatch[0].length);\n      }\n    }\n  }\n\n  // Expand End\n  // If the selection ends with a word characte or punctuation, check if we need to expand right to include the rest of the word\n  if ($to.pos < $to.end() && $to.pos > 0) {\n    const charBeforeEnd = doc.textBetween($to.pos - 1, $to.pos);\n    if (/^[\\w\\p{P}]$/u.test(charBeforeEnd)) {\n      const textAfter = doc.textBetween($to.pos, $to.end());\n      const wordMatch = textAfter.match(/^[\\w\\p{P}]+/u);\n      if (wordMatch) {\n        $to = doc.resolve($to.pos + wordMatch[0].length);\n      }\n    }\n  }\n  return { $from, $to, from: $from.pos, to: $to.pos };\n}\n","import { TextSelection, type Transaction } from \"prosemirror-state\";\nimport { TableMap } from \"prosemirror-tables\";\nimport { Block } from \"../../../blocks/defaultBlocks.js\";\nimport { Selection } from \"../../../editor/selectionTypes.js\";\nimport {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../../schema/index.js\";\nimport { expandPMRangeToWords } from \"../../../util/expandToWords.js\";\nimport { getBlockInfo, getNearestBlockPos } from \"../../getBlockInfoFromPos.js\";\nimport {\n  nodeToBlock,\n  prosemirrorSliceToSlicedBlocks,\n} from \"../../nodeConversions/nodeToBlock.js\";\nimport { getNodeById } from \"../../nodeUtil.js\";\nimport { getBlockNoteSchema, getPmSchema } from \"../../pmUtil.js\";\n\nexport function getSelection<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(tr: Transaction): Selection<BSchema, I, S> | undefined {\n  const pmSchema = getPmSchema(tr);\n  // Return undefined if the selection is collapsed or a node is selected.\n  if (tr.selection.empty || \"node\" in tr.selection) {\n    return undefined;\n  }\n\n  const $startBlockBeforePos = tr.doc.resolve(\n    getNearestBlockPos(tr.doc, tr.selection.from).posBeforeNode,\n  );\n  const $endBlockBeforePos = tr.doc.resolve(\n    getNearestBlockPos(tr.doc, tr.selection.to).posBeforeNode,\n  );\n\n  // Converts the node at the given index and depth around `$startBlockBeforePos`\n  // to a block. Used to get blocks at given indices at the shared depth and\n  // at the depth of `$startBlockBeforePos`.\n  const indexToBlock = (\n    index: number,\n    depth?: number,\n  ): Block<BSchema, I, S> => {\n    const pos = $startBlockBeforePos.posAtIndex(index, depth);\n    const node = tr.doc.resolve(pos).nodeAfter;\n\n    if (!node) {\n      throw new Error(\n        `Error getting selection - node not found at position ${pos}`,\n      );\n    }\n\n    return nodeToBlock(node, pmSchema);\n  };\n\n  const blocks: Block<BSchema, I, S>[] = [];\n  // Minimum depth at which the blocks share a common ancestor.\n  const sharedDepth = $startBlockBeforePos.sharedDepth($endBlockBeforePos.pos);\n  const startIndex = $startBlockBeforePos.index(sharedDepth);\n  const endIndex = $endBlockBeforePos.index(sharedDepth);\n\n  // In most cases, we want to return the blocks spanned by the selection at the\n  // shared depth. However, when the block in which the selection starts is at a\n  // higher depth than the shared depth, we omit the first block at the shared\n  // depth. Instead, we include the first block at its depth, and any blocks at\n  // a higher index up to the shared depth. The following  example illustrates\n  // this:\n  // - id-0\n  //   - id-1\n  //     - >|id-2\n  //     - id-3\n  //   - id-4\n  //     - id-5\n  //   - id-6\n  // - id-7\n  // - id-8\n  // - id-9|<\n  //   - id-10\n  // Here, each block is represented by its ID, and the selection is represented\n  // by the `>|` and `|<` markers. So the selection starts in block `id-2` and\n  // ends in block `id-8`. In this case, the shared depth is 0, since the blocks\n  // `id-6`, `id-7`, and `id-8` set the shared depth, as they are the least\n  // nested blocks spanned by the selection. Therefore, these blocks are all\n  // added to the `blocks` array. However, the selection starts in block `id-2`,\n  // which is at a higher depth than the shared depth. So we add block `id-2` to\n  // the `blocks` array, as well as any later siblings (in this case, `id-3`),\n  // and move up one level of depth. The ancestor of block `id-2` at this depth\n  // is block `id-1`, so we add all its later siblings to the `blocks` array as\n  // well, again moving up one level of depth. Since we're now at the shared\n  // depth, we are done. The final `blocks` array for this example would be:\n  // [ id-2, id-3, id-4, id-6, id-7, id-8, id-9 ]\n  if ($startBlockBeforePos.depth > sharedDepth) {\n    // Adds the block that the selection starts in.\n    blocks.push(nodeToBlock($startBlockBeforePos.nodeAfter!, pmSchema));\n\n    // Traverses all depths from the depth of the block in which the selection\n    // starts, up to the shared depth.\n    for (let depth = $startBlockBeforePos.depth; depth > sharedDepth; depth--) {\n      const parentNode = $startBlockBeforePos.node(depth);\n\n      if (parentNode.type.isInGroup(\"childContainer\")) {\n        const startIndexAtDepth = $startBlockBeforePos.index(depth) + 1;\n        const childCountAtDepth = $startBlockBeforePos.node(depth).childCount;\n\n        // Adds all blocks after the index of the block in which the selection\n        // starts (or its ancestors at lower depths).\n        for (let i = startIndexAtDepth; i < childCountAtDepth; i++) {\n          blocks.push(indexToBlock(i, depth));\n        }\n      }\n    }\n  } else {\n    // Adds the first block spanned by the selection at the shared depth.\n    blocks.push(indexToBlock(startIndex, sharedDepth));\n  }\n\n  // Adds all blocks spanned by the selection at the shared depth, excluding\n  // the first.\n  for (let i = startIndex + 1; i <= endIndex; i++) {\n    blocks.push(indexToBlock(i, sharedDepth));\n  }\n\n  if (blocks.length === 0) {\n    throw new Error(\n      `Error getting selection - selection doesn't span any blocks (${tr.selection})`,\n    );\n  }\n\n  return {\n    blocks,\n  };\n}\n\nexport function setSelection(\n  tr: Transaction,\n  startBlock: BlockIdentifier,\n  endBlock: BlockIdentifier,\n) {\n  const startBlockId =\n    typeof startBlock === \"string\" ? startBlock : startBlock.id;\n  const endBlockId = typeof endBlock === \"string\" ? endBlock : endBlock.id;\n  const pmSchema = getPmSchema(tr);\n  const schema = getBlockNoteSchema(pmSchema);\n\n  if (startBlockId === endBlockId) {\n    throw new Error(\n      `Attempting to set selection with the same anchor and head blocks (id ${startBlockId})`,\n    );\n  }\n  const anchorPosInfo = getNodeById(startBlockId, tr.doc);\n  if (!anchorPosInfo) {\n    throw new Error(`Block with ID ${startBlockId} not found`);\n  }\n  const headPosInfo = getNodeById(endBlockId, tr.doc);\n  if (!headPosInfo) {\n    throw new Error(`Block with ID ${endBlockId} not found`);\n  }\n\n  const anchorBlockInfo = getBlockInfo(anchorPosInfo);\n  const headBlockInfo = getBlockInfo(headPosInfo);\n\n  const anchorBlockConfig =\n    schema.blockSchema[\n      anchorBlockInfo.blockNoteType as keyof typeof schema.blockSchema\n    ];\n  const headBlockConfig =\n    schema.blockSchema[\n      headBlockInfo.blockNoteType as keyof typeof schema.blockSchema\n    ];\n\n  if (\n    !anchorBlockInfo.isBlockContainer ||\n    anchorBlockConfig.content === \"none\"\n  ) {\n    throw new Error(\n      `Attempting to set selection anchor in block without content (id ${startBlockId})`,\n    );\n  }\n  if (!headBlockInfo.isBlockContainer || headBlockConfig.content === \"none\") {\n    throw new Error(\n      `Attempting to set selection anchor in block without content (id ${endBlockId})`,\n    );\n  }\n\n  let startPos: number;\n  let endPos: number;\n\n  if (anchorBlockConfig.content === \"table\") {\n    const tableMap = TableMap.get(anchorBlockInfo.blockContent.node);\n    const firstCellPos =\n      anchorBlockInfo.blockContent.beforePos +\n      tableMap.positionAt(0, 0, anchorBlockInfo.blockContent.node) +\n      1;\n    startPos = firstCellPos + 2;\n  } else {\n    startPos = anchorBlockInfo.blockContent.beforePos + 1;\n  }\n\n  if (headBlockConfig.content === \"table\") {\n    const tableMap = TableMap.get(headBlockInfo.blockContent.node);\n    const lastCellPos =\n      headBlockInfo.blockContent.beforePos +\n      tableMap.positionAt(\n        tableMap.height - 1,\n        tableMap.width - 1,\n        headBlockInfo.blockContent.node,\n      ) +\n      1;\n    const lastCellNodeSize = tr.doc.resolve(lastCellPos).nodeAfter!.nodeSize;\n    endPos = lastCellPos + lastCellNodeSize - 2;\n  } else {\n    endPos = headBlockInfo.blockContent.afterPos - 1;\n  }\n\n  // TODO: We should polish up the `MultipleNodeSelection` and use that instead.\n  //  Right now it's missing a few things like a jsonID and styling to show\n  //  which nodes are selected. `TextSelection` is ok for now, but has the\n  //  restriction that the start/end blocks must have content.\n  tr.setSelection(TextSelection.create(tr.doc, startPos, endPos));\n}\n\nexport function getSelectionCutBlocks(tr: Transaction, expandToWords = false) {\n  // TODO: fix image node selection\n\n  const pmSchema = getPmSchema(tr);\n\n  const range = expandToWords\n    ? expandPMRangeToWords(tr.doc, tr.selection)\n    : tr.selection;\n\n  let start = range.$from;\n  let end = range.$to;\n\n  // the selection moves below are used to make sure `prosemirrorSliceToSlicedBlocks` returns\n  // the correct information about whether content is cut at the start or end of a block\n\n  // if the end is at the end of a node (|</span></p>) move it forward so we include all closing tags (</span></p>|)\n  while (end.parentOffset >= end.parent.nodeSize - 2 && end.depth > 0) {\n    end = tr.doc.resolve(end.pos + 1);\n  }\n\n  // if the end is at the start of an empty node (</span></p><p>|) move it backwards so we drop empty start tags (</span></p>|)\n  while (end.parentOffset === 0 && end.depth > 0) {\n    end = tr.doc.resolve(end.pos - 1);\n  }\n\n  // if the start is at the start of a node (<p><span>|) move it backwards so we include all open tags (|<p><span>)\n  while (start.parentOffset === 0 && start.depth > 0) {\n    start = tr.doc.resolve(start.pos - 1);\n  }\n\n  // if the start is at the end of a node (|</p><p><span>|) move it forwards so we drop all closing tags (|<p><span>)\n  while (start.parentOffset >= start.parent.nodeSize - 2 && start.depth > 0) {\n    start = tr.doc.resolve(start.pos + 1);\n  }\n\n  const selectionInfo = prosemirrorSliceToSlicedBlocks(\n    tr.doc.slice(start.pos, end.pos, true),\n    pmSchema,\n  );\n\n  return {\n    _meta: {\n      startPos: start.pos,\n      endPos: end.pos,\n    },\n    ...selectionInfo,\n  };\n}\n","import { isNodeSelection, posToDOMRect } from \"@tiptap/core\";\nimport {\n  getSelection,\n  getSelectionCutBlocks,\n  setSelection,\n} from \"../../api/blockManipulation/selections/selection.js\";\nimport {\n  getTextCursorPosition,\n  setTextCursorPosition,\n} from \"../../api/blockManipulation/selections/textCursorPosition.js\";\nimport {\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n} from \"../../blocks/defaultBlocks.js\";\nimport {\n  BlockIdentifier,\n  BlockSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"../../schema/index.js\";\nimport { BlockNoteEditor } from \"../BlockNoteEditor.js\";\nimport { TextCursorPosition } from \"../cursorPositionTypes.js\";\nimport { Selection } from \"../selectionTypes.js\";\n\nexport class SelectionManager<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> {\n  constructor(private editor: BlockNoteEditor<BSchema, ISchema, SSchema>) {}\n\n  /**\n   * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)\n   * that the selection spans across.\n   *\n   * If the selection starts / ends halfway through a block, the returned data will contain the entire block.\n   */\n  public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {\n    return this.editor.transact((tr) => getSelection(tr));\n  }\n\n  /**\n   * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)\n   * that the selection spans across.\n   *\n   * If the selection starts / ends halfway through a block, the returned block will be\n   * only the part of the block that is included in the selection.\n   */\n  public getSelectionCutBlocks(expandToWords = false) {\n    return this.editor.transact((tr) =>\n      getSelectionCutBlocks(tr, expandToWords),\n    );\n  }\n\n  /**\n   * Sets the selection to a range of blocks.\n   * @param startBlock The identifier of the block that should be the start of the selection.\n   * @param endBlock The identifier of the block that should be the end of the selection.\n   */\n  public setSelection(startBlock: BlockIdentifier, endBlock: BlockIdentifier) {\n    return this.editor.transact((tr) => setSelection(tr, startBlock, endBlock));\n  }\n\n  /**\n   * Gets a snapshot of the current text cursor position.\n   * @returns A snapshot of the current text cursor position.\n   */\n  public getTextCursorPosition(): TextCursorPosition<\n    BSchema,\n    ISchema,\n    SSchema\n  > {\n    return this.editor.transact((tr) => getTextCursorPosition(tr));\n  }\n\n  /**\n   * Sets the text cursor position to the start or end of an existing block. Throws an error if the target block could\n   * not be found.\n   * @param targetBlock The identifier of an existing block that the text cursor should be moved to.\n   * @param placement Whether the text cursor should be placed at the start or end of the block.\n   */\n  public setTextCursorPosition(\n    targetBlock: BlockIdentifier,\n    placement: \"start\" | \"end\" = \"start\",\n  ) {\n    return this.editor.transact((tr) =>\n      setTextCursorPosition(tr, targetBlock, placement),\n    );\n  }\n\n  /**\n   * Gets the bounding box of the current selection.\n   */\n  public getSelectionBoundingBox() {\n    if (!this.editor.prosemirrorView) {\n      return undefined;\n    }\n\n    const { selection } = this.editor.prosemirrorState;\n\n    // support for CellSelections\n    const { ranges } = selection;\n    const from = Math.min(...ranges.map((range) => range.$from.pos));\n    const to = Math.max(...ranges.map((range) => range.$to.pos));\n\n    if (isNodeSelection(selection)) {\n      const node = this.editor.prosemirrorView.nodeDOM(from) as HTMLElement;\n      if (node) {\n        return node.getBoundingClientRect();\n      }\n    }\n\n    return posToDOMRect(\n      this.editor.prosemirrorView,\n      from,\n      to,\n    ).toJSON() as DOMRect;\n  }\n}\n","import { Command, Transaction } from \"prosemirror-state\";\nimport type { YUndoExtension } from \"../../extensions/Collaboration/YUndo.js\";\nimport type { HistoryExtension } from \"../../extensions/History/History.js\";\nimport { BlockNoteEditor } from \"../BlockNoteEditor.js\";\n\nexport class StateManager {\n  constructor(private editor: BlockNoteEditor<any, any, any>) {}\n\n  /**\n   * Stores the currently active transaction, which is the accumulated transaction from all {@link dispatch} calls during a {@link transact} calls\n   */\n  private activeTransaction: Transaction | null = null;\n\n  /**\n   * For any command that can be executed, you can check if it can be executed by calling `editor.can(command)`.\n   * @example\n   * ```ts\n   * if (editor.can(editor.undo)) {\n   *   // show button\n   * } else {\n   *   // hide button\n   * }\n   */\n  public can(cb: () => boolean) {\n    try {\n      this.isInCan = true;\n      return cb();\n    } finally {\n      this.isInCan = false;\n    }\n  }\n\n  // Flag to indicate if we're in a `can` call\n  private isInCan = false;\n\n  /**\n   * Execute a prosemirror command. This is mostly for backwards compatibility with older code.\n   *\n   * @note You should prefer the {@link transact} method when possible, as it will automatically handle the dispatching of the transaction and work across blocknote transactions.\n   *\n   * @example\n   * ```ts\n   * editor.exec((state, dispatch, view) => {\n   *   dispatch(state.tr.insertText(\"Hello, world!\"));\n   * });\n   * ```\n   */\n  public exec(command: Command) {\n    if (this.activeTransaction) {\n      throw new Error(\n        \"`exec` should not be called within a `transact` call, move the `exec` call outside of the `transact` call\",\n      );\n    }\n    if (this.isInCan) {\n      return this.canExec(command);\n    }\n    const state = this.prosemirrorState;\n    const view = this.prosemirrorView;\n    const dispatch = (tr: Transaction) => this.prosemirrorView.dispatch(tr);\n\n    return command(state, dispatch, view);\n  }\n\n  /**\n   * Check if a command can be executed. A command should return `false` if it is not valid in the current state.\n   *\n   * @example\n   * ```ts\n   * if (editor.canExec(command)) {\n   *   // show button\n   * } else {\n   *   // hide button\n   * }\n   * ```\n   */\n  public canExec(command: Command): boolean {\n    if (this.activeTransaction) {\n      throw new Error(\n        \"`canExec` should not be called within a `transact` call, move the `canExec` call outside of the `transact` call\",\n      );\n    }\n    const state = this.prosemirrorState;\n    const view = this.prosemirrorView;\n\n    return command(state, undefined, view);\n  }\n\n  /**\n   * Execute a function within a \"blocknote transaction\".\n   * All changes to the editor within the transaction will be grouped together, so that\n   * we can dispatch them as a single operation (thus creating only a single undo step)\n   *\n   * @note There is no need to dispatch the transaction, as it will be automatically dispatched when the callback is complete.\n   *\n   * @example\n   * ```ts\n   * // All changes to the editor will be grouped together\n   * editor.transact((tr) => {\n   *   tr.insertText(\"Hello, world!\");\n   * // These two operations will be grouped together in a single undo step\n   *   editor.transact((tr) => {\n   *     tr.insertText(\"Hello, world!\");\n   *   });\n   * });\n   * ```\n   */\n  public transact<T>(\n    callback: (\n      /**\n       * The current active transaction, this will automatically be dispatched to the editor when the callback is complete\n       * If another `transact` call is made within the callback, it will be passed the same transaction as the parent call.\n       */\n      tr: Transaction,\n    ) => T,\n  ): T {\n    if (this.activeTransaction) {\n      // Already in a transaction, so we can just callback immediately\n      return callback(this.activeTransaction);\n    }\n\n    try {\n      // Enter transaction mode, by setting a starting transaction\n      this.activeTransaction = this.editor._tiptapEditor.state.tr;\n\n      // Capture all dispatch'd transactions\n      const result = callback(this.activeTransaction);\n\n      // Any transactions captured by the `dispatch` call will be stored in `this.activeTransaction`\n      const activeTr = this.activeTransaction;\n\n      this.activeTransaction = null;\n      if (\n        activeTr &&\n        // Only dispatch if the transaction was actually modified in some way\n        (activeTr.docChanged ||\n          activeTr.selectionSet ||\n          activeTr.scrolledIntoView ||\n          activeTr.storedMarksSet ||\n          !activeTr.isGeneric)\n      ) {\n        // Dispatch the transaction if it was modified\n        this.prosemirrorView.dispatch(activeTr);\n      }\n\n      return result;\n    } finally {\n      // We wrap this in a finally block to ensure we don't disable future transactions just because of an error in the callback\n      this.activeTransaction = null;\n    }\n  }\n  /**\n   * Get the underlying prosemirror state\n   * @note Prefer using `editor.transact` to read the current editor state, as that will ensure the state is up to date\n   * @see https://prosemirror.net/docs/ref/#state.EditorState\n   */\n  public get prosemirrorState() {\n    if (this.activeTransaction) {\n      throw new Error(\n        \"`prosemirrorState` should not be called within a `transact` call, move the `prosemirrorState` call outside of the `transact` call or use `editor.transact` to read the current editor state\",\n      );\n    }\n    return this.editor._tiptapEditor.state;\n  }\n\n  /**\n   * Get the underlying prosemirror view\n   * @see https://prosemirror.net/docs/ref/#view.EditorView\n   */\n  public get prosemirrorView() {\n    return this.editor._tiptapEditor.view;\n  }\n\n  public isFocused() {\n    return this.prosemirrorView?.hasFocus() || false;\n  }\n\n  public focus() {\n    this.prosemirrorView?.focus();\n  }\n\n  /**\n   * Checks if the editor is currently editable, or if it's locked.\n   * @returns True if the editor is editable, false otherwise.\n   */\n  public get isEditable(): boolean {\n    if (!this.editor._tiptapEditor) {\n      if (!this.editor.headless) {\n        throw new Error(\"no editor, but also not headless?\");\n      }\n      return false;\n    }\n    return this.editor._tiptapEditor.isEditable === undefined\n      ? true\n      : this.editor._tiptapEditor.isEditable;\n  }\n\n  /**\n   * Makes the editor editable or locks it, depending on the argument passed.\n   * @param editable True to make the editor editable, or false to lock it.\n   */\n  public set isEditable(editable: boolean) {\n    if (!this.editor._tiptapEditor) {\n      if (!this.editor.headless) {\n        throw new Error(\"no editor, but also not headless?\");\n      }\n      // not relevant on headless\n      return;\n    }\n    if (this.editor._tiptapEditor.options.editable !== editable) {\n      this.editor._tiptapEditor.setEditable(editable);\n    }\n  }\n\n  /**\n   * Undo the last action.\n   */\n  public undo(): boolean {\n    // Purposefully not using the UndoPlugin to not import y-prosemirror when not needed\n    const undoPlugin = this.editor.getExtension<typeof YUndoExtension>(\"yUndo\");\n    if (undoPlugin) {\n      return this.exec(undoPlugin.undoCommand);\n    }\n\n    const historyPlugin =\n      this.editor.getExtension<typeof HistoryExtension>(\"history\");\n    if (historyPlugin) {\n      return this.exec(historyPlugin.undoCommand);\n    }\n\n    throw new Error(\"No undo plugin found\");\n  }\n\n  /**\n   * Redo the last action.\n   */\n  public redo() {\n    const undoPlugin = this.editor.getExtension<typeof YUndoExtension>(\"yUndo\");\n    if (undoPlugin) {\n      return this.exec(undoPlugin.redoCommand);\n    }\n\n    const historyPlugin =\n      this.editor.getExtension<typeof HistoryExtension>(\"history\");\n    if (historyPlugin) {\n      return this.exec(historyPlugin.redoCommand);\n    }\n\n    throw new Error(\"No redo plugin found\");\n  }\n}\n","import { selectionToInsertionEnd } from \"@tiptap/core\";\nimport { Node } from \"prosemirror-model\";\n\nimport type { Transaction } from \"prosemirror-state\";\n\n// similar to tiptap insertContentAt\nexport function insertContentAt(\n  tr: Transaction,\n  position: number | { from: number; to: number },\n  nodes: Node[],\n  options: {\n    updateSelection: boolean;\n  } = { updateSelection: true },\n) {\n  // don’t dispatch an empty fragment because this can lead to strange errors\n  // if (content.toString() === \"<>\") {\n  //   return true;\n  // }\n\n  let { from, to } =\n    typeof position === \"number\"\n      ? { from: position, to: position }\n      : { from: position.from, to: position.to };\n\n  let isOnlyTextContent = true;\n  let isOnlyBlockContent = true;\n  // const nodes = isFragment(content) ? content : [content];\n\n  let text = \"\";\n\n  nodes.forEach((node) => {\n    // check if added node is valid\n    node.check();\n\n    if (isOnlyTextContent && node.isText && node.marks.length === 0) {\n      text += node.text;\n    } else {\n      isOnlyTextContent = false;\n    }\n\n    isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false;\n  });\n\n  // check if we can replace the wrapping node by\n  // the newly inserted content\n  // example:\n  // replace an empty paragraph by an inserted image\n  // instead of inserting the image below the paragraph\n  if (from === to && isOnlyBlockContent) {\n    const { parent } = tr.doc.resolve(from);\n    const isEmptyTextBlock =\n      parent.isTextblock && !parent.type.spec.code && !parent.childCount;\n\n    if (isEmptyTextBlock) {\n      from -= 1;\n      to += 1;\n    }\n  }\n\n  // if there is only plain text we have to use `insertText`\n  // because this will keep the current marks\n  if (isOnlyTextContent) {\n    // if value is string, we can use it directly\n    // otherwise if it is an array, we have to join it\n    // if (Array.isArray(value)) {\n    //   tr.insertText(value.map((v) => v.text || \"\").join(\"\"), from, to);\n    // } else if (typeof value === \"object\" && !!value && !!value.text) {\n    //   tr.insertText(value.text, from, to);\n    // } else {\n    //   tr.insertText(value as string, from, to);\n    // }\n    tr.insertText(text, from, to);\n  } else {\n    tr.replaceWith(from, to, nodes);\n  }\n\n  // set cursor at end of inserted content\n  if (options.updateSelection) {\n    selectionToInsertionEnd(tr, tr.steps.length - 1, -1);\n  }\n\n  return true;\n}\n","import { getMarkRange } from \"@tiptap/core\";\nimport { insertContentAt } from \"../../api/blockManipulation/insertContentAt.js\";\nimport { inlineContentToNodes } from \"../../api/nodeConversions/blockToNode.js\";\nimport {\n  BlockSchema,\n  InlineContentSchema,\n  PartialInlineContent,\n  StyleSchema,\n  Styles,\n} from \"../../schema/index.js\";\nimport {\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n} from \"../../blocks/defaultBlocks.js\";\nimport { UnreachableCaseError } from \"../../util/typescript.js\";\nimport { BlockNoteEditor } from \"../BlockNoteEditor.js\";\n\nexport class StyleManager<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> {\n  constructor(private editor: BlockNoteEditor<BSchema, ISchema, SSchema>) {}\n\n  /**\n   * Insert a piece of content at the current cursor position.\n   *\n   * @param content can be a string, or array of partial inline content elements\n   */\n  public insertInlineContent(\n    content: PartialInlineContent<ISchema, SSchema>,\n    { updateSelection = false }: { updateSelection?: boolean } = {},\n  ) {\n    const nodes = inlineContentToNodes(content, this.editor.pmSchema);\n\n    this.editor.transact((tr) => {\n      insertContentAt(\n        tr,\n        {\n          from: tr.selection.from,\n          to: tr.selection.to,\n        },\n        nodes,\n        {\n          updateSelection,\n        },\n      );\n    });\n  }\n\n  /**\n   * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.\n   */\n  public getActiveStyles() {\n    return this.editor.transact((tr) => {\n      const styles: Styles<SSchema> = {};\n      const marks = tr.selection.$to.marks();\n\n      for (const mark of marks) {\n        const config = this.editor.schema.styleSchema[mark.type.name];\n        if (!config) {\n          if (\n            // Links are not considered styles in blocknote\n            mark.type.name !== \"link\" &&\n            // \"blocknoteIgnore\" tagged marks (such as comments) are also not considered BlockNote \"styles\"\n            !mark.type.spec.blocknoteIgnore\n          ) {\n            // eslint-disable-next-line no-console\n            console.warn(\"mark not found in styleschema\", mark.type.name);\n          }\n\n          continue;\n        }\n        if (config.propSchema === \"boolean\") {\n          (styles as any)[config.type] = true;\n        } else {\n          (styles as any)[config.type] = mark.attrs.stringValue;\n        }\n      }\n\n      return styles;\n    });\n  }\n\n  /**\n   * Adds styles to the currently selected content.\n   * @param styles The styles to add.\n   */\n  public addStyles(styles: Styles<SSchema>) {\n    for (const [style, value] of Object.entries(styles)) {\n      const config = this.editor.schema.styleSchema[style];\n      if (!config) {\n        throw new Error(`style ${style} not found in styleSchema`);\n      }\n      if (config.propSchema === \"boolean\") {\n        this.editor._tiptapEditor.commands.setMark(style);\n      } else if (config.propSchema === \"string\") {\n        this.editor._tiptapEditor.commands.setMark(style, {\n          stringValue: value,\n        });\n      } else {\n        throw new UnreachableCaseError(config.propSchema);\n      }\n    }\n  }\n\n  /**\n   * Removes styles from the currently selected content.\n   * @param styles The styles to remove.\n   */\n  public removeStyles(styles: Styles<SSchema>) {\n    for (const style of Object.keys(styles)) {\n      this.editor._tiptapEditor.commands.unsetMark(style);\n    }\n  }\n\n  /**\n   * Toggles styles on the currently selected content.\n   * @param styles The styles to toggle.\n   */\n  public toggleStyles(styles: Styles<SSchema>) {\n    for (const [style, value] of Object.entries(styles)) {\n      const config = this.editor.schema.styleSchema[style];\n      if (!config) {\n        throw new Error(`style ${style} not found in styleSchema`);\n      }\n      if (config.propSchema === \"boolean\") {\n        this.editor._tiptapEditor.commands.toggleMark(style);\n      } else if (config.propSchema === \"string\") {\n        this.editor._tiptapEditor.commands.toggleMark(style, {\n          stringValue: value,\n        });\n      } else {\n        throw new UnreachableCaseError(config.propSchema);\n      }\n    }\n  }\n\n  /**\n   * Gets the currently selected text.\n   */\n  public getSelectedText() {\n    return this.editor.transact((tr) => {\n      return tr.doc.textBetween(tr.selection.from, tr.selection.to);\n    });\n  }\n\n  /**\n   * Find the link mark and its range at the given position.\n   * Returns undefined if there is no link at that position.\n   */\n  public getLinkMarkAtPos(pos: number) {\n    return this.editor.transact((tr) => {\n      const resolvedPos = tr.doc.resolve(pos);\n      const linkMark = resolvedPos\n        .marks()\n        .find((mark) => mark.type.name === \"link\");\n\n      if (!linkMark) {\n        return undefined;\n      }\n\n      const range = getMarkRange(resolvedPos, linkMark.type);\n      if (!range) {\n        return undefined;\n      }\n\n      return {\n        href: linkMark.attrs.href as string,\n        from: range.from,\n        to: range.to,\n        text: tr.doc.textBetween(range.from, range.to),\n      };\n    });\n  }\n\n  /**\n   * Gets the URL of the last link in the current selection, or `undefined` if there are no links in the selection.\n   */\n  public getSelectedLinkUrl() {\n    return this.editor.transact((tr) => {\n      return this.getLinkMarkAtPos(tr.selection.from)?.href;\n    });\n  }\n\n  /**\n   * Creates a new link to replace the selected content.\n   * @param url The link URL.\n   * @param text The text to display the link with.\n   */\n  public createLink(url: string, text?: string) {\n    if (url === \"\") {\n      return;\n    }\n\n    this.editor.transact((tr) => {\n      const { from, to } = tr.selection;\n      const linkMark = this.editor.pmSchema.mark(\"link\", { href: url });\n\n      if (text) {\n        tr.insertText(text, from, to).addMark(\n          from,\n          from + text.length,\n          linkMark,\n        );\n      } else {\n        tr.addMark(from, to, linkMark);\n      }\n    });\n  }\n\n  /**\n   * Updates the link at the given position with a new URL and text.\n   * @param url The new link URL.\n   * @param text The new text to display.\n   * @param position The position inside the link to edit. Defaults to the current selection anchor.\n   */\n  public editLink(\n    url: string,\n    text: string,\n    position = this.editor.transact((tr) => tr.selection.anchor),\n  ) {\n    this.editor.transact((tr) => {\n      const linkData = this.getLinkMarkAtPos(position + 1);\n      const { from, to } = linkData || {\n        from: tr.selection.from,\n        to: tr.selection.to,\n      };\n\n      const linkMark = this.editor.pmSchema.mark(\"link\", { href: url });\n      const existingText = tr.doc.textBetween(from, to);\n      if (text !== existingText) {\n        tr.insertText(text, from, to);\n      }\n      tr.addMark(from, from + text.length, linkMark);\n    });\n    this.editor.prosemirrorView.focus();\n  }\n\n  /**\n   * Removes the link at the given position, keeping the text.\n   * @param position The position inside the link to remove. Defaults to the current selection anchor.\n   */\n  public deleteLink(\n    position = this.editor.transact((tr) => tr.selection.anchor),\n  ) {\n    this.editor.transact((tr) => {\n      const linkData = this.getLinkMarkAtPos(position + 1);\n      const { from, to } = linkData || {\n        from: tr.selection.from,\n        to: tr.selection.to,\n      };\n\n      tr.removeMark(from, to, this.editor.pmSchema.marks[\"link\"]).setMeta(\n        \"preventAutolink\",\n        true,\n      );\n    });\n    this.editor.prosemirrorView.focus();\n  }\n}\n","import { Fragment, Schema, Slice } from \"@tiptap/pm/model\";\nimport { EditorView } from \"@tiptap/pm/view\";\n\nimport { getBlockInfoFromSelection } from \"../api/getBlockInfoFromPos.js\";\nimport { findParentNodeClosestToPos } from \"@tiptap/core\";\n\n/**\n * Checks if the current selection is inside a table cell.\n * Returns the depth of the tableCell/tableHeader node if found, -1 otherwise.\n */\nfunction isInTableCell(view: EditorView): boolean {\n  return (\n    findParentNodeClosestToPos(view.state.selection.$from, (n) => {\n      return n.type.name === \"tableCell\" || n.type.name === \"tableHeader\";\n    }) !== undefined\n  );\n}\n\n/**\n * Converts block content to inline content with hard breaks.\n * This is used when pasting into table cells which can only contain inline content.\n */\nfunction convertBlocksToInlineContent(\n  fragment: Fragment,\n  schema: Schema,\n): Fragment {\n  const hardBreak = schema.nodes.hardBreak;\n  let result = Fragment.empty;\n\n  fragment.forEach((node) => {\n    if (node.isTextblock && node.childCount > 0) {\n      // Extract inline content from paragraphs, headings, etc.\n      result = result.append(node.content);\n      result = result.addToEnd(hardBreak.create());\n    } else if (node.isText) {\n      result = result.addToEnd(node);\n    } else if (node.isBlock && node.childCount > 0) {\n      // Recurse into block containers, blockGroups, etc.\n      result = result.append(\n        convertBlocksToInlineContent(node.content, schema),\n      );\n      result = result.addToEnd(hardBreak.create());\n    }\n  });\n\n  // Remove trailing hard break\n  if (result.lastChild?.type === hardBreak) {\n    result = result.cut(0, result.size - 1);\n  }\n\n  return result;\n}\n\n// helper function to remove a child from a fragment\nfunction removeChild(node: Fragment, n: number) {\n  const children: any[] = [];\n  node.forEach((child, _, i) => {\n    if (i !== n) {\n      children.push(child);\n    }\n  });\n  return Fragment.from(children);\n}\n\n/**\n * Wrap adjacent tableRow items in a table.\n *\n * This makes sure the content that we paste is always a table (and not a tableRow)\n * A table works better for the remaing paste handling logic, as it's actually a blockContent node\n */\nexport function wrapTableRows(f: Fragment, schema: Schema) {\n  const newItems: any[] = [];\n  for (let i = 0; i < f.childCount; i++) {\n    if (f.child(i).type.name === \"tableRow\") {\n      if (\n        newItems.length > 0 &&\n        newItems[newItems.length - 1].type.name === \"table\"\n      ) {\n        // append to existing table\n        const prevTable = newItems[newItems.length - 1];\n        const newTable = prevTable.copy(prevTable.content.addToEnd(f.child(i)));\n        newItems[newItems.length - 1] = newTable;\n      } else {\n        // create new table to wrap tableRow with\n        const newTable = schema.nodes.table.createChecked(\n          undefined,\n          f.child(i),\n        );\n        newItems.push(newTable);\n      }\n    } else {\n      newItems.push(f.child(i));\n    }\n  }\n  f = Fragment.from(newItems);\n  return f;\n}\n\n/**\n * fix for https://github.com/ProseMirror/prosemirror/issues/1430#issuecomment-1822570821\n *\n * This fix wraps pasted ProseMirror nodes in their own `blockContainer` nodes\n * in most cases. This is to ensure that ProseMirror inserts them as separate\n * blocks, which it sometimes doesn't do because it doesn't have enough context\n * about the hierarchy of the pasted nodes. The issue can be seen when pasting\n * e.g. an image or two consecutive paragraphs, where PM tries to nest the\n * pasted block(s) when it shouldn't.\n *\n * However, the fix is not applied in a few cases. See `shouldApplyFix` for\n * which cases are excluded.\n */\nexport function transformPasted(slice: Slice, view: EditorView) {\n  let f = Fragment.from(slice.content);\n  f = wrapTableRows(f, view.state.schema);\n\n  const retyped = retypeLeadingParagraphForEmptyTarget(f, view, slice);\n  if (retyped) {\n    return retyped;\n  }\n\n  if (isInTableCell(view)) {\n    let hasTableContent = false;\n    f.descendants((node) => {\n      if (node.type.isInGroup(\"tableContent\")) {\n        hasTableContent = true;\n      }\n    });\n    if (\n      !hasTableContent &&\n      // is the content valid for a table paragraph?\n      !view.state.schema.nodes.tableParagraph.validContent(f)\n    ) {\n      // if not, convert the content to inline content\n      return new Slice(\n        convertBlocksToInlineContent(f, view.state.schema),\n        0,\n        0,\n      );\n    }\n  }\n\n  if (!shouldApplyFix(f, view)) {\n    // Don't apply the fix.\n    return new Slice(f, slice.openStart, slice.openEnd);\n  }\n\n  for (let i = 0; i < f.childCount; i++) {\n    if (f.child(i).type.spec.group === \"blockContent\") {\n      const content = [f.child(i)];\n\n      // when there is a blockGroup with lists, it should be nested in the new blockcontainer\n      // (if we remove this if-block, the nesting bug will be fixed, but lists won't be nested correctly)\n      if (\n        i + 1 < f.childCount &&\n        f.child(i + 1).type.name === \"blockGroup\" // TODO\n      ) {\n        const nestedChild = f\n          .child(i + 1)\n          .child(0)\n          .child(0);\n\n        if (\n          nestedChild.type.name === \"bulletListItem\" ||\n          nestedChild.type.name === \"numberedListItem\" ||\n          nestedChild.type.name === \"checkListItem\"\n        ) {\n          content.push(f.child(i + 1));\n          f = removeChild(f, i + 1);\n        }\n      }\n      const container = view.state.schema.nodes.blockContainer.createChecked(\n        undefined,\n        content,\n      );\n      f = f.replaceChild(i, container);\n    }\n  }\n  return new Slice(f, slice.openStart, slice.openEnd);\n}\n\n/**\n * Pasting plain text into an empty inline-content block (e.g. an empty\n * bullet list item) would normally replace that block with a paragraph:\n * BlockNote's serializer always wraps content in\n * `blockGroup > blockContainer > <block>`, producing a closed slice that\n * ProseMirror inserts as a new block rather than splicing inline.\n *\n * To preserve the empty block's type, retype the leading paragraph in the\n * slice to match the target block. Subsequent blocks in the slice are left\n * alone and end up as siblings.\n *\n * Scoped to: empty, non-paragraph, inline-content target + paragraph leading\n * the slice. A non-empty target already gives ProseMirror a valid inline\n * insertion point so it splices correctly on its own; non-paragraph leading\n * blocks (heading, list item, …) carry semantic meaning the user picked, so\n * we keep the existing replace behavior.\n */\nfunction retypeLeadingParagraphForEmptyTarget(\n  fragment: Fragment,\n  view: EditorView,\n  slice: Slice,\n): Slice | null {\n  if (isInTableCell(view)) {\n    return null;\n  }\n\n  // `transformPasted` is also called for drop events, where the slice will be\n  // inserted at the drop position rather than the current selection. In that\n  // case the selection-derived target is wrong, so bail out and let the\n  // default behavior handle drops.\n  if (view.dragging) {\n    return null;\n  }\n\n  const blockInfo = getBlockInfoFromSelection(view.state);\n  const target = blockInfo.isBlockContainer\n    ? blockInfo.blockContent.node\n    : null;\n  if (\n    !target ||\n    target.type.name === \"paragraph\" ||\n    target.type.spec.content !== \"inline*\" ||\n    target.childCount > 0\n  ) {\n    return null;\n  }\n\n  const blockGroup = fragment.firstChild;\n  const blockContainer = blockGroup?.firstChild;\n  const leading = blockContainer?.firstChild;\n  if (\n    blockGroup?.type.name !== \"blockGroup\" ||\n    blockContainer?.type.name !== \"blockContainer\" ||\n    leading?.type.name !== \"paragraph\"\n  ) {\n    return null;\n  }\n\n  const retyped = target.type.create(target.attrs, leading.content);\n  const newBlockContainer = blockContainer.copy(\n    blockContainer.content.replaceChild(0, retyped),\n  );\n  const newBlockGroup = blockGroup.copy(\n    blockGroup.content.replaceChild(0, newBlockContainer),\n  );\n  return new Slice(\n    fragment.replaceChild(0, newBlockGroup),\n    slice.openStart,\n    slice.openEnd,\n  );\n}\n\n/**\n * Used in `transformPasted` to check if the fix there should be applied, i.e.\n * if the pasted fragment should be wrapped in a `blockContainer` node. This\n * will explicitly tell ProseMirror to treat it as a separate block.\n */\nfunction shouldApplyFix(fragment: Fragment, view: EditorView) {\n  const nodeHasSingleChild = fragment.childCount === 1;\n  const nodeHasInlineContent =\n    fragment.firstChild?.type.spec.content === \"inline*\";\n  const nodeHasTableContent =\n    fragment.firstChild?.type.spec.content === \"tableRow+\";\n\n  if (nodeHasSingleChild) {\n    if (nodeHasInlineContent) {\n      // Case when we paste a single node with inline content, e.g. a paragraph\n      // or heading. We want to insert the content in-line for better UX instead\n      // of a separate block, so we return false.\n      return false;\n    }\n\n    if (nodeHasTableContent) {\n      // Not ideal that we check selection here, as `transformPasted` is called\n      // for both paste and drop events. Drop events can potentially cause\n      // issues as they don't always happen at the current selection.\n      const blockInfo = getBlockInfoFromSelection(view.state);\n      if (blockInfo.isBlockContainer) {\n        const selectedBlockHasTableContent =\n          blockInfo.blockContent.node.type.spec.content === \"tableRow+\";\n\n        // Case for when we paste a single node with table content, i.e. a\n        // table. Normally, we return true as we want to ensure the table is\n        // inserted as a separate block. However, if the selection is in an\n        // existing table, we return false, as we want the content of the pasted\n        // table to be added to the existing one for better UX.\n        return !selectedBlockHasTableContent;\n      }\n    }\n  }\n\n  return true;\n}\n","import {\n  createDocument,\n  EditorOptions,\n  FocusPosition,\n  getSchema,\n  Editor as TiptapEditor,\n} from \"@tiptap/core\";\nimport { type Command, type Transaction } from \"@tiptap/pm/state\";\nimport { Node, Schema } from \"prosemirror-model\";\nimport type { BlocksChanged } from \"../api/getBlocksChangedByTransaction.js\";\nimport { blockToNode } from \"../api/nodeConversions/blockToNode.js\";\nimport {\n  Block,\n  BlockNoteSchema,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  PartialBlock,\n} from \"../blocks/index.js\";\nimport type { CollaborationOptions } from \"../extensions/Collaboration/Collaboration.js\";\nimport {\n  BlockChangeExtension,\n  DropCursorOptions,\n} from \"../extensions/index.js\";\nimport { UniqueID } from \"../extensions/tiptap-extensions/UniqueID/UniqueID.js\";\nimport type { Dictionary } from \"../i18n/dictionary.js\";\nimport { en } from \"../i18n/locales/index.js\";\nimport type {\n  BlockIdentifier,\n  BlockNoteDOMAttributes,\n  BlockSchema,\n  BlockSpecs,\n  CustomBlockNoteSchema,\n  InlineContentSchema,\n  InlineContentSpecs,\n  PartialInlineContent,\n  Styles,\n  StyleSchema,\n  StyleSpecs,\n} from \"../schema/index.js\";\nimport \"../style.css\";\nimport { mergeCSSClasses } from \"../util/browser.js\";\nimport { EventEmitter } from \"../util/EventEmitter.js\";\nimport type { NoInfer } from \"../util/typescript.js\";\nimport { ExtensionFactoryInstance } from \"./BlockNoteExtension.js\";\nimport type { TextCursorPosition } from \"./cursorPositionTypes.js\";\nimport {\n  BlockManager,\n  EventManager,\n  ExportManager,\n  ExtensionManager,\n  SelectionManager,\n  StateManager,\n  StyleManager,\n} from \"./managers/index.js\";\nimport type { Selection } from \"./selectionTypes.js\";\nimport { transformPasted } from \"./transformPasted.js\";\n\nexport type BlockCache<\n  BSchema extends BlockSchema = any,\n  ISchema extends InlineContentSchema = any,\n  SSchema extends StyleSchema = any,\n> = WeakMap<Node, Block<BSchema, ISchema, SSchema>>;\n\nexport interface BlockNoteEditorOptions<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n> {\n  /**\n   * Whether changes to blocks (like indentation, creating lists, changing headings) should be animated or not. Defaults to `true`.\n   *\n   * @default true\n   */\n  animations?: boolean;\n\n  /**\n   * Whether the editor should be focused automatically when it's created.\n   *\n   * @default false\n   */\n  autofocus?: FocusPosition;\n\n  /**\n   * When enabled, allows for collaboration between multiple users.\n   * See [Real-time Collaboration](https://www.blocknotejs.org/docs/advanced/real-time-collaboration) for more info.\n   */\n  collaboration?: CollaborationOptions;\n\n  /**\n   * Use default BlockNote font and reset the styles of <p> <li> <h1> elements etc., that are used in BlockNote.\n   *\n   * @default true\n   */\n  defaultStyles?: boolean;\n\n  /**\n   * A dictionary object containing translations for the editor.\n   *\n   * See [Localization / i18n](https://www.blocknotejs.org/docs/advanced/localization) for more info.\n   *\n   * @remarks `Dictionary` is a type that contains all the translations for the editor.\n   */\n  dictionary?: Dictionary & Record<string, any>;\n\n  /**\n   * Disable internal extensions (based on keys / extension name)\n   *\n   * @note Advanced\n   */\n  disableExtensions?: string[];\n\n  /**\n   * An object containing attributes that should be added to HTML elements of the editor.\n   *\n   * See [Adding DOM Attributes](https://www.blocknotejs.org/docs/theming#adding-dom-attributes) for more info.\n   *\n   * @example { editor: { class: \"my-editor-class\" } }\n   * @remarks `Record<string, Record<string, string>>`\n   */\n  domAttributes?: Partial<BlockNoteDOMAttributes>;\n\n  /**\n   * Options for configuring the drop cursor behavior when dragging and dropping blocks.\n   * Allows customization of cursor appearance and drop position computation through hooks.\n   * @remarks `DropCursorOptions`\n   */\n  dropCursor?: DropCursorOptions;\n\n  /**\n   * The content that should be in the editor when it's created, represented as an array of {@link PartialBlock} objects.\n   *\n   * See [Partial Blocks](https://www.blocknotejs.org/docs/editor-api/manipulating-blocks#partial-blocks) for more info.\n   *\n   * @remarks `PartialBlock[]`\n   */\n  initialContent?: PartialBlock<\n    NoInfer<BSchema>,\n    NoInfer<ISchema>,\n    NoInfer<SSchema>\n  >[];\n\n  /**\n   * Options for configuring how links behave in the editor.\n   */\n  links?: {\n    /**\n     * HTML attributes to add to rendered link elements.\n     *\n     * @default {}\n     * @example { class: \"my-link-class\", target: \"_blank\" }\n     */\n    HTMLAttributes?: Record<string, any>;\n    /**\n     * Custom handler invoked when a link is clicked. If left `undefined`,\n     * links are opened in a new window on click. If provided, the default\n     * open-on-click behavior is disabled and this function is called instead.\n     *\n     * Return `false` to let ProseMirror continue handling the click event.\n     * Returning `true` or nothing (the default) marks the event as handled.\n     */\n    onClick?: (\n      event: MouseEvent,\n      editor: BlockNoteEditor<any, any, any>,\n    ) => boolean | void;\n    /**\n     * Callback that decides whether a given `href` is a valid link. Applied at\n     * every gate where a link enters the document: HTML import, HTML export,\n     * paste, and autolink. Useful for supporting additional URI schemes (e.g.\n     * `vscode:`, `myapp:`) or tightening the default allowlist.\n     *\n     * Defaults to `isAllowedUri`, which allows\n     * `http|https|ftp|ftps|mailto|tel|callto|sms|cid|xmpp`. Import\n     * `isAllowedUri` from `@blocknote/core` to layer on top of the default.\n     *\n     * @example\n     * ```ts\n     * import { isAllowedUri } from \"@blocknote/core\";\n     *\n     * BlockNoteEditor.create({\n     *   links: {\n     *     isValidLink: (href) =>\n     *       isAllowedUri(href) || href.startsWith(\"myapp:\"),\n     *   },\n     * });\n     * ```\n     */\n    isValidLink?: (href: string) => boolean;\n  };\n\n  /**\n   * @deprecated, provide placeholders via dictionary instead\n   * @internal\n   */\n  placeholders?: Record<\n    string | \"default\" | \"emptyDocument\",\n    string | undefined\n  >;\n\n  /**\n   * Custom paste handler that can be used to override the default paste behavior.\n   *\n   * See [Paste Handling](https://www.blocknotejs.org/docs/advanced/paste-handling) for more info.\n   *\n   * @remarks `PasteHandler`\n   * @returns The function should return `true` if the paste event was handled, otherwise it should return `false` if it should be canceled or `undefined` if it should be handled by another handler.\n   *\n   * @example\n   * ```ts\n   * pasteHandler: ({ defaultPasteHandler }) => {\n   *   return defaultPasteHandler({ pasteBehavior: \"prefer-html\" });\n   * }\n   * ```\n   */\n  pasteHandler?: (context: {\n    event: ClipboardEvent;\n    editor: BlockNoteEditor<\n      NoInfer<BSchema>,\n      NoInfer<ISchema>,\n      NoInfer<SSchema>\n    >;\n    /**\n     * The default paste handler\n     * @param context The context object\n     * @returns Whether the paste event was handled or not\n     */\n    defaultPasteHandler: (context?: {\n      /**\n       * Whether to prioritize Markdown content in `text/plain` over `text/html` when pasting from the clipboard.\n       * @default true\n       */\n      prioritizeMarkdownOverHTML?: boolean;\n      /**\n       * Whether to parse `text/plain` content from the clipboard as Markdown content.\n       * @default true\n       */\n      plainTextAsMarkdown?: boolean;\n    }) => boolean | undefined;\n  }) => boolean | undefined;\n\n  /**\n   * Resolve a URL of a file block to one that can be displayed or downloaded. This can be used for creating authenticated URL or\n   * implementing custom protocols / schemes\n   * @returns The URL that's\n   */\n  resolveFileUrl?: (url: string) => Promise<string>;\n\n  /**\n   * The schema of the editor. The schema defines which Blocks, InlineContent, and Styles are available in the editor.\n   *\n   * See [Custom Schemas](https://www.blocknotejs.org/docs/custom-schemas) for more info.\n   * @remarks `BlockNoteSchema`\n   */\n  schema: CustomBlockNoteSchema<BSchema, ISchema, SSchema>;\n\n  /**\n   * A flag indicating whether to set an HTML ID for every block\n   *\n   * When set to `true`, on each block an id attribute will be set with the block id\n   * Otherwise, the HTML ID attribute will not be set.\n   *\n   * (note that the id is always set on the `data-id` attribute)\n   */\n  setIdAttribute?: boolean;\n\n  /**\n   * Determines behavior when pressing Tab (or Shift-Tab) while multiple blocks are selected and a toolbar is open.\n   * - `\"prefer-navigate-ui\"`: Changes focus to the toolbar. User must press Escape to close toolbar before indenting blocks. Better for keyboard accessibility.\n   * - `\"prefer-indent\"`: Always indents selected blocks, regardless of toolbar state. Keyboard navigation of toolbars not possible.\n   * @default \"prefer-navigate-ui\"\n   */\n  tabBehavior?: \"prefer-navigate-ui\" | \"prefer-indent\";\n\n  /**\n   * Allows enabling / disabling features of tables.\n   *\n   * See [Tables](https://www.blocknotejs.org/docs/editor-basics/document-structure#tables) for more info.\n   *\n   * @remarks `TableConfig`\n   */\n  tables?: {\n    /**\n     * Whether to allow splitting and merging cells within a table.\n     *\n     * @default false\n     */\n    splitCells?: boolean;\n    /**\n     * Whether to allow changing the background color of cells.\n     *\n     * @default false\n     */\n    cellBackgroundColor?: boolean;\n    /**\n     * Whether to allow changing the text color of cells.\n     *\n     * @default false\n     */\n    cellTextColor?: boolean;\n    /**\n     * Whether to allow changing cells into headers.\n     *\n     * @default false\n     */\n    headers?: boolean;\n  };\n\n  /**\n   * When the editor document doesn't end in an empty paragraph block, this option causes the editor to render an element simulating one.\n   * When clicked by the user, it gets turned into an actual block at the end of the document. This element is not shown when the option is `false`.\n   *\n   * @default true\n   */\n  trailingBlock?: boolean;\n\n  /**\n   * The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).\n   * This method should set when creating the editor as this is application-specific.\n   *\n   * `undefined` means the application doesn't support file uploads.\n   *\n   * @param file The file that should be uploaded.\n   * @returns The URL of the uploaded file OR an object containing props that should be set on the file block (such as an id)\n   * @remarks `(file: File) => Promise<UploadFileResult>`\n   */\n  uploadFile?: (\n    file: File,\n    blockId?: string,\n  ) => Promise<string | Record<string, any>>;\n\n  /**\n   * additional tiptap options, undocumented\n   * @internal\n   */\n  _tiptapOptions?: Partial<EditorOptions>;\n\n  /**\n   * Register extensions to the editor.\n   *\n   * See [Extensions](/docs/features/extensions) for more info.\n   *\n   * @remarks `ExtensionFactory[]`\n   */\n  extensions?: Array<ExtensionFactoryInstance>;\n}\n\nconst blockNoteTipTapOptions = {\n  enableInputRules: true,\n  enablePasteRules: true,\n  enableCoreExtensions: false,\n};\n\nexport class BlockNoteEditor<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> extends EventEmitter<{\n  create: void;\n}> {\n  /**\n   * The underlying prosemirror schema\n   */\n  public readonly pmSchema: Schema;\n\n  public readonly _tiptapEditor: TiptapEditor & {\n    contentComponent: any;\n  };\n\n  /**\n   * Used by React to store a reference to an `ElementRenderer` helper utility to make sure we can render React elements\n   * in the correct context (used by `ReactRenderUtil`)\n   */\n  public elementRenderer: ((node: any, container: HTMLElement) => void) | null =\n    null;\n\n  /**\n   * Cache of all blocks. This makes sure we don't have to \"recompute\" blocks if underlying Prosemirror Nodes haven't changed.\n   * This is especially useful when we want to keep track of the same block across multiple operations,\n   * with this cache, blocks stay the same object reference (referential equality with ===).\n   */\n  public blockCache: BlockCache = new WeakMap();\n\n  /**\n   * The dictionary contains translations for the editor.\n   */\n  public readonly dictionary: Dictionary & Record<string, any>;\n\n  /**\n   * The schema of the editor. The schema defines which Blocks, InlineContent, and Styles are available in the editor.\n   */\n  public readonly schema: BlockNoteSchema<BSchema, ISchema, SSchema>;\n\n  public readonly blockImplementations: BlockSpecs;\n  public readonly inlineContentImplementations: InlineContentSpecs;\n  public readonly styleImplementations: StyleSpecs;\n\n  /**\n   * The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).\n   * This method should set when creating the editor as this is application-specific.\n   *\n   * `undefined` means the application doesn't support file uploads.\n   *\n   * @param file The file that should be uploaded.\n   * @returns The URL of the uploaded file OR an object containing props that should be set on the file block (such as an id)\n   */\n  public readonly uploadFile:\n    | ((file: File, blockId?: string) => Promise<string | Record<string, any>>)\n    | undefined;\n\n  private onUploadStartCallbacks: ((blockId?: string) => void)[] = [];\n  private onUploadEndCallbacks: ((blockId?: string) => void)[] = [];\n\n  public readonly resolveFileUrl?: (url: string) => Promise<string>;\n  /**\n   * Editor settings\n   */\n  public readonly settings: {\n    tables: {\n      splitCells: boolean;\n      cellBackgroundColor: boolean;\n      cellTextColor: boolean;\n      headers: boolean;\n    };\n  };\n  public static create<\n    Options extends Partial<BlockNoteEditorOptions<any, any, any>> | undefined,\n  >(\n    options?: Options,\n  ): Options extends {\n    schema: CustomBlockNoteSchema<infer BSchema, infer ISchema, infer SSchema>;\n  }\n    ? BlockNoteEditor<BSchema, ISchema, SSchema>\n    : BlockNoteEditor<\n        DefaultBlockSchema,\n        DefaultInlineContentSchema,\n        DefaultStyleSchema\n      > {\n    return new BlockNoteEditor(options ?? {}) as any;\n  }\n\n  protected constructor(\n    protected readonly options: Partial<\n      BlockNoteEditorOptions<BSchema, ISchema, SSchema>\n    >,\n  ) {\n    super();\n\n    this.dictionary = options.dictionary || en;\n    this.settings = {\n      tables: {\n        splitCells: options?.tables?.splitCells ?? false,\n        cellBackgroundColor: options?.tables?.cellBackgroundColor ?? false,\n        cellTextColor: options?.tables?.cellTextColor ?? false,\n        headers: options?.tables?.headers ?? false,\n      },\n    };\n\n    // apply defaults\n    const newOptions = {\n      defaultStyles: true,\n      schema:\n        options.schema ||\n        (BlockNoteSchema.create() as unknown as CustomBlockNoteSchema<\n          BSchema,\n          ISchema,\n          SSchema\n        >),\n      ...options,\n      placeholders: {\n        ...this.dictionary.placeholders,\n        ...options.placeholders,\n      },\n    };\n\n    this.schema = newOptions.schema;\n    this.blockImplementations = newOptions.schema.blockSpecs;\n    this.inlineContentImplementations = newOptions.schema.inlineContentSpecs;\n    this.styleImplementations = newOptions.schema.styleSpecs;\n\n    // TODO this should just be an extension\n    if (newOptions.uploadFile) {\n      const uploadFile = newOptions.uploadFile;\n      this.uploadFile = async (file, blockId) => {\n        this.onUploadStartCallbacks.forEach((callback) =>\n          callback.apply(this, [blockId]),\n        );\n        try {\n          return await uploadFile(file, blockId);\n        } finally {\n          this.onUploadEndCallbacks.forEach((callback) =>\n            callback.apply(this, [blockId]),\n          );\n        }\n      };\n    }\n\n    this.resolveFileUrl = newOptions.resolveFileUrl;\n\n    this._eventManager = new EventManager(this as any);\n    this._extensionManager = new ExtensionManager(this, newOptions);\n\n    const tiptapExtensions = this._extensionManager.getTiptapExtensions();\n\n    const collaborationEnabled =\n      this._extensionManager.hasExtension(\"ySync\") ||\n      this._extensionManager.hasExtension(\"liveblocksExtension\");\n\n    if (collaborationEnabled && newOptions.initialContent) {\n      // eslint-disable-next-line no-console\n      console.warn(\n        \"When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider\",\n      );\n    }\n\n    const tiptapOptions: EditorOptions = {\n      ...blockNoteTipTapOptions,\n      ...newOptions._tiptapOptions,\n      element: null,\n      autofocus: newOptions.autofocus ?? false,\n      extensions: tiptapExtensions,\n      editorProps: {\n        ...newOptions._tiptapOptions?.editorProps,\n        attributes: {\n          // As of TipTap v2.5.0 the tabIndex is removed when the editor is not\n          // editable, so you can't focus it. We want to revert this as we have\n          // UI behaviour that relies on it.\n          tabIndex: \"0\",\n          ...newOptions._tiptapOptions?.editorProps?.attributes,\n          ...newOptions.domAttributes?.editor,\n          class: mergeCSSClasses(\n            \"bn-editor\",\n            newOptions.defaultStyles ? \"bn-default-styles\" : \"\",\n            newOptions.domAttributes?.editor?.class || \"\",\n          ),\n        },\n        transformPasted,\n      },\n    } as any;\n\n    try {\n      const initialContent =\n        newOptions.initialContent ||\n        (collaborationEnabled\n          ? [\n              {\n                type: \"paragraph\",\n                id: \"initialBlockId\",\n              },\n            ]\n          : [\n              {\n                type: \"paragraph\",\n                id: UniqueID.options.generateID(),\n              },\n            ]);\n\n      if (!Array.isArray(initialContent) || initialContent.length === 0) {\n        throw new Error(\n          \"initialContent must be a non-empty array of blocks, received: \" +\n            initialContent,\n        );\n      }\n      const schema = getSchema(tiptapOptions.extensions!);\n      const pmNodes = initialContent.map((b) =>\n        blockToNode(b, schema, this.schema.styleSchema).toJSON(),\n      );\n      const doc = createDocument(\n        {\n          type: \"doc\",\n          content: [\n            {\n              type: \"blockGroup\",\n              content: pmNodes,\n            },\n          ],\n        },\n        schema,\n        tiptapOptions.parseOptions,\n      );\n\n      this._tiptapEditor = new TiptapEditor({\n        ...tiptapOptions,\n        content: doc.toJSON(),\n      }) as any;\n      this.pmSchema = this._tiptapEditor.schema;\n    } catch (e) {\n      throw new Error(\n        \"Error creating document from blocks passed as `initialContent`\",\n        { cause: e },\n      );\n    }\n\n    // When y-prosemirror creates an empty document, the `blockContainer` node is created with an `id` of `null`.\n    // This causes the unique id extension to generate a new id for the initial block, which is not what we want\n    // Since it will be randomly generated & cause there to be more updates to the ydoc\n    // This is a hack to make it so that anytime `schema.doc.createAndFill` is called, the initial block id is already set to \"initialBlockId\"\n    let cache: Node | undefined = undefined;\n    const oldCreateAndFill = this.pmSchema.nodes.doc.createAndFill;\n    this.pmSchema.nodes.doc.createAndFill = (...args: any) => {\n      if (cache) {\n        return cache;\n      }\n      const ret = oldCreateAndFill.apply(this.pmSchema.nodes.doc, args)!;\n\n      // create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)\n      const jsonNode = JSON.parse(JSON.stringify(ret.toJSON()));\n      jsonNode.content[0].content[0].attrs.id = \"initialBlockId\";\n\n      cache = Node.fromJSON(this.pmSchema, jsonNode);\n      return cache;\n    };\n    this.pmSchema.cached.blockNoteEditor = this;\n\n    this._tiptapEditor.on(\"mount\", () => {\n      this.headless = false;\n    });\n    this._tiptapEditor.on(\"unmount\", () => {\n      this.headless = true;\n    });\n\n    // Initialize managers\n    this._blockManager = new BlockManager(this as any);\n\n    this._exportManager = new ExportManager(this as any);\n    this._selectionManager = new SelectionManager(this as any);\n    this._stateManager = new StateManager(this as any);\n    this._styleManager = new StyleManager(this as any);\n\n    this.emit(\"create\");\n  }\n\n  // Manager instances\n  private readonly _blockManager: BlockManager<any, any, any>;\n  private readonly _eventManager: EventManager<any, any, any>;\n  private readonly _exportManager: ExportManager<any, any, any>;\n  private readonly _extensionManager: ExtensionManager;\n  private readonly _selectionManager: SelectionManager<any, any, any>;\n  private readonly _stateManager: StateManager;\n  private readonly _styleManager: StyleManager<any, any, any>;\n\n  /**\n   * BlockNote extensions that are added to the editor, keyed by the extension key\n   */\n  public get extensions() {\n    return this._extensionManager.getExtensions();\n  }\n\n  /**\n   * Execute a prosemirror command. This is mostly for backwards compatibility with older code.\n   *\n   * @note You should prefer the {@link transact} method when possible, as it will automatically handle the dispatching of the transaction and work across blocknote transactions.\n   *\n   * @example\n   * ```ts\n   * editor.exec((state, dispatch, view) => {\n   *   dispatch(state.tr.insertText(\"Hello, world!\"));\n   * });\n   * ```\n   */\n  public exec(command: Command) {\n    return this._stateManager.exec(command);\n  }\n\n  /**\n   * Check if a command can be executed. A command should return `false` if it is not valid in the current state.\n   *\n   * @example\n   * ```ts\n   * if (editor.canExec(command)) {\n   *   // show button\n   * } else {\n   *   // hide button\n   * }\n   * ```\n   */\n  public canExec(command: Command): boolean {\n    return this._stateManager.canExec(command);\n  }\n\n  /**\n   * Execute a function within a \"blocknote transaction\".\n   * All changes to the editor within the transaction will be grouped together, so that\n   * we can dispatch them as a single operation (thus creating only a single undo step)\n   *\n   * @note There is no need to dispatch the transaction, as it will be automatically dispatched when the callback is complete.\n   *\n   * @example\n   * ```ts\n   * // All changes to the editor will be grouped together\n   * editor.transact((tr) => {\n   *   tr.insertText(\"Hello, world!\");\n   * // These two operations will be grouped together in a single undo step\n   *   editor.transact((tr) => {\n   *     tr.insertText(\"Hello, world!\");\n   *   });\n   * });\n   * ```\n   */\n  public transact<T>(\n    callback: (\n      /**\n       * The current active transaction, this will automatically be dispatched to the editor when the callback is complete\n       * If another `transact` call is made within the callback, it will be passed the same transaction as the parent call.\n       */\n      tr: Transaction,\n    ) => T,\n  ): T {\n    return this._stateManager.transact(callback);\n  }\n\n  /**\n   * Remove extension(s) from the editor\n   */\n  public unregisterExtension: ExtensionManager[\"unregisterExtension\"] = (\n    ...args: Parameters<ExtensionManager[\"unregisterExtension\"]>\n  ) => this._extensionManager.unregisterExtension(...args);\n\n  /**\n   * Register extension(s) to the editor\n   */\n  public registerExtension: ExtensionManager[\"registerExtension\"] = (\n    ...args: Parameters<ExtensionManager[\"registerExtension\"]>\n  ) => this._extensionManager.registerExtension(...args) as any;\n\n  /**\n   * Get an extension from the editor\n   */\n  public getExtension: ExtensionManager[\"getExtension\"] = ((\n    ...args: Parameters<ExtensionManager[\"getExtension\"]>\n  ) => this._extensionManager.getExtension(...args)) as any;\n\n  /**\n   * Mount the editor to a DOM element.\n   *\n   * @param element The DOM element to mount the editor's contenteditable into.\n   * @param options.portalTarget Where to mount `editor.portalElement` — the\n   *   container that floating UI (toolbars, menus, etc) portals into. When\n   *   omitted, defaults to `element.parentElement` (which is the editor's\n   *   `bn-container` in typical React usage), or to `document.body` /\n   *   the surrounding shadow root when no parent is available.\n   *\n   * @warning Not needed to call manually when using React, use BlockNoteView to take care of mounting\n   */\n  public mount = (\n    element: HTMLElement,\n    options?: { portalTarget?: HTMLElement | null },\n  ) => {\n    const root = element.getRootNode();\n    const isInShadowRoot =\n      typeof ShadowRoot !== \"undefined\" && root instanceof ShadowRoot;\n    const target =\n      options?.portalTarget ??\n      element.parentElement ??\n      (isInShadowRoot ? (root as ShadowRoot) : document.body);\n    target.appendChild(this.portalElement);\n    this._tiptapEditor.mount({ mount: element });\n  };\n\n  /**\n   * Unmount the editor from the DOM element it is bound to\n   */\n  public unmount = () => {\n    this.portalElement?.remove();\n    this._tiptapEditor.unmount();\n  };\n\n  /**\n   * Get the underlying prosemirror state\n   * @note Prefer using `editor.transact` to read the current editor state, as that will ensure the state is up to date\n   * @see https://prosemirror.net/docs/ref/#state.EditorState\n   */\n  public get prosemirrorState() {\n    return this._stateManager.prosemirrorState;\n  }\n\n  /**\n   * Get the underlying prosemirror view\n   * @see https://prosemirror.net/docs/ref/#view.EditorView\n   */\n  public get prosemirrorView() {\n    return this._stateManager.prosemirrorView;\n  }\n\n  public get domElement() {\n    if (this.headless) {\n      return undefined;\n    }\n    return this.prosemirrorView?.dom as HTMLDivElement | undefined;\n  }\n\n  private _portalElement: HTMLElement | undefined;\n\n  /**\n   * The portal container element at `document.body` used by floating UI\n   * elements (menus, toolbars) to escape overflow:hidden ancestors.\n   * Set by BlockNoteView; undefined in headless mode.\n   */\n  public get portalElement() {\n    if (typeof document === \"undefined\") {\n      throw new Error(\n        \"Portal element accessed, but not available in headless mode\",\n      );\n    }\n    if (!this._portalElement) {\n      this._portalElement = document.createElement(\"div\");\n    }\n    return this._portalElement;\n  }\n\n  /**\n   * Checks whether a DOM element belongs to this editor — either inside the\n   * editor's DOM tree or inside its portal container (used for floating UI\n   * elements like menus and toolbars).\n   */\n  public isWithinEditor = (element: Element): boolean => {\n    return !!(\n      this.domElement?.parentElement?.contains(element) ||\n      this.portalElement?.contains(element)\n    );\n  };\n\n  public isFocused() {\n    if (this.headless) {\n      return false;\n    }\n    return this.prosemirrorView?.hasFocus() || false;\n  }\n\n  public headless = true;\n\n  /**\n   * Focus on the editor\n   */\n  public focus() {\n    if (this.headless) {\n      return;\n    }\n    this.prosemirrorView.focus();\n  }\n\n  /**\n   * Blur the editor\n   */\n  public blur() {\n    if (this.headless) {\n      return;\n    }\n    this.domElement?.blur();\n  }\n\n  // TODO move to extension\n  public onUploadStart(callback: (blockId?: string) => void) {\n    this.onUploadStartCallbacks.push(callback);\n\n    return () => {\n      const index = this.onUploadStartCallbacks.indexOf(callback);\n      if (index > -1) {\n        this.onUploadStartCallbacks.splice(index, 1);\n      }\n    };\n  }\n\n  public onUploadEnd(callback: (blockId?: string) => void) {\n    this.onUploadEndCallbacks.push(callback);\n\n    return () => {\n      const index = this.onUploadEndCallbacks.indexOf(callback);\n      if (index > -1) {\n        this.onUploadEndCallbacks.splice(index, 1);\n      }\n    };\n  }\n\n  /**\n   * @deprecated, use `editor.document` instead\n   */\n  public get topLevelBlocks(): Block<BSchema, ISchema, SSchema>[] {\n    return this.document;\n  }\n\n  /**\n   * Gets a snapshot of all top-level (non-nested) blocks in the editor.\n   * @returns A snapshot of all top-level (non-nested) blocks in the editor.\n   */\n  public get document(): Block<BSchema, ISchema, SSchema>[] {\n    return this._blockManager.document;\n  }\n\n  /**\n   * Gets a snapshot of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block that should be\n   * retrieved.\n   * @returns The block that matches the identifier, or `undefined` if no\n   * matching block was found.\n   */\n  public getBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this._blockManager.getBlock(blockIdentifier);\n  }\n\n  /**\n   * Gets a snapshot of the previous sibling of an existing block from the\n   * editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * previous sibling should be retrieved.\n   * @returns The previous sibling of the block that matches the identifier.\n   * `undefined` if no matching block was found, or it's the first child/block\n   * in the document.\n   */\n  public getPrevBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this._blockManager.getPrevBlock(blockIdentifier);\n  }\n\n  /**\n   * Gets a snapshot of the next sibling of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * next sibling should be retrieved.\n   * @returns The next sibling of the block that matches the identifier.\n   * `undefined` if no matching block was found, or it's the last child/block in\n   * the document.\n   */\n  public getNextBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this._blockManager.getNextBlock(blockIdentifier);\n  }\n\n  /**\n   * Gets a snapshot of the parent of an existing block from the editor.\n   * @param blockIdentifier The identifier of an existing block for which the\n   * parent should be retrieved.\n   * @returns The parent of the block that matches the identifier. `undefined`\n   * if no matching block was found, or the block isn't nested.\n   */\n  public getParentBlock(\n    blockIdentifier: BlockIdentifier,\n  ): Block<BSchema, ISchema, SSchema> | undefined {\n    return this._blockManager.getParentBlock(blockIdentifier);\n  }\n\n  /**\n   * Traverses all blocks in the editor depth-first, and executes a callback for each.\n   * @param callback The callback to execute for each block. Returning `false` stops the traversal.\n   * @param reverse Whether the blocks should be traversed in reverse order.\n   */\n  public forEachBlock(\n    callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,\n    reverse = false,\n  ): void {\n    this._blockManager.forEachBlock(callback, reverse);\n  }\n\n  /**\n   * Executes a callback whenever the editor's contents change.\n   * @param callback The callback to execute.\n   *\n   * @deprecated use {@link BlockNoteEditor.onChange} instead\n   */\n  public onEditorContentChange(callback: () => void) {\n    this._tiptapEditor.on(\"update\", callback);\n  }\n\n  /**\n   * Executes a callback whenever the editor's selection changes.\n   * @param callback The callback to execute.\n   *\n   * @deprecated use `onSelectionChange` instead\n   */\n  public onEditorSelectionChange(callback: () => void) {\n    this._tiptapEditor.on(\"selectionUpdate\", callback);\n  }\n\n  /**\n   * Executes a callback before any change is applied to the editor, allowing you to cancel the change.\n   * @param callback The callback to execute.\n   * @returns A function to remove the callback.\n   */\n  public onBeforeChange(\n    callback: (context: {\n      getChanges: () => BlocksChanged<BSchema, ISchema, SSchema>;\n      tr: Transaction;\n    }) => boolean | void,\n  ): () => void {\n    return this._extensionManager\n      .getExtension(BlockChangeExtension)!\n      .subscribe(callback);\n  }\n\n  /**\n   * Gets a snapshot of the current text cursor position.\n   * @returns A snapshot of the current text cursor position.\n   */\n  public getTextCursorPosition(): TextCursorPosition<\n    BSchema,\n    ISchema,\n    SSchema\n  > {\n    return this._selectionManager.getTextCursorPosition();\n  }\n\n  /**\n   * Sets the text cursor position to the start or end of an existing block. Throws an error if the target block could\n   * not be found.\n   * @param targetBlock The identifier of an existing block that the text cursor should be moved to.\n   * @param placement Whether the text cursor should be placed at the start or end of the block.\n   */\n  public setTextCursorPosition(\n    targetBlock: BlockIdentifier,\n    placement: \"start\" | \"end\" = \"start\",\n  ) {\n    return this._selectionManager.setTextCursorPosition(targetBlock, placement);\n  }\n\n  /**\n   * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)\n   * that the selection spans across.\n   *\n   * If the selection starts / ends halfway through a block, the returned data will contain the entire block.\n   */\n  public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {\n    return this._selectionManager.getSelection();\n  }\n\n  /**\n   * Gets a snapshot of the current selection. This contains all blocks (included nested blocks)\n   * that the selection spans across.\n   *\n   * If the selection starts / ends halfway through a block, the returned block will be\n   * only the part of the block that is included in the selection.\n   */\n  public getSelectionCutBlocks(expandToWords = false) {\n    return this._selectionManager.getSelectionCutBlocks(expandToWords);\n  }\n\n  /**\n   * Sets the selection to a range of blocks.\n   * @param startBlock The identifier of the block that should be the start of the selection.\n   * @param endBlock The identifier of the block that should be the end of the selection.\n   */\n  public setSelection(startBlock: BlockIdentifier, endBlock: BlockIdentifier) {\n    return this._selectionManager.setSelection(startBlock, endBlock);\n  }\n\n  /**\n   * Checks if the editor is currently editable, or if it's locked.\n   * @returns True if the editor is editable, false otherwise.\n   */\n  public get isEditable(): boolean {\n    return this._stateManager.isEditable;\n  }\n\n  /**\n   * Makes the editor editable or locks it, depending on the argument passed.\n   * @param editable True to make the editor editable, or false to lock it.\n   */\n  public set isEditable(editable: boolean) {\n    this._stateManager.isEditable = editable;\n  }\n\n  /**\n   * Inserts new blocks into the editor. If a block's `id` is undefined, BlockNote generates one automatically. Throws an\n   * error if the reference block could not be found.\n   * @param blocksToInsert An array of partial blocks that should be inserted.\n   * @param referenceBlock An identifier for an existing block, at which the new blocks should be inserted.\n   * @param placement Whether the blocks should be inserted just before, just after, or nested inside the\n   * `referenceBlock`.\n   */\n  public insertBlocks(\n    blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],\n    referenceBlock: BlockIdentifier,\n    placement: \"before\" | \"after\" = \"before\",\n  ) {\n    return this._blockManager.insertBlocks(\n      blocksToInsert,\n      referenceBlock,\n      placement,\n    );\n  }\n\n  /**\n   * Updates an existing block in the editor. Since updatedBlock is a PartialBlock object, some fields might not be\n   * defined. These undefined fields are kept as-is from the existing block. Throws an error if the block to update could\n   * not be found.\n   * @param blockToUpdate The block that should be updated.\n   * @param update A partial block which defines how the existing block should be changed.\n   */\n  public updateBlock(\n    blockToUpdate: BlockIdentifier,\n    update: PartialBlock<BSchema, ISchema, SSchema>,\n  ) {\n    return this._blockManager.updateBlock(blockToUpdate, update);\n  }\n\n  /**\n   * Removes existing blocks from the editor. Throws an error if any of the blocks could not be found.\n   * @param blocksToRemove An array of identifiers for existing blocks that should be removed.\n   */\n  public removeBlocks(blocksToRemove: BlockIdentifier[]) {\n    return this._blockManager.removeBlocks(blocksToRemove);\n  }\n\n  /**\n   * Replaces existing blocks in the editor with new blocks. If the blocks that should be removed are not adjacent or\n   * are at different nesting levels, `blocksToInsert` will be inserted at the position of the first block in\n   * `blocksToRemove`. Throws an error if any of the blocks to remove could not be found.\n   * @param blocksToRemove An array of blocks that should be replaced.\n   * @param blocksToInsert An array of partial blocks to replace the old ones with.\n   */\n  public replaceBlocks(\n    blocksToRemove: BlockIdentifier[],\n    blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],\n  ) {\n    return this._blockManager.replaceBlocks(blocksToRemove, blocksToInsert);\n  }\n\n  /**\n   * Undo the last action.\n   */\n  public undo(): boolean {\n    return this._stateManager.undo();\n  }\n\n  /**\n   * Redo the last action.\n   */\n  public redo(): boolean {\n    return this._stateManager.redo();\n  }\n\n  /**\n   * Insert a piece of content at the current cursor position.\n   *\n   * @param content can be a string, or array of partial inline content elements\n   */\n  public insertInlineContent(\n    content: PartialInlineContent<ISchema, SSchema>,\n    { updateSelection = false }: { updateSelection?: boolean } = {},\n  ) {\n    this._styleManager.insertInlineContent(content, { updateSelection });\n  }\n\n  /**\n   * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.\n   */\n  public getActiveStyles(): Styles<SSchema> {\n    return this._styleManager.getActiveStyles();\n  }\n\n  /**\n   * Adds styles to the currently selected content.\n   * @param styles The styles to add.\n   */\n  public addStyles(styles: Styles<SSchema>) {\n    this._styleManager.addStyles(styles);\n  }\n\n  /**\n   * Removes styles from the currently selected content.\n   * @param styles The styles to remove.\n   */\n  public removeStyles(styles: Styles<SSchema>) {\n    this._styleManager.removeStyles(styles);\n  }\n\n  /**\n   * Toggles styles on the currently selected content.\n   * @param styles The styles to toggle.\n   */\n  public toggleStyles(styles: Styles<SSchema>) {\n    this._styleManager.toggleStyles(styles);\n  }\n\n  /**\n   * Gets the currently selected text.\n   */\n  public getSelectedText() {\n    return this._styleManager.getSelectedText();\n  }\n\n  /**\n   * Gets the URL of the last link in the current selection, or `undefined` if there are no links in the selection.\n   */\n  public getSelectedLinkUrl() {\n    return this._styleManager.getSelectedLinkUrl();\n  }\n\n  /**\n   * Creates a new link to replace the selected content.\n   * @param url The link URL.\n   * @param text The text to display the link with.\n   */\n  public createLink(url: string, text?: string) {\n    this._styleManager.createLink(url, text);\n  }\n\n  /**\n   * Find the link mark and its range at the given position.\n   * Returns undefined if there is no link at that position.\n   */\n  public getLinkMarkAtPos(pos: number) {\n    return this._styleManager.getLinkMarkAtPos(pos);\n  }\n\n  /**\n   * Updates the link at the given position with a new URL and text.\n   * @param url The new link URL.\n   * @param text The new text to display.\n   * @param position The position inside the link to edit. Defaults to the current selection anchor.\n   */\n  public editLink(url: string, text: string, position?: number) {\n    this._styleManager.editLink(url, text, position);\n  }\n\n  /**\n   * Removes the link at the given position, keeping the text.\n   * @param position The position inside the link to remove. Defaults to the current selection anchor.\n   */\n  public deleteLink(position?: number) {\n    this._styleManager.deleteLink(position);\n  }\n\n  /**\n   * Checks if the block containing the text cursor can be nested.\n   */\n  public canNestBlock() {\n    return this._blockManager.canNestBlock();\n  }\n\n  /**\n   * Nests the block containing the text cursor into the block above it.\n   */\n  public nestBlock() {\n    this._blockManager.nestBlock();\n  }\n\n  /**\n   * Checks if the block containing the text cursor is nested.\n   */\n  public canUnnestBlock() {\n    return this._blockManager.canUnnestBlock();\n  }\n\n  /**\n   * Lifts the block containing the text cursor out of its parent.\n   */\n  public unnestBlock() {\n    this._blockManager.unnestBlock();\n  }\n\n  /**\n   * Moves the selected blocks up. If the previous block has children, moves\n   * them to the end of its children. If there is no previous block, but the\n   * current blocks share a common parent, moves them out of & before it. If a\n   * `blockIdentifier` is provided, that block is moved instead of the\n   * selection, and the selection is left unchanged.\n   */\n  public moveBlocksUp(blockIdentifier?: BlockIdentifier) {\n    return this._blockManager.moveBlocksUp(blockIdentifier);\n  }\n\n  /**\n   * Moves the selected blocks down. If the next block has children, moves\n   * them to the start of its children. If there is no next block, but the\n   * current blocks share a common parent, moves them out of & after it. If a\n   * `blockIdentifier` is provided, that block is moved instead of the\n   * selection, and the selection is left unchanged.\n   */\n  public moveBlocksDown(blockIdentifier?: BlockIdentifier) {\n    return this._blockManager.moveBlocksDown(blockIdentifier);\n  }\n\n  /**\n   * Exports blocks into a simplified HTML string. To better conform to HTML standards, children of blocks which aren't list\n   * items are un-nested in the output HTML.\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public blocksToHTMLLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,\n  ): string {\n    return this._exportManager.blocksToHTMLLossy(blocks);\n  }\n\n  /**\n   * Serializes blocks into an HTML string in the format that would normally be rendered by the editor.\n   *\n   * Use this method if you want to server-side render HTML (for example, a blog post that has been edited in BlockNote)\n   * and serve it to users without loading the editor on the client (i.e.: displaying the blog post)\n   *\n   * @param blocks An array of blocks that should be serialized into HTML.\n   * @returns The blocks, serialized as an HTML string.\n   */\n  public blocksToFullHTML(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,\n  ): string {\n    return this._exportManager.blocksToFullHTML(blocks);\n  }\n\n  /**\n   * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and\n   * `InlineNode` objects from any HTML inline elements, though not all element types are recognized. If BlockNote\n   * doesn't recognize an HTML element's tag, it will parse it as a paragraph or plain text.\n   * @param html The HTML string to parse blocks from.\n   * @returns The blocks parsed from the HTML string.\n   */\n  public tryParseHTMLToBlocks(\n    html: string,\n  ): Block<BSchema, ISchema, SSchema>[] {\n    return this._exportManager.tryParseHTMLToBlocks(html);\n  }\n\n  /**\n   * Serializes blocks into a Markdown string. The output is simplified as Markdown does not support all features of\n   * BlockNote - children of blocks which aren't list items are un-nested and certain styles are removed.\n   * @param blocks An array of blocks that should be serialized into Markdown.\n   * @returns The blocks, serialized as a Markdown string.\n   */\n  public blocksToMarkdownLossy(\n    blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,\n  ): string {\n    return this._exportManager.blocksToMarkdownLossy(blocks);\n  }\n\n  /**\n   * Creates a list of blocks from a Markdown string. Tries to create `Block` and `InlineNode` objects based on\n   * Markdown syntax, though not all symbols are recognized. If BlockNote doesn't recognize a symbol, it will parse it\n   * as text.\n   * @param markdown The Markdown string to parse blocks from.\n   * @returns The blocks parsed from the Markdown string.\n   */\n  public tryParseMarkdownToBlocks(\n    markdown: string,\n  ): Block<BSchema, ISchema, SSchema>[] {\n    return this._exportManager.tryParseMarkdownToBlocks(markdown);\n  }\n\n  /**\n   * A callback function that runs whenever the editor's contents change.\n   *\n   * @param callback The callback to execute.\n   * @returns A function to remove the callback.\n   */\n  public onChange(\n    callback: (\n      editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n      context: {\n        /**\n         * Returns the blocks that were inserted, updated, or deleted by the change that occurred.\n         */\n        getChanges(): BlocksChanged<BSchema, ISchema, SSchema>;\n      },\n    ) => void,\n    /**\n     * If true, the callback will be triggered when the changes are caused by a remote user\n     * @default true\n     */\n    includeUpdatesFromRemote?: boolean,\n  ) {\n    return this._eventManager.onChange(callback, includeUpdatesFromRemote);\n  }\n\n  /**\n   * A callback function that runs whenever the text cursor position or selection changes.\n   *\n   * @param callback The callback to execute.\n   * @returns A function to remove the callback.\n   */\n  public onSelectionChange(\n    callback: (editor: BlockNoteEditor<BSchema, ISchema, SSchema>) => void,\n    includeSelectionChangedByRemote?: boolean,\n  ) {\n    return this._eventManager.onSelectionChange(\n      callback,\n      includeSelectionChangedByRemote,\n    );\n  }\n\n  /**\n   * A callback function that runs when the editor has been mounted.\n   *\n   * This can be useful for plugins to initialize themselves after the editor has been mounted.\n   *\n   * @param callback The callback to execute.\n   * @returns A function to remove the callback.\n   */\n  public onMount(\n    callback: (ctx: {\n      editor: BlockNoteEditor<BSchema, ISchema, SSchema>;\n    }) => void,\n  ) {\n    return this._eventManager.onMount(callback);\n  }\n\n  /**\n   * A callback function that runs when the editor has been unmounted.\n   *\n   * This can be useful for plugins to clean up themselves after the editor has been unmounted.\n   *\n   * @param callback The callback to execute.\n   * @returns A function to remove the callback.\n   */\n  public onUnmount(\n    callback: (ctx: {\n      editor: BlockNoteEditor<BSchema, ISchema, SSchema>;\n    }) => void,\n  ) {\n    return this._eventManager.onUnmount(callback);\n  }\n\n  /**\n   * Gets the bounding box of the current selection.\n   * @returns The bounding box of the current selection.\n   */\n  public getSelectionBoundingBox() {\n    return this._selectionManager.getSelectionBoundingBox();\n  }\n\n  public get isEmpty() {\n    const doc = this.document;\n    // Note: only works for paragraphs as default blocks (but for now this is default in blocknote)\n    // checking prosemirror directly might be faster\n    return (\n      doc.length === 0 ||\n      (doc.length === 1 &&\n        doc[0].type === \"paragraph\" &&\n        (doc[0].content as any).length === 0)\n    );\n  }\n\n  /**\n   * Paste HTML into the editor. Defaults to converting HTML to BlockNote HTML.\n   * @param html The HTML to paste.\n   * @param raw Whether to paste the HTML as is, or to convert it to BlockNote HTML.\n   */\n  public pasteHTML(html: string, raw = false) {\n    this._exportManager.pasteHTML(html, raw);\n  }\n\n  /**\n   * Paste text into the editor. Defaults to interpreting text as markdown.\n   * @param text The text to paste.\n   */\n  public pasteText(text: string) {\n    return this._exportManager.pasteText(text);\n  }\n\n  /**\n   * Paste markdown into the editor.\n   * @param markdown The markdown to paste.\n   */\n  public pasteMarkdown(markdown: string) {\n    return this._exportManager.pasteMarkdown(markdown);\n  }\n}\n","import { BlockNoteSchema } from \"../blocks/BlockNoteSchema.js\";\nimport { COLORS_DEFAULT } from \"../editor/defaultColors.js\";\nimport {\n  BlockFromConfig,\n  BlockSchema,\n  InlineContent,\n  InlineContentSchema,\n  StyleSchema,\n  StyledText,\n  Styles,\n} from \"../schema/index.js\";\n\nimport type {\n  BlockMapping,\n  InlineContentMapping,\n  StyleMapping,\n} from \"./mapping.js\";\n\nexport type ExporterOptions = {\n  /**\n   * A function that can be used to resolve files, images, etc.\n   * Exporters might need the binary contents of files like images,\n   * which might not always be available from the same origin as the main page.\n   * You can use this option to proxy requests through a server you control\n   * to avoid cross-origin (CORS) issues.\n   *\n   * @default uses a BlockNote hosted proxy (https://corsproxy.api.blocknotejs.org/)\n   * @param url - The URL of the file to resolve\n   * @returns A Promise that resolves to a string (the URL to use instead of the original)\n   * or a Blob (you can return the Blob directly if you have already fetched it)\n   */\n  resolveFileUrl?: (url: string) => Promise<string | Blob>;\n  /**\n   * Colors to use for background of blocks, font colors, and highlight colors\n   */\n  colors: typeof COLORS_DEFAULT;\n};\nexport abstract class Exporter<\n  B extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n  RB,\n  RI,\n  RS,\n  TS,\n> {\n  public constructor(\n    _schema: BlockNoteSchema<B, I, S>, // only used for type inference\n    protected readonly mappings: {\n      blockMapping: BlockMapping<B, I, S, RB, RI>;\n      inlineContentMapping: InlineContentMapping<I, S, RI, TS>;\n      styleMapping: StyleMapping<S, RS>;\n    },\n    public readonly options: ExporterOptions,\n  ) {}\n\n  public async resolveFile(url: string) {\n    if (!this.options?.resolveFileUrl) {\n      return (await fetch(url)).blob();\n    }\n    const ret = await this.options.resolveFileUrl(url);\n    if (ret instanceof Blob) {\n      return ret;\n    }\n    return (await fetch(ret)).blob();\n  }\n\n  public mapStyles(styles: Styles<S>) {\n    const stylesArray = Object.entries(styles).map(([key, value]) => {\n      const mappedStyle = this.mappings.styleMapping[key](value, this);\n      return mappedStyle;\n    });\n    return stylesArray;\n  }\n\n  public mapInlineContent(inlineContent: InlineContent<I, S>) {\n    return this.mappings.inlineContentMapping[inlineContent.type](\n      inlineContent,\n      this,\n    );\n  }\n\n  public transformInlineContent(inlineContentArray: InlineContent<I, S>[]) {\n    return inlineContentArray.map((ic) => this.mapInlineContent(ic));\n  }\n\n  public abstract transformStyledText(styledText: StyledText<S>): TS;\n\n  public async mapBlock(\n    block: BlockFromConfig<B[keyof B], I, S>,\n    nestingLevel: number,\n    numberedListIndex: number,\n    children?: Array<Awaited<RB>>,\n  ) {\n    return this.mappings.blockMapping[block.type](\n      block,\n      this,\n      nestingLevel,\n      numberedListIndex,\n      children,\n    );\n  }\n}\n","import { BlockNoteSchema } from \"../blocks/BlockNoteSchema.js\";\nimport {\n  BlockFromConfigNoChildren,\n  BlockSchema,\n  InlineContentFromConfig,\n  InlineContentSchema,\n  StyleSchema,\n  Styles,\n} from \"../schema/index.js\";\nimport type { Exporter } from \"./Exporter.js\";\n\n/**\n * Defines a mapping from all block types with a schema to a result type `R`.\n */\nexport type BlockMapping<\n  B extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n  RB,\n  RI,\n> = {\n  [K in keyof B]: (\n    block: BlockFromConfigNoChildren<B[K], I, S>,\n    // we don't know the exact types that are supported by the exporter at this point,\n    // because the mapping only knows about converting certain types (which might be a subset of the supported types)\n    // this is why there are many `any` types here (same for types below)\n    exporter: Exporter<any, any, any, RB, RI, any, any>,\n    nestingLevel: number,\n    numberedListIndex?: number,\n    children?: Array<Awaited<RB>>,\n  ) => RB | Promise<RB>;\n};\n\n/**\n * Defines a mapping from all inline content types with a schema to a result type R.\n */\nexport type InlineContentMapping<\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n  RI,\n  TS,\n> = {\n  [K in keyof I]: (\n    inlineContent: InlineContentFromConfig<I[K], S>,\n    exporter: Exporter<any, I, S, any, RI, any, TS>,\n  ) => RI;\n};\n\n/**\n * Defines a mapping from all style types with a schema to a result type R.\n */\nexport type StyleMapping<S extends StyleSchema, RS> = {\n  [K in keyof S]: (\n    style: Styles<S>[K],\n    exporter: Exporter<any, any, any, any, any, RS, any>,\n  ) => RS;\n};\n\n/**\n * The mapping factory is a utility function to easily create mappings for\n * a BlockNoteSchema. Using the factory makes it easier to get typescript code completion etc.\n */\nexport function mappingFactory<\n  B extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n>(_schema: BlockNoteSchema<B, I, S>) {\n  return {\n    createBlockMapping: <R, RI>(mapping: BlockMapping<B, I, S, R, RI>) =>\n      mapping,\n    createInlineContentMapping: <R, RS>(\n      mapping: InlineContentMapping<I, S, R, RS>,\n    ) => mapping,\n    createStyleMapping: <R>(mapping: StyleMapping<S, R>) => mapping,\n  };\n}\n","/**\n * Combines items by group. This can be used to combine multiple slash menu item arrays,\n * while making sure that items from the same group are adjacent to each other.\n */\nexport function combineByGroup<T extends { group?: string }>(\n  items: T[],\n  ...additionalItemsArray: {\n    group?: string;\n  }[][]\n) {\n  const combinedItems = [...items];\n  for (const additionalItems of additionalItemsArray) {\n    for (const additionalItem of additionalItems) {\n      const lastItemWithSameGroup = combinedItems.findLastIndex(\n        (item) => item.group === additionalItem.group,\n      );\n      if (lastItemWithSameGroup === -1) {\n        combinedItems.push(additionalItem as T);\n      } else {\n        combinedItems.splice(lastItemWithSameGroup + 1, 0, additionalItem as T);\n      }\n    }\n  }\n  return combinedItems;\n}\n"],"mappings":"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"}