UNPKG

@blocknote/core

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

1 lines 506 kB
{"version":3,"file":"src-DM3syMdf.cjs","names":[],"sources":["../src/schema/blocks/types.ts","../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/user/userColors.ts","../src/util/EventEmitter.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/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/TextAlignment/TextAlignmentExtension.ts","../src/extensions/tiptap-extensions/TextColor/TextColorExtension.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/pm-nodes/BlockContainer.ts","../src/pm-nodes/BlockGroup.ts","../src/pm-nodes/Doc.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/ExportImage.ts","../src/exporter/mapping.ts","../src/util/combineByGroup.ts"],"sourcesContent":["/** Define the main block types **/\n// import { Extension, Node } from \"@tiptap/core\";\nimport type { Node, NodeViewRendererProps } from \"@tiptap/core\";\nimport type {\n  Fragment,\n  Node as ProsemirrorNode,\n  Schema,\n} from \"prosemirror-model\";\nimport type { ViewMutationRecord } from \"prosemirror-view\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport type {\n  Extension,\n  ExtensionFactoryInstance,\n} from \"../../editor/BlockNoteExtension.js\";\nimport type {\n  InlineContent,\n  InlineContentSchema,\n  PartialInlineContent,\n  StyledText,\n} from \"../inlineContent/types.js\";\nimport type { PropSchema, Props } from \"../propTypes.js\";\nimport type { StyleSchema } from \"../styles/types.js\";\n\nexport type BlockNoteDOMElement =\n  | \"editor\"\n  | \"block\"\n  | \"blockGroup\"\n  | \"blockContent\"\n  | \"inlineContent\";\n\nexport type BlockNoteDOMAttributes = Partial<{\n  [DOMElement in BlockNoteDOMElement]: Record<string, string>;\n}>;\n\nexport interface BlockConfigMeta<\n  TName extends string = string,\n  TProps extends PropSchema = PropSchema,\n> {\n  /**\n   * Defines which keyboard shortcut should be used to insert a hard break into the block's inline content.\n   * @default \"shift+enter\"\n   */\n  hardBreakShortcut?: \"shift+enter\" | \"enter\" | \"none\";\n\n  /**\n   * Whether the block is selectable\n   */\n  selectable?: boolean;\n\n  /**\n   * The accept mime types for the file block\n   */\n  fileBlockAccept?: string[];\n\n  /**\n   * Whether the block is a {@link https://prosemirror.net/docs/ref/#model.NodeSpec.code} block\n   */\n  code?: boolean;\n\n  /**\n   * Whether the block is a {@link https://prosemirror.net/docs/ref/#model.NodeSpec.defining} block\n   */\n  defining?: boolean;\n\n  /**\n   * Whether the block is a {@link https://prosemirror.net/docs/ref/#model.NodeSpec.isolating} block\n   */\n  isolating?: boolean;\n\n  /**\n   * Enables syntax highlighting of the contents of the block with the result of this callback\n   */\n  highlight?(block: { type: TName; props: Props<TProps> }): string | undefined;\n\n  /**\n   * Marks the block as rendering a preview with an editable source popup, driven\n   * by the editor-wide `SourceBlockWithPreviewExtension`. When `true`, the\n   * block's source is hidden behind its preview and edited via the popup.\n   */\n  hasPreview?: boolean;\n}\n\n/**\n * BlockConfig contains the \"schema\" info about a Block type\n * i.e. what props it supports, what content it supports, etc.\n */\nexport interface BlockConfig<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"table\" | \"plain\" =\n    | \"inline\"\n    | \"none\"\n    | \"table\"\n    | \"plain\",\n> {\n  /**\n   * The type of the block (unique identifier within a schema)\n   */\n  type: T;\n  /**\n   * The properties that the block supports\n   * @todo will be zod schema in the future\n   */\n  readonly propSchema: PS;\n  /**\n   * The content that the block supports\n   */\n  content: C;\n  // TODO: how do you represent things that have nested content?\n  // e.g. tables, alerts (with title & content)\n}\n\n/**\n * BlockConfigOrCreator is a union type of BlockConfig and a function that returns a BlockConfig.\n * This is used to create block configs that can be passed to the createBlockSpec function.\n */\nexport type BlockConfigOrCreator<\n  TName extends string = string,\n  TProps extends PropSchema = PropSchema,\n  TContent extends \"inline\" | \"none\" | \"plain\" = \"inline\" | \"none\" | \"plain\",\n  TOptions extends Record<string, any> | undefined =\n    | Record<string, any>\n    | undefined,\n> =\n  | BlockConfig<TName, TProps, TContent>\n  | (TOptions extends undefined\n      ? () => BlockConfig<TName, TProps, TContent>\n      : (options: Partial<TOptions>) => BlockConfig<TName, TProps, TContent>);\n\n/**\n * ExtractBlockConfigFromConfigOrCreator is a helper type that extracts the BlockConfig type from a BlockConfigOrCreator.\n */\nexport type ExtractBlockConfigFromConfigOrCreator<\n  ConfigOrCreator extends\n    | BlockConfig<string, PropSchema, \"inline\" | \"none\" | \"plain\">\n    | ((\n        ...args: any[]\n      ) => BlockConfig<string, PropSchema, \"inline\" | \"none\" | \"plain\">),\n> = ConfigOrCreator extends (...args: any[]) => infer Config\n  ? Config\n  : ConfigOrCreator;\n\n// restrict content to \"inline\" and \"none\" only\nexport type CustomBlockConfig<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"plain\" = \"inline\" | \"none\" | \"plain\",\n> = BlockConfig<T, PS, C>;\n\n// A Spec contains both the Config and Implementation\nexport type BlockSpec<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"table\" | \"plain\" =\n    | \"inline\"\n    | \"none\"\n    | \"table\"\n    | \"plain\",\n> = {\n  config: BlockConfig<T, PS, C>;\n  implementation: BlockImplementation<T, PS, C>;\n  extensions?: (Extension | ExtensionFactoryInstance)[];\n};\n\n/**\n * BlockSpecOrCreator is a union type of BlockSpec and a function that returns a BlockSpec.\n * This is used to create block specs that can be passed to the createBlockSpec function.\n */\nexport type BlockSpecOrCreator<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"table\" | \"plain\" =\n    | \"inline\"\n    | \"none\"\n    | \"table\"\n    | \"plain\",\n  TOptions extends Record<string, any> | undefined =\n    | Record<string, any>\n    | undefined,\n> =\n  | BlockSpec<T, PS, C>\n  | (TOptions extends undefined\n      ? () => BlockSpec<T, PS, C>\n      : (options: Partial<TOptions>) => BlockSpec<T, PS, C>);\n\n/**\n * ExtractBlockSpecFromSpecOrCreator is a helper type that extracts the BlockSpec type from a BlockSpecOrCreator.\n */\nexport type ExtractBlockSpecFromSpecOrCreator<\n  SpecOrCreator extends\n    | BlockSpec<string, PropSchema, \"inline\" | \"none\" | \"plain\">\n    | ((\n        ...args: any[]\n      ) => BlockSpec<string, PropSchema, \"inline\" | \"none\" | \"plain\">),\n> = SpecOrCreator extends (...args: any[]) => infer Spec ? Spec : SpecOrCreator;\n\n/**\n * This allows de-coupling the types that we display to users versus the types we expose internally.\n *\n * This prevents issues with type-inference across parameters that Typescript cannot handle.\n * Specifically, the blocks shape cannot be properly inferred to a specific type like we expose to the user.\n */\nexport type LooseBlockSpec<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"table\" | \"plain\" =\n    | \"inline\"\n    | \"none\"\n    | \"table\"\n    | \"plain\",\n> = {\n  config: BlockConfig<T, PS, C>;\n  implementation: Omit<\n    BlockImplementation<T, PS, C>,\n    \"render\" | \"toExternalHTML\"\n  > & {\n    // purposefully stub the types for render and toExternalHTML since they reference the block\n    render: (\n      /**\n       * The custom block to render\n       */\n      block: any,\n      /**\n       * The BlockNote editor instance\n       */\n      editor: BlockNoteEditor<any>,\n    ) => {\n      dom: HTMLElement | DocumentFragment;\n      contentDOM?: HTMLElement;\n      ignoreMutation?: (mutation: ViewMutationRecord) => boolean;\n      update?: (node: ProsemirrorNode) => boolean;\n      destroy?: () => void;\n    };\n    toExternalHTML?: (\n      block: any,\n      editor: BlockNoteEditor<any>,\n      context: {\n        nestingLevel: number;\n      },\n    ) =>\n      | {\n          dom: HTMLElement | DocumentFragment;\n          contentDOM?: HTMLElement;\n          childrenDOM?: HTMLElement;\n        }\n      | undefined;\n\n    node: Node;\n  };\n  extensions?: (Extension | ExtensionFactoryInstance)[];\n};\n\n// Utility type. For a given object block schema, ensures that the key of each\n// block spec matches the name of the TipTap node in it.\ntype NamesMatch<Blocks extends Record<string, BlockConfig>> = Blocks extends {\n  [Type in keyof Blocks]: Type extends string\n    ? Blocks[Type] extends { type: Type }\n      ? Blocks[Type]\n      : never\n    : never;\n}\n  ? Blocks\n  : never;\n\n// A Schema contains all the types (Configs) supported in an editor\n// The keys are the \"type\" of a block\nexport type BlockSchema = NamesMatch<Record<string, BlockConfig>>;\n\nexport type BlockSpecs = {\n  [k in string]: {\n    config: BlockSpec<k>[\"config\"];\n    implementation: Omit<\n      BlockSpec<k>[\"implementation\"],\n      \"render\" | \"toExternalHTML\"\n    > & {\n      // purposefully stub the types for render and toExternalHTML since they reference the block\n      render: (\n        /**\n         * The custom block to render\n         */\n        block: any,\n        /**\n         * The BlockNote editor instance\n         */\n        editor: BlockNoteEditor<any>,\n      ) => {\n        dom: HTMLElement | DocumentFragment;\n        contentDOM?: HTMLElement;\n        ignoreMutation?: (mutation: ViewMutationRecord) => boolean;\n        update?: (node: ProsemirrorNode) => boolean;\n        destroy?: () => void;\n      };\n      toExternalHTML?: (\n        block: any,\n        editor: BlockNoteEditor<any>,\n        context: {\n          nestingLevel: number;\n        },\n      ) =>\n        | {\n            dom: HTMLElement | DocumentFragment;\n            contentDOM?: HTMLElement;\n            childrenDOM?: HTMLElement;\n          }\n        | undefined;\n    };\n    extensions?: BlockSpec<k>[\"extensions\"];\n  };\n};\n\nexport type BlockImplementations = Record<\n  string,\n  BlockImplementation<any, any>\n>;\n\nexport type BlockSchemaFromSpecs<BS extends BlockSpecs> = {\n  [K in keyof BS]: BS[K][\"config\"];\n};\n\nexport type BlockSpecsFromSchema<BS extends BlockSchema> = {\n  [K in keyof BS]: {\n    config: BlockConfig<BS[K][\"type\"], BS[K][\"propSchema\"], BS[K][\"content\"]>;\n    implementation: BlockImplementation<\n      BS[K][\"type\"],\n      BS[K][\"propSchema\"],\n      BS[K][\"content\"]\n    >;\n    extensions?: (Extension | ExtensionFactoryInstance)[];\n  };\n};\n\nexport type BlockSchemaWithBlock<T extends string, C extends BlockConfig> = {\n  [k in T]: C;\n};\n\nexport type TableCellProps = {\n  backgroundColor: string;\n  textColor: string;\n  textAlignment: \"left\" | \"center\" | \"right\" | \"justify\";\n  colspan?: number;\n  rowspan?: number;\n};\n\nexport type TableCell<\n  I extends InlineContentSchema,\n  S extends StyleSchema = StyleSchema,\n> = {\n  type: \"tableCell\";\n  props: TableCellProps;\n  content: InlineContent<I, S>[];\n};\n\nexport type TableContent<\n  I extends InlineContentSchema,\n  S extends StyleSchema = StyleSchema,\n> = {\n  type: \"tableContent\";\n  columnWidths: (number | undefined)[];\n  headerRows?: number;\n  headerCols?: number;\n  rows: {\n    cells: InlineContent<I, S>[][] | TableCell<I, S>[];\n  }[];\n};\n\n// The content of a block with \"plain\" content (e.g. a code block): unstyled\n// text, represented as StyledText items whose `styles` is always empty.\nexport type PlainContent = (StyledText<{}> & {\n  styles: Record<string, never>;\n})[];\n\n// Partial form of PlainContent: also accepts bare strings (both as the whole\n// content and as array items), which are normalized on write.\nexport type PartialPlainContent =\n  | string\n  | (string | (StyledText<{}> & { styles: Record<string, never> }))[];\n\n/**\n * The text of a block's `\"plain\"` content (e.g. a code block's source code).\n * Accepts the partial form too: block render/export paths can receive\n * `PartialBlock`s (e.g. the HTML serializers take them directly), where\n * plain content may still be the bare-string sugar.\n */\nexport function plainContentToString(\n  content: PlainContent | PartialPlainContent,\n): string {\n  if (typeof content === \"string\") {\n    return content;\n  }\n\n  return content\n    .map((item) => (typeof item === \"string\" ? item : item.text))\n    .join(\"\");\n}\n\n// A BlockConfig has all the information to get the type of a Block (which is a specific instance of the BlockConfig.\n// i.e.: paragraphConfig: BlockConfig defines what a \"paragraph\" is / supports, and BlockFromConfigNoChildren<paragraphConfig> is the shape of a specific paragraph block.\n// (for internal use)\nexport type BlockFromConfigNoChildren<\n  B extends BlockConfig,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = {\n  id: string;\n  type: B[\"type\"];\n  props: Props<B[\"propSchema\"]>;\n  content: B[\"content\"] extends \"inline\"\n    ? InlineContent<I, S>[]\n    : B[\"content\"] extends \"table\"\n      ? TableContent<I, S>\n      : B[\"content\"] extends \"plain\"\n        ? PlainContent\n        : B[\"content\"] extends \"none\"\n          ? undefined\n          : never;\n};\n\nexport type BlockFromConfig<\n  B extends BlockConfig,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = BlockFromConfigNoChildren<B, I, S> & {\n  children: BlockNoDefaults<BlockSchema, I, S>[];\n};\n\n// Converts each block spec into a Block object without children. We later merge\n// them into a union type and add a children property to create the Block and\n// PartialBlock objects we use in the external API.\ntype BlocksWithoutChildren<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = {\n  [BType in keyof BSchema]: BlockFromConfigNoChildren<BSchema[BType], I, S>;\n};\n\n// Converts each block spec into a Block object without children, merges them\n// into a union type, and adds a children property\nexport type BlockNoDefaults<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = BlocksWithoutChildren<BSchema, I, S>[keyof BSchema] & {\n  children: BlockNoDefaults<BSchema, I, S>[];\n};\n\nexport type SpecificBlock<\n  BSchema extends BlockSchema,\n  BType extends keyof BSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = BlocksWithoutChildren<BSchema, I, S>[BType] & {\n  children: BlockNoDefaults<BSchema, I, S>[];\n};\n\n/** CODE FOR PARTIAL BLOCKS, analogous to above\n *\n * Partial blocks are convenience-wrappers to make it easier to\n *create/update blocks in the editor.\n *\n */\n\nexport type PartialTableCell<\n  I extends InlineContentSchema,\n  S extends StyleSchema = StyleSchema,\n> = {\n  type: \"tableCell\";\n  props?: Partial<TableCellProps>;\n  content?: PartialInlineContent<I, S>;\n};\n\nexport type PartialTableContent<\n  I extends InlineContentSchema,\n  S extends StyleSchema = StyleSchema,\n> = {\n  type: \"tableContent\";\n  columnWidths?: (number | undefined)[];\n  headerRows?: number;\n  headerCols?: number;\n  rows: {\n    cells: PartialInlineContent<I, S>[] | PartialTableCell<I, S>[];\n  }[];\n};\n\ntype PartialBlockFromConfigNoChildren<\n  B extends BlockConfig,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = {\n  id?: string;\n  type?: B[\"type\"];\n  props?: Partial<Props<B[\"propSchema\"]>>;\n  content?: B[\"content\"] extends \"inline\"\n    ? PartialInlineContent<I, S>\n    : B[\"content\"] extends \"table\"\n      ? PartialTableContent<I, S>\n      : B[\"content\"] extends \"plain\"\n        ? PartialPlainContent\n        : B[\"content\"] extends \"none\"\n          ? undefined\n          : never;\n};\n\ntype PartialBlocksWithoutChildren<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = {\n  [BType in keyof BSchema]: PartialBlockFromConfigNoChildren<\n    BSchema[BType],\n    I,\n    S\n  >;\n};\n\nexport type PartialBlockNoDefaults<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = PartialBlocksWithoutChildren<\n  BSchema,\n  I,\n  S\n>[keyof PartialBlocksWithoutChildren<BSchema, I, S>] &\n  Partial<{\n    children: PartialBlockNoDefaults<BSchema, I, S>[];\n  }>;\n\nexport type SpecificPartialBlock<\n  BSchema extends BlockSchema,\n  I extends InlineContentSchema,\n  BType extends keyof BSchema,\n  S extends StyleSchema,\n> = PartialBlocksWithoutChildren<BSchema, I, S>[BType] & {\n  children?: BlockNoDefaults<BSchema, I, S>[];\n};\n\nexport type PartialBlockFromConfig<\n  B extends BlockConfig,\n  I extends InlineContentSchema,\n  S extends StyleSchema,\n> = PartialBlockFromConfigNoChildren<B, I, S> & {\n  children?: BlockNoDefaults<BlockSchema, I, S>[];\n};\n\nexport type BlockIdentifier = { id: string } | string;\n\nexport type BlockImplementation<\n  TName extends string = string,\n  TProps extends PropSchema = PropSchema,\n  TContent extends \"inline\" | \"none\" | \"table\" | \"plain\" =\n    | \"inline\"\n    | \"none\"\n    | \"table\"\n    | \"plain\",\n> = {\n  /**\n   * Metadata\n   */\n  meta?: BlockConfigMeta<TName, TProps>;\n  /**\n   * A function that converts the block into a DOM element\n   */\n  render: (\n    this:\n      | Record<string, never>\n      | ({\n          blockContentDOMAttributes: Record<string, string>;\n          propSchema?: TProps;\n        } & (\n          | {\n              renderType: \"nodeView\";\n              props: NodeViewRendererProps;\n            }\n          | {\n              renderType: \"dom\";\n              props: undefined;\n            }\n        )),\n    /**\n     * The custom block to render\n     */\n    block: BlockFromConfig<BlockConfig<TName, TProps, TContent>, any, any>,\n    /**\n     * The BlockNote editor instance\n     */\n    editor: BlockNoteEditor<\n      Record<TName, BlockConfig<TName, TProps, TContent>>\n    >,\n  ) => {\n    dom: HTMLElement | DocumentFragment;\n    contentDOM?: HTMLElement;\n    ignoreMutation?: (mutation: ViewMutationRecord) => boolean;\n    /**\n     * Called by ProseMirror when this block's node is updated (e.g. its content\n     * or props change). Return `true` to handle the update in place - keeping\n     * the existing DOM - or `false` to have the node view recreated via\n     * `render`. When omitted, ProseMirror keeps the node view and reconciles its\n     * `contentDOM` in place as long as the node type stays the same.\n     *\n     * Useful for blocks whose `render` builds custom DOM that needs to stay in\n     * sync with the node (e.g. a code block rendering a preview of its content).\n     */\n    update?: (node: ProsemirrorNode) => boolean;\n    destroy?: () => void;\n  };\n\n  /**\n   * Exports block to external HTML. If not defined, the output will be the same\n   * as `render(...).dom`.\n   */\n  toExternalHTML?: (\n    this: Partial<{\n      blockContentDOMAttributes: Record<string, string>;\n      propSchema: TProps;\n    }>,\n    block: BlockFromConfig<BlockConfig<TName, TProps, TContent>, any, any>,\n    editor: BlockNoteEditor<\n      Record<TName, BlockConfig<TName, TProps, TContent>>\n    >,\n    context: {\n      nestingLevel: number;\n    },\n  ) =>\n    | {\n        dom: HTMLElement | DocumentFragment;\n        contentDOM?: HTMLElement;\n        childrenDOM?: HTMLElement;\n      }\n    | undefined;\n\n  /**\n   * Parses an external HTML element into a block of this type when it returns the block props object, otherwise undefined\n   */\n  parse?: (el: HTMLElement) => Partial<Props<TProps>> | undefined;\n\n  /**\n   * The blocks that this block should run before.\n   * This is used to determine the order in which blocks are parsed\n   */\n  runsBefore?: string[];\n\n  /**\n   * Advanced parsing function that controls how content within the block is parsed.\n   * This is not recommended to use, and is only useful for advanced use cases.\n   */\n  parseContent?: (options: {\n    el: HTMLElement;\n    schema: Schema;\n  }) => Fragment | undefined;\n};\n\n/**\n * BlockImplementationOrCreator is a union type of BlockImplementation and a function that returns a BlockImplementation.\n * This is used to create block implementations that can be passed to the createBlockSpec function.\n */\nexport type BlockImplementationOrCreator<\n  ConfigOrCreator extends BlockConfigOrCreator = BlockConfigOrCreator,\n  TOptions extends Record<string, any> | undefined =\n    | Record<string, any>\n    | undefined,\n  Config extends ExtractBlockConfigFromConfigOrCreator<ConfigOrCreator> =\n    ExtractBlockConfigFromConfigOrCreator<ConfigOrCreator>,\n> =\n  | BlockImplementation<Config[\"type\"], Config[\"propSchema\"], Config[\"content\"]>\n  | (TOptions extends undefined\n      ? () => BlockImplementation<\n          Config[\"type\"],\n          Config[\"propSchema\"],\n          Config[\"content\"]\n        >\n      : (\n          options: Partial<TOptions>,\n        ) => BlockImplementation<\n          Config[\"type\"],\n          Config[\"propSchema\"],\n          Config[\"content\"]\n        >);\n\n/**\n * ExtractBlockImplementationFromImplementationOrCreator is a helper type that extracts the BlockImplementation type from a BlockImplementationOrCreator.\n */\nexport type ExtractBlockImplementationFromImplementationOrCreator<\n  ImplementationOrCreator extends\n    | BlockImplementation<string, PropSchema, \"inline\" | \"none\" | \"plain\">\n    | ((\n        ...args: any[]\n      ) => BlockImplementation<\n        string,\n        PropSchema,\n        \"inline\" | \"none\" | \"plain\"\n      >),\n> = ImplementationOrCreator extends (...args: any[]) => infer Implementation\n  ? Implementation\n  : ImplementationOrCreator;\n\n// restrict content to \"inline\" and \"none\" only\nexport type CustomBlockImplementation<\n  T extends string = string,\n  PS extends PropSchema = PropSchema,\n  C extends \"inline\" | \"none\" | \"plain\" = \"inline\" | \"none\" | \"plain\",\n> = BlockImplementation<T, PS, C>;\n","import { Node } from \"@tiptap/core\";\n\nimport {\n  DOMParser,\n  Fragment,\n  Node as ProsemirrorNode,\n  Schema,\n  TagParseRule,\n} 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 { ignoreNonContentMutations } from \"../nodeViewMutations.js\";\nimport { propsToAttributes } from \"../blocks/internal.js\";\nimport { nonFormattingMarks } from \"../markGroups.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    code?: boolean;\n    /**\n     * When {@link code} is `true`, this can syntax highlight the contents of the\n     * inline content with the result of this callback.\n     */\n    // Method syntax (rather than an arrow-function property) so its parameter is\n    // checked bivariantly, keeping a specific implementation assignable to the\n    // generic spec record type.\n    highlight?(\n      inlineContent: Pick<InlineContentFromConfig<T, S>, \"type\" | \"props\">,\n    ): string | undefined;\n    /**\n     * Marks the inline content as rendering a preview with an editable source\n     * popup, driven by the editor-wide\n     * `SourceInlineContentWithPreviewExtension`.\n     */\n    hasPreview?: 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   * Advanced parsing function that controls how the content within the inline\n   * content is parsed. This is not recommended to use, and is only useful for\n   * advanced use cases. Only applies to inline content with `content: \"styled\"`.\n   * Return `undefined` to fall through to the default inline content parsing.\n   */\n  parseContent?: (options: {\n    el: HTMLElement;\n    schema: Schema;\n  }) => Fragment | 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     * The ProseMirror node backing this inline content.\n     */\n    node: ProsemirrorNode,\n    /**\n     * Returns this inline content's position in the document. When rendered\n     * outside the editor (i.e. serialized to HTML), this is a no-op that returns\n     * `undefined`.\n     */\n    getPos: () => number | undefined,\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\n// Resolves the element whose children hold the inline content's editable\n// content, i.e. the `[data-editable]` element (or the element itself if it is /\n// contains none).\nfunction getEditableElement(element: HTMLElement) {\n  if (element.matches(\"[data-editable]\")) {\n    return element;\n  }\n\n  return element.querySelector<HTMLElement>(\"[data-editable]\") || element;\n}\n\n// Parses an element's children as inline content.\nfunction parseInlineContent(el: HTMLElement, schema: Schema) {\n  return DOMParser.fromSchema(schema).parse(el, {\n    topNode: schema.nodes.paragraph.create(),\n    preserveWhitespace: true,\n  }).content;\n}\n\n// Flattens parsed inline content into text nodes only. \"plain\" inline content\n// holds text only, so non-text inline nodes are flattened: line breaks become\n// newline characters and other nodes (e.g. mentions) are kept as their text.\nfunction flattenToText(content: Fragment, schema: Schema) {\n  const textNodes: ProsemirrorNode[] = [];\n  content.forEach((child) => {\n    if (child.isText) {\n      textNodes.push(child);\n    } else {\n      const text =\n        child.type === schema.linebreakReplacement ? \"\\n\" : child.textContent;\n      if (text) {\n        textNodes.push(schema.text(text, child.marks));\n      }\n    }\n  });\n\n  return Fragment.fromArray(textNodes);\n}\n\nexport function getInlineContentParseRules<C extends CustomInlineContentConfig>(\n  config: C,\n  customParseFunction?: CustomInlineContentImplementation<C, any>[\"parse\"],\n  customParseContentFunction?: CustomInlineContentImplementation<\n    C,\n    any\n  >[\"parseContent\"],\n) {\n  // When a custom `parseContent` function is provided (and this inline content\n  // actually holds content), it controls how content within the inline content\n  // is parsed. This applies to _both_ parse rules below, as content copied from\n  // within the editor is tagged with `data-inline-content-type` (matched by the\n  // first rule), while content pasted from outside is matched by the custom\n  // `parse` function (the second rule). `resolveContentElement` locates the\n  // element whose children to parse as a fallback when `parseContent` returns\n  // `undefined`.\n  // \"plain\" inline content always needs `getContent` so its parsed content is\n  // flattened to text (`<br>`/line breaks become newline characters), regardless\n  // of whether a custom `parseContent` is provided — mirroring \"plain\" blocks.\n  // \"styled\" inline content only needs it to run a custom `parseContent`.\n  const getContent =\n    config.content === \"plain\" ||\n    (customParseContentFunction && config.content === \"styled\")\n      ? (resolveContentElement: (el: HTMLElement) => HTMLElement) =>\n          (node: HTMLElement, schema: Schema) => {\n            const result = customParseContentFunction?.({ el: node, schema });\n\n            // `parseContent` may return `undefined` to fall through to the\n            // default inline content parsing.\n            if (result !== undefined) {\n              return config.content === \"plain\"\n                ? flattenToText(result, schema)\n                : result;\n            }\n\n            const parsed = parseInlineContent(\n              resolveContentElement(node),\n              schema,\n            );\n            return config.content === \"plain\"\n              ? flattenToText(parsed, schema)\n              : parsed;\n          }\n      : undefined;\n\n  const rules: TagParseRule[] = [\n    {\n      tag: `[data-inline-content-type=\"${config.type}\"]`,\n      contentElement: (element) => getEditableElement(element as HTMLElement),\n      getContent: getContent\n        ? (node, schema) =>\n            getContent(getEditableElement)(node as HTMLElement, schema)\n        : undefined,\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      // Because we do the parsing ourselves, we want to preserve whitespace for\n      // content we've parsed.\n      preserveWhitespace: getContent ? true : undefined,\n      getContent: getContent\n        ? (node, schema) => getContent((el) => el)(node as HTMLElement, schema)\n        : undefined,\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 !== \"none\",\n    atom: inlineContentConfig.content === \"none\",\n    code: inlineContentImplementation.meta?.code,\n    content:\n      inlineContentConfig.content === \"styled\"\n        ? \"inline*\"\n        : inlineContentConfig.content === \"plain\"\n          ? \"text*\"\n          : \"\",\n    // \"plain\" inline content holds unstyled text, so it disallows formatting\n    // marks (mirroring \"plain\" blocks). It still allows the non-formatting marks\n    // (comments and suggestions/diffs), which annotate content without changing\n    // it and are ignored by the content model. `nonFormattingMarks` resolves the\n    // group only when at least one such mark is registered, so a plain inline\n    // content in an editor without any of them doesn't reference an empty\n    // (unknown) mark group.\n    marks() {\n      return inlineContentConfig.content === \"plain\"\n        ? nonFormattingMarks(this.editor)\n        : undefined;\n    },\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        inlineContentImplementation.parseContent,\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        node,\n        () => undefined,\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          node,\n          getPos,\n        );\n\n        const nodeView = addInlineContentAttributes(\n          output,\n          inlineContentConfig.type,\n          node.attrs as Props<T[\"propSchema\"]>,\n          inlineContentConfig.propSchema,\n        );\n\n        // Ignores DOM mutations that don't affect the inline content, so that\n        // browser extensions which rewrite the DOM (e.g. Dark Reader) can't\n        // trigger an infinite re-render loop that freezes the tab.\n        ignoreNonContentMutations(nodeView);\n\n        return nodeView;\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        // Rendered outside the editor (serialization), so there's no live node\n        // view - derive the node from the content and stub out `getPos`.\n        const node = inlineContentToNodes(\n          [inlineContent] as any,\n          editor.pmSchema,\n        )[0];\n\n        const output = inlineContentImplementation.render(\n          inlineContent,\n          updateInlineContent,\n          editor,\n          node,\n          () => undefined,\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, tr.doc),\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 { getNodeId } from \"../../../getBlockInfoFromPos.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  options: {\n    fixColumns?: boolean;\n  } = {},\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 (!node.type.isInGroup(\"bnBlock\")) {\n      return true;\n    }\n\n    const nodeId = getNodeId(node, tr.doc);\n\n    if (!idsOfBlocksToRemove.has(nodeId)) {\n      return true;\n    }\n\n    // Saves the block that is being deleted.\n    removedBlocks.push(nodeToBlock(node, tr.doc));\n    idsOfBlocksToRemove.delete(nodeId);\n\n    if (blocksToInsert.length > 0 && nodeId === 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  // Collapses empty columns/columnLists. Callers where the removal isn't a\n  // deletion can opt out - e.g. `moveBlocks` re-inserts the blocks elsewhere\n  // and deliberately leaves emptied columns as-is.\n  if (options.fixColumns !== false) {\n    columnListPositions.forEach((pos) => fixColumnList(tr, pos));\n  }\n\n  // Converts the nodes created from `blocksToInsert` into full `Block`s.\n  const insertedBlocks = nodesToInsert.map((node) =>\n    nodeToBlock(node, tr.doc),\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 { digestString } from \"lib0/hash/fnv1a\";\nimport type { UserStore } from \"./UserStore.js\";\n\n/**\n * Deterministic hash of a string to an unsigned 32-bit integer.\n */\nconst hashStr = (s: string): number => {\n  let hash = 0;\n  for (let i = 0; i < s.length; i++) {\n    hash = Math.imul(31, hash) + s.charCodeAt(i);\n  }\n  return Math.abs(hash);\n};\n\n/** Fallback palette used when a user has no resolved color of their own. */\nexport const userColorPalette: Array<{ light: string; dark: string }> = [\n  { light: \"#fff0c2\", dark: \"#8a6d1a\" },\n  { light: \"#fcc9c3\", dark: \"#8a2e24\" },\n  { light: \"#d4e8eb\", dark: \"#4a7178\" },\n  { light: \"#c2eeff\", dark: \"#1a6e8a\" },\n  { light: \"#bef3ff\", dark: \"#0a7a8a\" },\n];\n\n/** The deterministic {@link userColorPalette} entry for a single user id. */\nexport const fallbackColorForUserId = (\n  id: string,\n): { light: string; dark: string } =>\n  userColorPalette[hashStr(id) % userColorPalette.length];\n\n/**\n * The (first) user's resolved color from the {@link UserStore}, or their\n * {@link fallbackColorForUserId} palette entry. Used where a concrete color\n * string is needed (the portaled hover tooltip); marks themselves use the\n * cascaded {@link userColorVarNames} properties instead.\n */\nexport const colorsForUserIds = (\n  userStore: UserStore,\n  userIds: readonly string[] | undefined | null,\n): { light: string; dark: string } => {\n  if (!userIds || userIds.length === 0) {\n    return userColorPalette[0];\n  }\n  const firstId = userIds[0];\n  const user = userStore.getUser(firstId);\n  if (user?.color && user.colorLight) {\n    return { light: user.colorLight, dark: user.color };\n  }\n  return fallbackColorForUserId(firstId);\n};\n\n/**\n * Reduce a user id to a fixed-width `[0-9a-f]` token safe to embed in a CSS\n * custom-property name. Uses the (non-cryptographic) FNV-1a 32-bit hash; a\n * collision only means two authors share a highlight color.\n */\nexport const cssVarUserId = (id: string): string =>\n  digestString(id).toString(16).padStart(8, \"0\");\n\n/**\n * The `--user-color-<key>-{light,dark}` custom-property names for a user. Set on\n * the editor root by `AttributionExtension`, read by the mark wrapper via\n * `var(..., <fallback>)`.\n */\nexport const userColorVarNames = (\n  id: string,\n): { light: string; dark: string } => {\n  const key = cssVarUserId(id);\n  return {\n    light: `--user-color-${key}-light`,\n    dark: `--user-color-${key}-dark`,\n  };\n};\n","// from https://raw.githubusercontent.com/ueberdosis/tiptap/develop/packages/core/src/EventEmitter.ts (MIT)\n\ntype StringKeyOf<T> = Extract<keyof T, string>;\ntype CallbackType<\n  T extends Record<string, any>,\n  EventName extends StringKeyOf<T>,\n> = T[EventName] extends any[] ? T[EventName] : [T[EventName]];\ntype CallbackFunction<\n  T extends Record<string, any>,\n  EventName extends StringKeyOf<T>,\n> = (...props: CallbackType<T, EventName>) => any;\n\nexport class EventEmitter<T extends Record<string, any>> {\n  // eslint-disable-next-line @typescript-eslint/ban-types\n  private callbacks: { [key: string]: Function[] } = {};\n\n  public on<EventName extends StringKeyOf<T>>(\n    event: EventName,\n    fn: CallbackFunction<T, EventName>,\n  ) {\n    if (!this.callbacks[event]) {\n      this.callbacks[event] = [];\n    }\n\n    this.callbacks[event].push(fn);\n\n    return () => this.off(event, fn);\n  }\n\n  protected emit<EventName extends StringKeyOf<T>>(\n    event: EventName,\n    ...args: CallbackType<T, EventName>\n  ) {\n    const callbacks = this.callbacks[event];\n\n    if (callbacks) {\n      callbacks.forEach((callback) => callback.apply(this, args));\n    }\n  }\n\n  public off<EventName extends StringKeyOf<T>>(\n    event: EventName,\n    fn?: CallbackFunction<T, EventName>,\n  ) {\n    const callbacks = this.callbacks[event];\n\n    if (callbacks) {\n      if (fn) {\n        this.callbacks[event] = callbacks.filter((callback) => callback !== fn);\n      } else {\n        delete this.callbacks[event];\n      }\n    }\n  }\n\n  protected removeAllListeners(): void {\n    this.callbacks = {};\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 {\n  getBlockInfoAtNearest,\n  getNodeId,\n} from \"../../../getBlockInfoFromPos.js\";\nimport { getNodeById } from \"../../../nodeUtil.js\";\nimport { insertBlocks } from \"../insertBlocks/insertBlocks.js\";\nimport { removeAndInsertBlocks } from \"../replaceBlocks/replaceBlocks.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 = getBlockInfoAtNearest(tr, tr.selection.anchor);\n\n    const anchorBlockId = getNodeId(anchorBlockPosInfo.bnBlock.node, tr.doc);\n\n    if (tr.selection instanceof CellSelection) {\n      return {\n        type: \"cell\" as const,\n        anchorBlockId,\n        anchorCellOffset:\n          tr.selection.$anchorCell.pos - anchorBlockPosInfo.bnBlock.beforePos,\n        headCellOffset:\n          tr.selection.$headCell.pos - anchorBlockPosInfo.bnBlock.beforePos,\n      };\n    } else if (tr.selection instanceof NodeSelection) {\n      return {\n        type: \"node\" as const,\n        anchorBlockId,\n      };\n    } else {\n      const headBlockPosInfo = getBlockInfoAtNearest(tr, tr.selection.head);\n\n      return {\n        type: \"text\" as const,\n        anchorBlockId,\n        headBlockId: getNodeId(headBlockPosInfo.bnBlock.node, tr.doc),\n        anchorOffset:\n          tr.selection.anchor - anchorBlockPosInfo.bnBlock.beforePos,\n        headOffset: tr.selection.head - headBlockPosInfo.bnBlock.beforePos,\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((tr) => {\n    // Don't fix columns/columnLists in the removal step. Since a move is a\n    // rearrangement rather than a deletion, columns that it empties out are\n    // deliberately left as-is instead of being collapsed - this keeps moves\n    // free of side effects (and reversible by moving back), and matches\n    // dragging a block out of a column, which doesn't collapse it either.\n    // Fixing them mid-move also broke the following case:\n    // <column>\n    //  <paragraph></paragraph>\n    //  <paragraph>Paragraph</paragraph>\n    // </column>\n    // When the non-empty block is moved up, the column is seen as empty and\n    // collapsed in the removal step, so the following insertion fails.\n    removeAndInsertBlocks(tr, blocks, [], { fixColumns: false });\n    insertBlocks<any, any, any>(\n      tr,\n      flattenColumns(blocks),\n      referenceBlock,\n      placement,\n    );\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 { getBlockInfoFromSelection } 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(tr: Transaction, itemType: NodeType, groupType: NodeType) {\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 } = getBlockInfoFromSelection(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 } = getBlockInfoFromSelection(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\";\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\n  const posInfo = getNodeById(id, doc);\n  if (!posInfo) {\n    return undefined;\n  }\n\n  return nodeToBlock(posInfo.node, doc);\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  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, doc);\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  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, doc);\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 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, doc);\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);\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 { EMPTY_BLOCK_PLACEHOLDER } from \"../../exporters/html/util/serializeBlocksExternalHTML.js\";\nimport { nodeToBlock } from \"../../nodeConversions/nodeToBlock.js\";\nimport { nestedListsToBlockNoteStructure } from \"./util/nestedLists.js\";\nimport { preprocessHTMLWhitespace } from \"./util/normalizeWhitespace.js\";\n\n/**\n * Removes the placeholder character that the external HTML exporter inserts\n * into empty inline-content blocks (see `EMPTY_BLOCK_PLACEHOLDER`). The\n * placeholder keeps such blocks from being dropped while the HTML is parsed;\n * stripping it here lets the block round trip back to genuinely empty content.\n */\nfunction stripEmptyBlockPlaceholder(content: any[]): any[] {\n  const stripped: any[] = [];\n\n  for (const item of content) {\n    if (item.type === \"text\") {\n      const text = item.text.split(EMPTY_BLOCK_PLACEHOLDER).join(\"\");\n      if (text.length > 0) {\n        stripped.push({ ...item, text });\n      }\n    } else if (Array.isArray(item.content)) {\n      // Links and custom inline content that hold nested styled text.\n      stripped.push({\n        ...item,\n        content: stripEmptyBlockPlaceholder(item.content),\n      });\n    } else {\n      stripped.push(item);\n    }\n  }\n\n  return stripped;\n}\n\nfunction stripEmptyBlockPlaceholderFromBlocks(blocks: Block<any, any, any>[]) {\n  for (const block of blocks) {\n    if (Array.isArray(block.content)) {\n      (block as any).content = stripEmptyBlockPlaceholder(block.content);\n    }\n    if (block.children.length > 0) {\n      stripEmptyBlockPlaceholderFromBlocks(block.children);\n    }\n  }\n}\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), parentNode));\n  }\n\n  stripEmptyBlockPlaceholderFromBlocks(blocks);\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 = 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) {\n    return null;\n  }\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] !== \"`\") {\n    return null;\n  }\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] !== \"[\") {\n    return null;\n  }\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] !== \"[\") {\n    return null;\n  }\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] !== \"~\") {\n    return null;\n  }\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] !== \"<\") {\n    return null;\n  }\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) {\n    return null;\n  }\n  const altStart = start + 2; // after ![\n\n  if (text[altEnd + 1] !== \"(\") {\n    return null;\n  }\n\n  const urlStart = altEnd + 2;\n  const parenEnd = findClosingParen(text, urlStart - 1);\n  if (parenEnd === -1) {\n    return null;\n  }\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 = 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) {\n    return null;\n  }\n\n  if (text[textEnd + 1] !== \"(\") {\n    return null;\n  }\n\n  const urlStart = textEnd + 2;\n  const parenEnd = findClosingParen(text, textEnd + 1);\n  if (parenEnd === -1) {\n    return null;\n  }\n\n  const linkText = text.substring(textStart, textEnd);\n  const { url, title } = parseDestinationAndTitle(\n    text.substring(urlStart, parenEnd),\n  );\n\n  const titleAttr = 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] === \"[\") {\n      depth++;\n    }\n    if (text[i] === \"]\") {\n      depth--;\n      if (depth === 0) {\n        return i;\n      }\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] === \"(\") {\n      depth++;\n    }\n    if (text[i] === \")\") {\n      depth--;\n      if (depth === 0) {\n        return i;\n      }\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) {\n    return null;\n  }\n\n  // Opening delimiter must not be followed by whitespace\n  if (text[afterOpen] === \" \" || text[afterOpen] === \"\\t\") {\n    return null;\n  }\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 &&\n          text[j - 1] === delimiter[0] &&\n          !(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\",\n  \"article\",\n  \"aside\",\n  \"audio\",\n  \"base\",\n  \"basefont\",\n  \"blockquote\",\n  \"body\",\n  \"caption\",\n  \"center\",\n  \"col\",\n  \"colgroup\",\n  \"dd\",\n  \"details\",\n  \"dialog\",\n  \"dir\",\n  \"div\",\n  \"dl\",\n  \"dt\",\n  \"fieldset\",\n  \"figcaption\",\n  \"figure\",\n  \"footer\",\n  \"form\",\n  \"frame\",\n  \"frameset\",\n  \"h1\",\n  \"h2\",\n  \"h3\",\n  \"h4\",\n  \"h5\",\n  \"h6\",\n  \"head\",\n  \"header\",\n  \"hr\",\n  \"html\",\n  \"iframe\",\n  \"legend\",\n  \"li\",\n  \"link\",\n  \"main\",\n  \"menu\",\n  \"menuitem\",\n  \"nav\",\n  \"noframes\",\n  \"ol\",\n  \"optgroup\",\n  \"option\",\n  \"p\",\n  \"param\",\n  \"section\",\n  \"source\",\n  \"summary\",\n  \"table\",\n  \"tbody\",\n  \"td\",\n  \"tfoot\",\n  \"th\",\n  \"thead\",\n  \"title\",\n  \"tr\",\n  \"track\",\n  \"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) {\n    return false;\n  }\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() === \"\") {\n          break;\n        }\n        // Stop on block-level markers\n        if (/^\\s{0,3}>/.test(cur)) {\n          break;\n        } // new blockquote\n        if (/^(#{1,6})\\s/.test(cur)) {\n          break;\n        } // heading\n        if (/^(`{3,}|~{3,})/.test(cur)) {\n          break;\n        } // code fence\n        if (/^(\\s{0,3})([-*_])\\s*(\\2\\s*){2,}$/.test(cur)) {\n          break;\n        } // hr\n        if (/^\\s*([-*+]|\\d+[.)])\\s+/.test(cur)) {\n          break;\n        } // list item\n        if (/^\\s*\\|(.+\\|)+\\s*$/.test(cur)) {\n          break;\n        } // 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() === \"\") {\n          return true;\n        } // blank lines checked separately\n        const lineInd = lineStr.match(/^\\s*/)![0].length;\n        // Lines at contentIndent are continuation text\n        if (lineInd >= contentIndent) {\n          return true;\n        }\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)) {\n          break;\n        }\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() === \"\") {\n        break;\n      }\n      // Stop on block-level element\n      if (/^(#{1,6})\\s/.test(nextLine)) {\n        break;\n      }\n      if (/^(`{3,}|~{3,})/.test(nextLine)) {\n        break;\n      }\n      if (/^\\s{0,3}>/.test(nextLine)) {\n        break;\n      }\n      if (/^(\\s{0,3})([-*_])\\s*(\\2\\s*){2,}$/.test(nextLine)) {\n        break;\n      }\n      if (/^\\s*([-*+]|\\d+[.)])\\s+/.test(nextLine)) {\n        break;\n      }\n      if (/^\\s*\\|(.+\\|)+\\s*$/.test(nextLine)) {\n        break;\n      }\n      if (isHtmlBlockStart(nextLine)) {\n        break;\n      }\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) {\n    return null;\n  }\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  ) {\n    return null;\n  }\n\n  // Check header line has at least one pipe (required to distinguish from plain text)\n  if (!headerLine.includes(\"|\")) {\n    return null;\n  }\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(\"|\")) {\n      break;\n    }\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 (\n      content[i] === \"\\\\\" &&\n      i + 1 < content.length &&\n      content[i + 1] === \"|\"\n    ) {\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) {\n      return \"center\";\n    }\n    if (right) {\n      return \"right\";\n    }\n    if (left) {\n      return \"left\";\n    }\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  getBlockInfoFromSelection,\n  getNodeId,\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 } = getBlockInfoFromSelection(tr);\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, tr.doc),\n    prevBlock: prevNode === null ? undefined : nodeToBlock(prevNode, tr.doc),\n    nextBlock: nextNode === null ? undefined : nodeToBlock(nextNode, tr.doc),\n    parentBlock:\n      parentNode === undefined ? undefined : nodeToBlock(parentNode, tr.doc),\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\" | \"plain\" =\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\" || contentType === \"plain\") {\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, getNodeId(child, tr.doc), placement);\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 { getBlockInfoAtNearest, getNodeId } 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 blockInfo = getBlockInfoAtNearest(tr, pos.pos);\n          const id = getNodeId(blockInfo.bnBlock.node, tr.doc);\n          // TODO technically data-id will always be the non-rewritten id, so there might be multiple in the document.\n          // getNodeId might find the wrong one (aka point to a deleted node when it should be a non-deleted on)\n          // This is acceptable right now, given that we don't expect edits on the document content\n          const blockElement = editor.domElement?.querySelector(\n            `[data-id=\"${id}\"]`,\n          );\n\n          const blockRect = blockElement?.getBoundingClientRect();\n\n          const existingBlock = editor.getBlock(id);\n          if (!existingBlock) {\n            return;\n          }\n\n          return insertOrUpdateBlock(\n            editor,\n            existingBlock,\n            fileBlock,\n            blockRect && (blockRect.top + blockRect.bottom) / 2 > coords.top\n              ? \"before\"\n              : \"after\",\n          );\n        });\n      } else {\n        return;\n      }\n\n      if (!insertedBlockId) {\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                  void 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    void 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 { FilePanelExtension } from \"../../FilePanel/FilePanel.js\";\nimport { FormattingToolbarExtension } from \"../../FormattingToolbar/FormattingToolbar.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 === 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 === 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 blockSpec =\n              this.options.editor.schema.blockSpecs[blockInfo.blockNoteType];\n\n            // NOTE: This likely doesn't work as intended - `blockSchema[type]`\n            // holds the block *config* (type/propSchema/content), which carries\n            // no `meta`, so `meta?.hardBreakShortcut` is always `undefined` and\n            // this falls back to the default. It should read from the block\n            // spec's implementation instead (i.e.\n            // `editor.schema.blockSpecs[type].implementation.meta`), the way the\n            // syntax-highlighting extension reads `meta.highlight`. Left as-is\n            // for a follow-up pass.\n            const blockHardBreakShortcut =\n              blockSpec?.implementation?.meta?.hardBreakShortcut ??\n              \"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              // \"plain\" blocks (e.g. code/math/diagram source) hold text only\n              // (their content is `text*`), which can't contain a `hardBreak`\n              // node - inserting one would split the block into a new one.\n              // They represent line breaks as literal newline characters.\n              if (blockSpec?.config?.content === \"plain\") {\n                tr.insertText(\"\\n\", tr.selection.head);\n                return true;\n              }\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                .scrollIntoView()\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 { 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","// 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 = /(?: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(text: string, options?: FindOptions): 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(\"@\") &&\n    !value.includes(\"://\") &&\n    !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","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\";\nimport { suggestionMarks } from \"./suggestionMarks.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() {\n    return suggestionMarks(this.editor);\n  },\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\";\nimport { suggestionMarks } from \"./suggestionMarks.js\";\n\nexport const BlockGroup = Node.create<{\n  domAttributes?: BlockNoteDOMAttributes;\n}>({\n  name: \"blockGroup\",\n  group: \"childContainer\",\n  content: \"blockGroupChild+\",\n  marks() {\n    return suggestionMarks(this.editor);\n  },\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\";\nimport { suggestionMarks } from \"./suggestionMarks.js\";\n\nexport const Doc = Node.create({\n  name: \"doc\",\n  topNode: true,\n  content: \"blockGroup\",\n  marks() {\n    return suggestionMarks(this.editor);\n  },\n});\n","import {\n  AnyExtension as AnyTiptapExtension,\n  extensions,\n  Node,\n  Extension as TiptapExtension,\n} from \"@tiptap/core\";\nimport { Text } from \"@tiptap/extension-text\";\nimport { Gapcursor } from \"@tiptap/extensions/gap-cursor\";\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  InlineContentBoundaryEditExtension,\n  LinkToolbarExtension,\n  NodeSelectionKeyboardExtension,\n  PlaceholderExtension,\n  PositionMappingExtension,\n  PreviousBlockTypeExtension,\n  ShowSelectionExtension,\n  SideMenuExtension,\n  SourceBlockWithPreviewExtension,\n  SourceInlineContentWithPreviewExtension,\n  SuggestionMenu,\n  TableHandlesExtension,\n  TrailingNodeExtension,\n} from \"../../../extensions/index.js\";\nimport {\n  BackgroundColorExtension,\n  HardBreak,\n  KeyboardShortcutsExtension,\n  LinkExtension,\n  TextAlignmentExtension,\n  TextColorExtension,\n  UniqueID,\n} from \"../../../extensions/tiptap-extensions/index.js\";\nimport { BlockContainer, BlockGroup, Doc } from \"../../../pm-nodes/index.js\";\nimport type {\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n} from \"../../BlockNoteEditor.js\";\nimport type { ExtensionFactoryInstance } from \"../../BlockNoteExtension.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    ...(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    SourceBlockWithPreviewExtension(),\n    SourceInlineContentWithPreviewExtension(),\n    SuggestionMenu(options),\n    HistoryExtension(),\n    InlineContentBoundaryEditExtension(),\n    PositionMappingExtension(),\n    ...(options.trailingBlock !== false ? [TrailingNodeExtension()] : []),\n  ] as ExtensionFactoryInstance[];\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 {\n  getBlockInfoFromSelection,\n  getNodeId,\n} 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   * Maps an extension key to the set of extension keys that declared it as a\n   * dependency via `blockNoteExtensions`. A sub-extension is a dependency of\n   * the extension that declares it, so it must run *before* its parent(s).\n   */\n  private blockNoteExtensionDependents: Map<string, Set<string>> = 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    this.replaceExtension(undefined, extension);\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    /**\n     * When this extension is being added as a dependency declared in another\n     * extension's `blockNoteExtensions`, this is the key of that declaring\n     * (parent) extension.\n     */\n    parentKey?: string,\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    // A sub-extension declared via `blockNoteExtensions` must run before the\n    // extension that declares it. We record this dependency before the\n    // de-duplication check below, so that it applies even when multiple\n    // extensions declare the same sub-extension (and all but the first are\n    // de-duplicated).\n    if (parentKey) {\n      let dependents = this.blockNoteExtensionDependents.get(instance.key);\n      if (!dependents) {\n        dependents = new Set();\n        this.blockNoteExtensionDependents.set(instance.key, dependents);\n      }\n      dependents.add(parentKey);\n    }\n\n    // De-duplicate by key: if an extension with the same key is already\n    // registered, don't register it again. This allows an extension to declare\n    // a dependency on another extension via `blockNoteExtensions` without\n    // conflicting when the user (or another extension) registers that same\n    // extension directly. The first registration wins.\n    if (this.extensions.some((e) => e.key === 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 subExtension of instance.blockNoteExtensions) {\n        this.addExtension(subExtension, instance.key);\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    this.replaceExtension(toUnregister, []);\n  }\n\n  /**\n   * Atomically replace extension instances in the editor.\n   * @param toUnregister - The extensions to unregister, can be a string key, an extension instance, an extension factory, or an array of any of those\n   * @param toRegister - The extensions to register, can be an extension instance, an extension factory, or an array of any of those\n   * @returns void\n   */\n  public replaceExtension(\n    toUnregister:\n      | undefined\n      | string\n      | Extension\n      | ExtensionFactory\n      | (Extension | ExtensionFactory | string | undefined)[],\n    toRegister:\n      | Extension\n      | ExtensionFactoryInstance\n      | (Extension | ExtensionFactoryInstance)[],\n  ): void {\n    // ---- Remove phase (no updatePlugins call) ----\n    const extensionsToRemove = this.resolveExtensions(toUnregister);\n\n    if (toUnregister && !extensionsToRemove.length) {\n      // eslint-disable-next-line no-console\n      console.warn(`No extensions found to unregister`, toUnregister);\n    }\n\n    let didWarnUnregister = false;\n    // We collect both plugin references and plugin keys to remove.\n    // Key-based matching is needed because re-entrant dispatches (e.g. from\n    // y-prosemirror view hooks) can replace plugin instances in the ProseMirror\n    // state with new objects that share the same key, making reference-based\n    // matching unreliable.\n    const pluginRefsToRemove = new Set<Plugin>();\n    const pluginKeysToRemove = new Set<string>();\n    for (const extension of extensionsToRemove) {\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        pluginRefsToRemove.add(plugin);\n        const key = (plugin as any).spec?.key;\n        const keyStr = typeof key === \"object\" && key ? key.key : key;\n        if (typeof keyStr === \"string\") {\n          pluginKeysToRemove.add(keyStr);\n        }\n      });\n      this.extensionPlugins.delete(extension);\n\n      if (extension.tiptapExtensions && !didWarnUnregister) {\n        didWarnUnregister = 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    // ---- Add phase (no updatePlugins call) ----\n    const newExtensions = ([] as (Extension | ExtensionFactoryInstance)[])\n      .concat(toRegister)\n      .filter(Boolean) as (Extension | ExtensionFactoryInstance)[];\n\n    const registeredExtensions = newExtensions\n      .map((ext) => this.addExtension(ext))\n      .filter(Boolean) as Extension[];\n\n    const pluginsToAdd: Plugin[] = [];\n    for (const extension of registeredExtensions) {\n      if (extension?.tiptapExtensions) {\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        // 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.push(plugin);\n        },\n      );\n    }\n\n    // Nothing to do\n    if (\n      !pluginRefsToRemove.size &&\n      !pluginKeysToRemove.size &&\n      !pluginsToAdd.length\n    ) {\n      return;\n    }\n\n    // ---- Single atomic plugin update ----\n    this.updatePlugins((plugins) => [\n      ...plugins.filter((plugin) => {\n        // Fast path: exact reference match\n        if (pluginRefsToRemove.has(plugin)) {\n          return false;\n        }\n        // Fallback: match by key string (handles cases where plugin instances\n        // in the state differ from the ones we tracked)\n        if (pluginKeysToRemove.size) {\n          const key = (plugin as any).spec?.key;\n          const keyStr = typeof key === \"object\" && key ? key.key : key;\n          if (typeof keyStr === \"string\" && pluginKeysToRemove.has(keyStr)) {\n            return false;\n          }\n        }\n        return true;\n      }),\n      ...pluginsToAdd,\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(\n      this.extensions.map((extension) => {\n        // A sub-extension declared via `blockNoteExtensions` must run before the\n        // extension(s) that declared it, so we merge those parents into its\n        // `runsBefore`.\n        const dependents = this.blockNoteExtensionDependents.get(extension.key);\n        if (!dependents?.size) {\n          return extension;\n        }\n        return {\n          key: extension.key,\n          runsBefore: [...(extension.runsBefore ?? []), ...dependents],\n        };\n      }),\n    );\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((a, b) => a - b)\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 = getBlockInfoFromSelection(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                setTextCursorPosition(\n                  tr,\n                  getNodeId(blockInfo.bnBlock.node, tr.doc),\n                  \"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  // 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, tr.doc);\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!, tr.doc));\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      // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string\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 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  );\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 { 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 =\n      this.editor.getExtension<typeof HistoryExtension>(\"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 =\n      this.editor.getExtension<typeof HistoryExtension>(\"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 {\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 {\n  Extension,\n  ExtensionFactory,\n  ExtensionFactoryInstance,\n} 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   * 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<string, string | undefined>;\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 tiptapOptions: EditorOptions = {\n      ...blockNoteTipTapOptions,\n      ...newOptions._tiptapOptions,\n      element: null,\n      autofocus: newOptions.autofocus ?? false,\n      extensions: tiptapExtensions,\n      editorProps: {\n        scrollMargin: { top: 72, bottom: 72, left: 0, right: 0 },\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          // eslint-disable-next-line @typescript-eslint/no-misused-spread\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 = newOptions.initialContent || [\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            JSON.stringify(initialContent),\n        );\n      }\n      const schema = getSchema(tiptapOptions.extensions!);\n      // `blockToNode` (via `isPlainContentNodeType`) resolves the block schema\n      // through `schema.cached.blockNoteEditor`, so stamp it on this throwaway\n      // schema now — the real `pmSchema` is stamped separately below.\n      schema.cached.blockNoteEditor = this;\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    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   * Atomically unregister old extensions and register new ones in a single\n   * plugin update, avoiding re-entrant dispatch issues.\n   */\n  public replaceExtension: ExtensionManager[\"replaceExtension\"] = (\n    ...args: Parameters<ExtensionManager[\"replaceExtension\"]>\n  ) => this._extensionManager.replaceExtension(...args);\n\n  /**\n   * Get an extension from the editor\n   */\n  // Declared as an explicit intersection of the two `ExtensionManager`\n  // overloads rather than `ExtensionManager[\"getExtension\"]`: indexed access on\n  // an overloaded method collapses the signatures, which widened the factory\n  // overload's `ReturnType<ReturnType<T>>` result to `any` (losing e.g. a\n  // returned extension's `store` type).\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    (<const T extends ExtensionFactory>(\n      extension: T,\n    ) => ReturnType<ReturnType<T>> | undefined) = ((extension: any) =>\n    this._extensionManager.getExtension(extension)) 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 type { Dictionary } from \"../i18n/dictionary.js\";\nimport { en } from \"../i18n/locales/index.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  /**\n   * The strings an exporter renders into the produced document (file link\n   * texts, error placeholders). Accepts a locale from\n   * `@blocknote/core/locales` or an editor dictionary; block packages that\n   * ship their own exporter strings (e.g. math, diagram) read their sections\n   * from this same object, exactly as they do from an editor dictionary.\n   *\n   * @default the English strings\n   */\n  dictionary?: { exporter: Dictionary[\"exporter\"] } & {\n    // Block packages read their own sections (e.g. `math`, `diagram`) from\n    // the same object; their types live with those packages.\n    [blockDictionary: string]: unknown;\n  };\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  /**\n   * The strings this exporter renders into the produced document - the\n   * `exporter` section of the configured dictionary (the `dictionary`\n   * option of {@link ExporterOptions}), or the English defaults.\n   */\n  public get dictionary(): Dictionary[\"exporter\"] {\n    return this.options.dictionary?.exporter ?? en.exporter;\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 mapping = this.mappings.styleMapping[key];\n      if (!mapping) {\n        throw new Error(\n          `Exporter is missing a style mapping for style \"${key}\". If this style comes from a separate package, spread that package's exporter mappings into your styleMapping.`,\n        );\n      }\n      const mappedStyle = mapping(value, this);\n      return mappedStyle;\n    });\n    return stylesArray;\n  }\n\n  public mapInlineContent(inlineContent: InlineContent<I, S>) {\n    const mapping = this.mappings.inlineContentMapping[inlineContent.type];\n    if (!mapping) {\n      throw new Error(\n        `Exporter is missing an inline content mapping for inline content type \"${inlineContent.type}\". If this inline content comes from a separate package, spread that package's exporter mappings into your inlineContentMapping.`,\n      );\n    }\n    return mapping(inlineContent, this);\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    const mapping = this.mappings.blockMapping[block.type];\n    if (!mapping) {\n      throw new Error(\n        `Exporter is missing a block mapping for block type \"${block.type}\". If this block comes from a separate package, spread that package's exporter mappings into your blockMapping.`,\n      );\n    }\n    return mapping(block, this, nestingLevel, numberedListIndex, children);\n  }\n}\n","/**\n * An image generated during export (e.g. a rendered formula or diagram): the\n * encoded image bytes plus the dimensions to display it at.\n *\n * The bytes (rather than e.g. a data URL string or a `Blob`) are the source\n * of truth: they carry no encoding ambiguity, work in every environment, and\n * are readable synchronously - each output format converts them at its own\n * boundary (data URL for HTML-based targets, raw bytes for DOCX, base64 for\n * email attachments).\n */\nexport type ExportImage = {\n  /** MIME type of `data`, e.g. `\"image/png\"` or `\"image/svg+xml\"`. */\n  mimeType: string;\n  /** The encoded image bytes. */\n  data: Uint8Array;\n  /**\n   * Dimensions to display the image at, in the target format's units (CSS\n   * pixels, points, ...). For raster images, `data`'s own pixel dimensions\n   * may be larger - images are often rendered at 2-4x for sharpness.\n   */\n  width: number;\n  height: number;\n};\n\n/**\n * Encodes bytes as base64. This papers over a platform gap: until\n * `Uint8Array.prototype.toBase64()` (ES2026) is available in every runtime\n * BlockNote supports, the only universal built-in is `btoa`, which takes\n * binary *strings*. Uses `toBase64` when the runtime has it.\n */\nexport function bytesToBase64(bytes: Uint8Array): string {\n  if (\"toBase64\" in bytes && typeof bytes.toBase64 === \"function\") {\n    return (bytes as Uint8Array & { toBase64(): string }).toBase64();\n  }\n\n  let binary = \"\";\n  for (const byte of bytes) {\n    binary += String.fromCharCode(byte);\n  }\n  return btoa(binary);\n}\n\n/**\n * Encodes an {@link ExportImage}'s bytes as a base64 data URL, for targets\n * that take image sources as URLs (HTML `src` attributes, react-pdf image\n * sources, ...).\n */\nexport function exportImageToDataURL(image: ExportImage): string {\n  return `data:${image.mimeType};base64,${bytesToBase64(image.data)}`;\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    // Deliberately loose on the schema generics, like `BlockMapping` above -\n    // otherwise a mapping declared for one schema can't be reused (e.g.\n    // spread) in a mapping for a schema with different types.\n    exporter: Exporter<any, any, any, 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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"}