UNPKG

@blocknote/react

Version:

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

1 lines 67.5 kB
{"version":3,"file":"confirmDiscardUnsavedComment-CwhAQZ3p.cjs","names":[],"sources":["../src/editor/BlockNoteContext.ts","../src/hooks/useBlockNoteEditor.ts","../src/hooks/useStore.ts","../src/hooks/useExtension.ts","../src/util/useIsomorphicLayoutEffect.ts","../src/hooks/useEditorState.ts","../src/hooks/useEditorDomElement.ts","../src/editor/BlockNoteViewContext.ts","../src/editor/PortalElementOverride.tsx","../src/components/Popovers/GenericPopover.tsx","../src/editor/ComponentsContext.tsx","../src/i18n/dictionary.ts","../src/components/Popovers/PositionPopover.tsx","../src/hooks/useCreateBlockNote.tsx","../src/components/Comments/CommentEditor.tsx","../src/components/Comments/defaultCommentEditorSchema.ts","../src/components/Comments/confirmDiscardUnsavedComment.ts"],"sourcesContent":["import {\n  BlockNoteEditor,\n  BlockNoteSchema,\n  BlockSchema,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"@blocknote/core\";\nimport { createContext, useContext, useState } from \"react\";\n\nexport type BlockNoteContextValue<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n> = {\n  setContentEditableProps?: ReturnType<typeof useState<Record<string, any>>>[1]; // copy type of setXXX from useState\n  editor?: BlockNoteEditor<BSchema, ISchema, SSchema>;\n  colorSchemePreference?: \"light\" | \"dark\";\n};\n\nexport const BlockNoteContext = createContext<\n  BlockNoteContextValue | undefined\n>(undefined);\n\n/**\n * Get the BlockNoteContext instance from the nearest BlockNoteContext provider\n * @param _schema: optional, pass in the schema to return type-safe Context if you're using a custom schema\n */\nexport function useBlockNoteContext<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n>(\n  _schema?: BlockNoteSchema<BSchema, ISchema, SSchema>,\n): BlockNoteContextValue<BSchema, ISchema, SSchema> | undefined {\n  const context = useContext(BlockNoteContext) as any;\n\n  return context;\n}\n","import {\n  BlockNoteEditor,\n  BlockNoteSchema,\n  BlockSchema,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n  InlineContentSchema,\n  StyleSchema,\n} from \"@blocknote/core\";\n\nimport { useBlockNoteContext } from \"../editor/BlockNoteContext.js\";\n\n/**\n * Get the BlockNoteEditor instance from the nearest BlockNoteContext provider\n * @param _schema: optional, pass in the schema to return type-safe BlockNoteEditor if you're using a custom schema\n */\nexport function useBlockNoteEditor<\n  BSchema extends BlockSchema = DefaultBlockSchema,\n  ISchema extends InlineContentSchema = DefaultInlineContentSchema,\n  SSchema extends StyleSchema = DefaultStyleSchema,\n>(\n  _schema?: BlockNoteSchema<BSchema, ISchema, SSchema>,\n): BlockNoteEditor<BSchema, ISchema, SSchema> {\n  const context = useBlockNoteContext(_schema);\n\n  if (!context?.editor) {\n    throw new Error(\n      \"useBlockNoteEditor was called outside of a BlockNoteContext provider or BlockNoteView component\",\n    );\n  }\n\n  return context.editor;\n}\n","// Vendored from https://github.com/TanStack/store/blob/main/packages/react-store/src/index.ts (MIT)\n//\n// See `packages/core/src/util/Store.ts` for why the store itself is vendored. This is the\n// matching React binding, kept behaviourally identical to `@tanstack/react-store@0.7.7` —\n// in particular the `shallow` default comparator, which `useCommentUsers` and\n// `useVersionUsers` rely on to avoid re-rendering when an unrelated user resolves.\n\nimport type { Store } from \"@blocknote/core\";\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/shim/with-selector\";\n\n/**\n * Subscribe to a {@link Store}, optionally selecting a slice of its state.\n *\n * The component re-renders only when the selected value changes under a\n * {@link shallow} comparison, so selectors are free to build a fresh object,\n * `Map` or `Set` on each call.\n */\nexport function useStore<TState, TSelected = NoInfer<TState>>(\n  store: Store<TState>,\n  selector: (state: TState) => TSelected = (d) => d as unknown as TSelected,\n): TSelected {\n  return useSyncExternalStoreWithSelector(\n    store.subscribe,\n    () => store.state,\n    () => store.state,\n    selector,\n    shallow,\n  );\n}\n\n/**\n * Compares two values one level deep, with special handling for `Map`, `Set` and `Date`.\n */\nexport function shallow<T>(objA: T, objB: T): boolean {\n  if (Object.is(objA, objB)) {\n    return true;\n  }\n\n  if (\n    typeof objA !== \"object\" ||\n    objA === null ||\n    typeof objB !== \"object\" ||\n    objB === null\n  ) {\n    return false;\n  }\n\n  if (objA instanceof Map && objB instanceof Map) {\n    if (objA.size !== objB.size) {\n      return false;\n    }\n    for (const [k, v] of objA) {\n      if (!objB.has(k) || !Object.is(v, objB.get(k))) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  if (objA instanceof Set && objB instanceof Set) {\n    if (objA.size !== objB.size) {\n      return false;\n    }\n    for (const v of objA) {\n      if (!objB.has(v)) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  if (objA instanceof Date && objB instanceof Date) {\n    return objA.getTime() === objB.getTime();\n  }\n\n  const keysA = getOwnKeys(objA);\n  if (keysA.length !== getOwnKeys(objB).length) {\n    return false;\n  }\n\n  return keysA.every(\n    (key) =>\n      Object.prototype.hasOwnProperty.call(objB, key) &&\n      Object.is(objA[key as keyof T], objB[key as keyof T]),\n  );\n}\n\nfunction getOwnKeys<T extends object>(obj: T): Array<string | symbol> {\n  return (Object.keys(obj) as Array<string | symbol>).concat(\n    Object.getOwnPropertySymbols(obj),\n  );\n}\n","import {\n  BlockNoteEditor,\n  Extension,\n  ExtensionFactory,\n  Store,\n} from \"@blocknote/core\";\nimport { useBlockNoteEditor } from \"./useBlockNoteEditor.js\";\nimport { useStore } from \"./useStore.js\";\n\n/**\n * Use an extension instance\n */\nexport function useExtension<\n  const T extends ExtensionFactory | Extension | string,\n>(\n  plugin: T,\n  ctx?: { editor?: BlockNoteEditor<any, any, any> },\n): T extends ExtensionFactory\n  ? NonNullable<ReturnType<ReturnType<T>>>\n  : T extends string\n    ? Extension\n    : T extends Extension\n      ? T\n      : never {\n  // eslint-disable-next-line react-hooks/rules-of-hooks\n  const editor = ctx?.editor ?? useBlockNoteEditor();\n\n  const instance = editor.getExtension(plugin as any);\n\n  if (!instance) {\n    throw new Error(\"Extension not found\", { cause: { plugin } });\n  }\n\n  return instance;\n}\n\ntype ExtractStore<T> = T extends Store<infer U> ? U : never;\n\n/**\n * Use the state of an extension\n */\nexport function useExtensionState<\n  T extends ExtensionFactory | Extension,\n  TExtension = T extends ExtensionFactory ? ReturnType<ReturnType<T>> : T,\n  TStore = TExtension extends { store: Store<any> }\n    ? TExtension[\"store\"]\n    : never,\n  TSelected = NoInfer<ExtractStore<TStore>>,\n>(\n  plugin: T | string,\n  ctx?: {\n    editor?: BlockNoteEditor<any, any, any>;\n    selector?: (state: NoInfer<ExtractStore<TStore>>) => TSelected;\n  },\n): TSelected {\n  const extension = useExtension(\n    plugin as ExtensionFactory | Extension | string,\n    ctx,\n  );\n  const { store } = extension;\n  if (!store) {\n    throw new Error(\"Store not found on plugin\", { cause: { plugin } });\n  }\n  return useStore<ExtractStore<TStore>, TSelected>(store, ctx?.selector as any);\n}\n","import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * `useLayoutEffect` in the browser, `useEffect` under SSR — where\n * `useLayoutEffect` cannot run and React warns.\n *\n * Used for latest-ref updates: the ref must be current before any layout\n * effect can trigger an editor event, or a subscription could still invoke\n * the previous render's callback.\n */\nexport const useIsomorphicLayoutEffect =\n  typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n","import type { BlockNoteEditor } from \"@blocknote/core\";\nimport deepEqual from \"fast-deep-equal/es6/react.js\";\nimport { useDebugValue, useState } from \"react\";\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/shim/with-selector\";\nimport { useBlockNoteContext } from \"../editor/BlockNoteContext.js\";\nimport { useIsomorphicLayoutEffect } from \"../util/useIsomorphicLayoutEffect.js\";\n\nexport type EditorStateSnapshot<\n  TEditor extends BlockNoteEditor<any, any, any> | null = BlockNoteEditor<\n    any,\n    any,\n    any\n  > | null,\n> = {\n  editor: TEditor;\n  transactionNumber: number;\n};\n\nexport type UseEditorStateOptions<\n  TSelectorResult,\n  TEditor extends BlockNoteEditor<any, any, any> | null = BlockNoteEditor<\n    any,\n    any,\n    any\n  > | null,\n> = {\n  /**\n   * The editor instance. If not provided, will use the editor from BlockNoteContext.\n   */\n  editor?: TEditor;\n\n  /**\n   * A selector function to determine the value to compare for re-rendering.\n   */\n  selector: (context: EditorStateSnapshot<TEditor>) => TSelectorResult;\n\n  /**\n   * A custom equality function to determine if the editor should re-render.\n   * @default `deepEqual` from `fast-deep-equal`\n   */\n  equalityFn?: (a: TSelectorResult, b: TSelectorResult | null) => boolean;\n\n  /**\n   * The event to subscribe to.\n   * @default \"all\"\n   */\n  on?: \"all\" | \"mount\" | \"selection\" | \"change\";\n};\n\n/**\n * To synchronize the editor instance with the component state,\n * we need to create a separate instance that is not affected by the component re-renders.\n */\nclass EditorStateManager<\n  TEditor extends BlockNoteEditor<any, any, any> | null = BlockNoteEditor<\n    any,\n    any,\n    any\n  > | null,\n> {\n  private transactionNumber = 0;\n\n  private lastTransactionNumber = 0;\n\n  private lastSnapshot: EditorStateSnapshot<TEditor>;\n\n  private editor: TEditor;\n\n  private subscribers = new Set<() => void>();\n\n  constructor(initialEditor: TEditor) {\n    this.editor = initialEditor;\n    this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 };\n\n    this.getSnapshot = this.getSnapshot.bind(this);\n    this.getServerSnapshot = this.getServerSnapshot.bind(this);\n    this.watch = this.watch.bind(this);\n    this.subscribe = this.subscribe.bind(this);\n  }\n\n  /**\n   * Get the current editor instance.\n   */\n  getSnapshot(): EditorStateSnapshot<TEditor> {\n    if (this.transactionNumber === this.lastTransactionNumber) {\n      return this.lastSnapshot;\n    }\n    this.lastTransactionNumber = this.transactionNumber;\n    this.lastSnapshot = {\n      editor: this.editor,\n      transactionNumber: this.transactionNumber,\n    };\n    return this.lastSnapshot;\n  }\n\n  /**\n   * Always disable the editor on the server-side.\n   */\n  getServerSnapshot(): EditorStateSnapshot<null> {\n    return { editor: null, transactionNumber: 0 };\n  }\n\n  /**\n   * Subscribe to the editor instance's changes.\n   */\n  subscribe(callback: () => void): () => void {\n    this.subscribers.add(callback);\n    return () => {\n      this.subscribers.delete(callback);\n    };\n  }\n\n  /**\n   * Watch the editor instance for changes.\n   */\n  watch(\n    nextEditor: BlockNoteEditor<any, any, any> | null,\n    on: \"all\" | \"mount\" | \"selection\" | \"change\",\n  ): undefined | (() => void) {\n    this.editor = nextEditor as TEditor;\n\n    if (this.editor) {\n      /**\n       * This will force a re-render when the editor state changes.\n       * This is to support things like `editor.can().toggleBold()` in components that `useEditor`.\n       * This could be more efficient, but it's a good trade-off for now.\n       */\n      const fn = () => {\n        this.transactionNumber += 1;\n        this.subscribers.forEach((callback) => callback());\n      };\n\n      const currentTiptapEditor = this.editor._tiptapEditor;\n\n      const EVENT_TYPES = {\n        all: [\"transaction\", \"create\", \"mount\", \"unmount\"],\n        // Listen for \"create\" as \"mount\" may fire before the hook is run.\n        mount: [\"create\", \"mount\", \"unmount\"],\n        selection: [\"selectionUpdate\"],\n        change: [\"update\"],\n      } as const;\n\n      for (const eventType of EVENT_TYPES[on]) {\n        currentTiptapEditor.on(eventType, fn);\n      }\n\n      return () => {\n        for (const eventType of EVENT_TYPES[on]) {\n          currentTiptapEditor.off(eventType, fn);\n        }\n      };\n    }\n\n    return undefined;\n  }\n}\n\n/**\n * This hook allows you to watch for changes on the editor instance.\n * It will allow you to select a part of the editor state and re-render the component when it changes.\n * @example\n * ```tsx\n * const { currentSelection } = useEditorState({\n *  selector: snapshot => ({ currentSelection: snapshot.editor?._tiptapEditor.state.selection }),\n * })\n * ```\n */\nexport function useEditorState<TSelectorResult>(\n  options: UseEditorStateOptions<\n    TSelectorResult,\n    BlockNoteEditor<any, any, any>\n  >,\n): TSelectorResult;\n/**\n * This hook allows you to watch for changes on the editor instance.\n * It will allow you to select a part of the editor state and re-render the component when it changes.\n * @example\n * ```tsx\n * const { currentSelection } = useEditorState({\n *  selector: snapshot => ({ currentSelection: snapshot.editor?._tiptapEditor.state.selection }),\n * })\n * ```\n */\nexport function useEditorState<TSelectorResult>(\n  options: UseEditorStateOptions<\n    TSelectorResult,\n    BlockNoteEditor<any, any, any> | null\n  >,\n): TSelectorResult | null;\n\n/**\n * This hook allows you to watch for changes on the editor instance.\n * It will allow you to select a part of the editor state and re-render the component when it changes.\n * @example\n * ```tsx\n * const { currentSelection } = useEditorState({\n *  selector: snapshot => ({ currentSelection: snapshot.editor?._tiptapEditor.state.selection }),\n * })\n * ```\n */\nexport function useEditorState<TSelectorResult>(\n  options:\n    | UseEditorStateOptions<TSelectorResult, BlockNoteEditor<any, any, any>>\n    | UseEditorStateOptions<\n        TSelectorResult,\n        BlockNoteEditor<any, any, any> | null\n      >,\n): TSelectorResult | null {\n  const editorContext = useBlockNoteContext();\n  const editor = options.editor || editorContext?.editor || null;\n  const on = options.on || \"all\";\n\n  const [editorStateManager] = useState(() => new EditorStateManager(editor));\n\n  // Using the `useSyncExternalStore` hook to sync the editor instance with the component state\n  const selectedState = useSyncExternalStoreWithSelector(\n    // eslint-disable-next-line @typescript-eslint/unbound-method -- methods are bound in EditorStateManager constructor\n    editorStateManager.subscribe,\n    // eslint-disable-next-line @typescript-eslint/unbound-method -- methods are bound in EditorStateManager constructor\n    editorStateManager.getSnapshot,\n    // eslint-disable-next-line @typescript-eslint/unbound-method -- methods are bound in EditorStateManager constructor\n    editorStateManager.getServerSnapshot,\n    options.selector as UseEditorStateOptions<\n      TSelectorResult,\n      BlockNoteEditor<any, any, any> | null\n    >[\"selector\"],\n    options.equalityFn ?? deepEqual,\n  );\n\n  useIsomorphicLayoutEffect(() => {\n    return editorStateManager.watch(editor, on);\n  }, [editor, editorStateManager, on]);\n\n  useDebugValue(selectedState);\n\n  return selectedState;\n}\n","import { BlockNoteEditor } from \"@blocknote/core\";\n\nimport { useBlockNoteContext } from \"../editor/BlockNoteContext.js\";\nimport { useEditorState } from \"./useEditorState.js\";\n\n// Returns the editor's DOM element reactively.\nexport function useEditorDOMElement(editor?: BlockNoteEditor<any, any, any>) {\n  const editorContext = useBlockNoteContext();\n  if (!editor) {\n    editor = editorContext?.editor;\n  }\n\n  if (!editor) {\n    throw new Error(\n      \"'editor' is required in `useEditorDOMElement`, either from BlockNoteContext or as a function argument\",\n    );\n  }\n\n  return useEditorState({\n    editor,\n    selector: (ctx) => ctx.editor.domElement,\n    equalityFn: (a, b) => a === b,\n    on: \"mount\",\n  });\n}\n","import { createContext, useContext } from \"react\";\nimport { BlockNoteDefaultUIProps } from \"./BlockNoteDefaultUI.js\";\n\nexport type BlockNoteViewContextValue = {\n  editorProps: {\n    autoFocus?: boolean;\n    contentEditableProps?: Record<string, any>;\n    editable?: boolean;\n  };\n  defaultUIProps: BlockNoteDefaultUIProps;\n  /**\n   * Makes `element` a themed BlockNote root: the classes and color-scheme\n   * attribute the stylesheet keys off, plus whatever the UI library adds (its\n   * own color-scheme attribute, theme CSS variables).\n   *\n   * Applied imperatively because it is used for the portal roots BlockNote\n   * mounts outside React's DOM tree, which cannot be themed with props (see\n   * `PortalElementOverride`). The editor container is themed by rendering the\n   * same values as props instead.\n   */\n  applyThemedRoot: (element: HTMLElement) => void;\n};\n\nexport const BlockNoteViewContext = createContext<\n  BlockNoteViewContextValue | undefined\n>(undefined);\n\nexport function useBlockNoteViewContext():\n  | BlockNoteViewContextValue\n  | undefined {\n  const context = useContext(BlockNoteViewContext) as any;\n\n  return context;\n}\n","import {\n  createContext,\n  ReactNode,\n  useCallback,\n  useContext,\n  useEffect,\n  useInsertionEffect,\n  useState,\n} from \"react\";\n\nimport { useBlockNoteEditor } from \"../hooks/useBlockNoteEditor.js\";\nimport { useEditorDOMElement } from \"../hooks/useEditorDomElement.js\";\nimport { useBlockNoteViewContext } from \"./BlockNoteViewContext.js\";\n\n// Runs in the commit's mutation phase, before any layout effect in the tree.\nconst useIsomorphicInsertionEffect =\n  typeof window !== \"undefined\" ? useInsertionEffect : useEffect;\n\n// Set by `PortalElementOverride` (a root to escape to) and by\n// `PortalElementAnchor` (a UI element's own wrapper); the default comes from\n// the editor itself, see `usePortalElement`.\nconst PortalElementContext = createContext<HTMLElement | null>(null);\n\n/**\n * The element the floating UI below should portal into: the nearest\n * {@link PortalElementAnchor} (the wrapper of the toolbar, side menu, … that\n * opens it), else the nearest {@link PortalElementOverride}'s element, else by\n * default the element wrapping the editor element. In the default layout that\n * is the editor's `bn-container`; with `renderEditor={false}` it is whatever\n * `BlockNoteViewEditor` was rendered into, so the floating UI clips and scrolls\n * with the editor rather than escaping into the layout around it. All of these\n * sit inside a themed `.bn-root`, so portalled UI keeps the editor's styling\n * and color scheme wherever in the DOM it lands.\n *\n * `null` until the editor has mounted, and on the server. Consumers render\n * nothing until it exists.\n */\nexport function usePortalElement(): HTMLElement | null {\n  const override = useContext(PortalElementContext);\n  const editorDOMElement = useEditorDOMElement();\n\n  if (override) {\n    return override;\n  }\n\n  return editorDOMElement?.parentElement ?? null;\n}\n\n/**\n * Resets the portal element for the subtree to the editor's own default (see\n * {@link usePortalElement}). `BlockNoteViewContainer` wraps its content in it:\n * a `BlockNoteView` nested inside another view's floating UI (the comments\n * composer, an editor in a custom block's popover) would otherwise inherit the\n * outer view's anchor or override, an element registered with the outer\n * editor, so the nested editor's own menus and popovers would count as\n * outside it for `isWithinEditor` and the focus tracking built on it.\n */\nexport function PortalElementReset(props: { children?: ReactNode }) {\n  return (\n    <PortalElementContext.Provider value={null}>\n      {props.children}\n    </PortalElementContext.Provider>\n  );\n}\n\n/**\n * Redirects the floating UI below it into `target`, for UI that must escape\n * the editor container — an ancestor's `overflow` clipping it, or a stacking\n * context painting it behind the page (see\n * `MobileFormattingToolbarController`).\n *\n * The portal element is a themed `.bn-root` mounted inside `target`, so\n * portalled UI stays styled wherever it goes. It is created up front rather\n * than rendered, so consumers have it on their first render, and attached in\n * an insertion effect, so it is in the document before any layout effect of\n * the children runs: a UI library that portals eagerly (Ariakit renders its\n * popovers hidden from the start) picks its mount point in a layout effect,\n * and given a still-detached element it re-parents it to `document.body`,\n * outside the editor's registered UI. It is also registered with the editor,\n * so focus inside it still counts as focus within the editor.\n *\n * `undefined` means no redirect: the ambient portal element stays in effect.\n */\nexport function PortalElementOverride(props: {\n  target?: HTMLElement;\n  children?: ReactNode;\n}) {\n  const { target, children } = props;\n\n  const editor = useBlockNoteEditor();\n  const applyThemedRoot = useBlockNoteViewContext()?.applyThemedRoot;\n\n  const [portalElement] = useState(() =>\n    typeof document === \"undefined\" ? null : document.createElement(\"div\"),\n  );\n\n  // An insertion effect, not a layout effect: a parent's layout effect runs\n  // after its children's, and Ariakit picks the mount point of its eagerly\n  // rendered popovers in a layout effect, re-parenting a still-detached\n  // element to `document.body`, outside the editor's registered UI.\n  // How-to-test: as a layout effect, Ariakit re-parents the mobile toolbar's anchor to document.body, so focus in the link form counts as outside the editor and the toolbar unmounts (covered by portalElements: \"has an override root in the document before its children's layout effects run\", and skinFocus, android, ariakit: \"the link button hands focus to the URL input\").\n  useIsomorphicInsertionEffect(() => {\n    if (!portalElement || !target) {\n      return;\n    }\n\n    target.appendChild(portalElement);\n    return () => portalElement.remove();\n  }, [portalElement, target]);\n\n  // React does not render this element, so the same theming the editor\n  // container gets from its props is applied here by hand. Same phase as the\n  // attach above, so children measure themed styles from their first layout\n  // effect on.\n  useIsomorphicInsertionEffect(() => {\n    if (!portalElement || !target) {\n      return;\n    }\n\n    applyThemedRoot?.(portalElement);\n  }, [portalElement, target, applyThemedRoot]);\n\n  // Floating UI portalled out of the editor's DOM tree is still the editor's\n  // UI: registering the element keeps `editor.isWithinEditor` (and the focus\n  // tracking built on it) true for what renders inside. Registered in the\n  // same phase too, so nothing a child focuses from its own effects is ever\n  // judged before the root counts as editor UI.\n  useIsomorphicInsertionEffect(() => {\n    if (!portalElement || !target) {\n      return;\n    }\n\n    editor.registerPortalElement(portalElement);\n    return () => editor.unregisterPortalElement(portalElement);\n  }, [editor, portalElement, target]);\n\n  if (target === undefined) {\n    return children;\n  }\n\n  return (\n    <PortalElementContext.Provider value={portalElement}>\n      {children}\n    </PortalElementContext.Provider>\n  );\n}\n\n/**\n * An anchor for the floating UI a UI element opens (its menus, popovers and\n * forms): a zero-size, absolutely positioned element next to that UI element,\n * inside the wrapper that positions it. What portals into it stays a DOM\n * descendant of that wrapper, so it shares the wrapper's stacking context and\n * visibility (it hides when the UI element hides) while taking no part in its\n * layout.\n *\n * The anchor exists from the first render (created up front and attached to\n * the rendered holder on commit, before any effect runs), so consumers never\n * see a `null` and nothing re-renders to pick it up. The holder is rendered\n * by React so that it, and with it the anchor, is re-attached whenever the\n * wrapper's content is re-rendered.\n */\nfunction usePortalElementAnchor(): {\n  anchor: HTMLElement | null;\n  holder: ReactNode;\n} {\n  const [anchor] = useState(() => {\n    if (typeof document === \"undefined\") {\n      return null;\n    }\n    const element = document.createElement(\"span\");\n    element.className = \"bn-portal-anchor\";\n    return element;\n  });\n\n  const holderRef = useCallback(\n    (holder: HTMLElement | null) => {\n      if (holder && anchor && anchor.parentElement !== holder) {\n        holder.appendChild(anchor);\n      }\n    },\n    [anchor],\n  );\n\n  const holder = (\n    <span\n      ref={holderRef}\n      className={PORTAL_ELEMENT_ANCHOR_HOLDER_CLASS}\n      style={{ position: \"absolute\", width: 0, height: 0, overflow: \"visible\" }}\n    />\n  );\n\n  return { anchor, holder };\n}\n\nconst PORTAL_ELEMENT_ANCHOR_HOLDER_CLASS = \"bn-portal-anchor-holder\";\n\n/**\n * Whether `element` has rendered children other than a\n * {@link PortalElementAnchor}'s holder. The holder means a wrapper that renders\n * an anchor is never empty, so \"the UI element rendered nothing\" has to be\n * checked with this instead of the wrapper's `innerHTML`.\n */\nexport function hasChildrenBesidesPortalElementAnchor(\n  element: HTMLElement,\n): boolean {\n  return Array.from(element.childNodes).some(\n    (node) =>\n      !(\n        node instanceof Element &&\n        node.classList.contains(PORTAL_ELEMENT_ANCHOR_HOLDER_CLASS)\n      ),\n  );\n}\n\n/**\n * Renders a portal anchor inside a UI element's wrapper and makes it the\n * portal element for everything below (see {@link usePortalElementAnchor}): the\n * menus and popovers a toolbar, side menu or table handle opens render inside\n * the wrapper that positions and hides that UI element. Nested menus resolve\n * to the same anchor, never to their parent dropdown, which may clip.\n *\n * The anchor is a sibling of the UI element, not a descendant, so it is never\n * inside a scrolling part of it (iOS WebKit clips positioned descendants of\n * scroll containers); and a `portalElements` override that relocates the\n * wrapper takes the anchor, and so the popups, along with it.\n *\n * Pass a function as `children` to receive the portal element for props that\n * need it explicitly.\n */\nexport function PortalElementAnchor(props: {\n  children?: ReactNode | ((portalElement: HTMLElement | null) => ReactNode);\n}) {\n  const { anchor, holder } = usePortalElementAnchor();\n  const ambient = usePortalElement();\n  const portalElement = anchor ?? ambient;\n\n  const children =\n    typeof props.children === \"function\"\n      ? props.children(portalElement)\n      : props.children;\n\n  return (\n    <>\n      {holder}\n      <PortalElementContext.Provider value={portalElement}>\n        {children}\n      </PortalElementContext.Provider>\n    </>\n  );\n}\n","import {\n  autoUpdate,\n  FloatingFocusManager,\n  FloatingPortal,\n  hide,\n  useDismiss,\n  useFloating,\n  UseFloatingOptions,\n  useHover,\n  useInteractions,\n  useMergeRefs,\n  useTransitionStatus,\n  useTransitionStyles,\n} from \"@floating-ui/react\";\nimport { HTMLAttributes, ReactNode, useEffect, useRef } from \"react\";\n\nimport {\n  hasChildrenBesidesPortalElementAnchor,\n  PortalElementAnchor,\n  usePortalElement,\n} from \"../../editor/PortalElementOverride.js\";\nimport { useBlockNoteEditor } from \"../../hooks/useBlockNoteEditor.js\";\nimport { FloatingUIOptions } from \"./FloatingUIOptions.js\";\n\nexport type GenericPopoverReference =\n  | {\n      // A DOM element to use as the reference element for the popover.\n      element: Element;\n      // To update the popover position, `element.getReferenceBoundingRect`\n      // is called. This flag caches the last result of the call while the\n      // element is mounted to the DOM, so it doesn't update while the\n      // popover is closing and transitioning out. Useful for if the\n      // reference element unmounts, as `element.getReferenceBoundingRect`\n      // would return a `DOMRect` with x, y, width, and height of 0.\n      // Defaults to `true`.\n      cacheMountedBoundingClientRect?: boolean;\n    }\n  | {\n      element: undefined;\n      // When no reference element is provided, this can be provided as an\n      // alternative \"virtual\" element to position the popover around.\n      getBoundingClientRect: () => DOMRect;\n      // Optional per-line client rects, required by floating-ui's `inline()`\n      // middleware. Virtual elements have no default `getClientRects`, so it\n      // must be provided explicitly when `inline()` is used.\n      getClientRects?: () => DOMRectList;\n    }\n  | {\n      element: Element;\n      cacheMountedBoundingClientRect?: boolean;\n      // If both `element` and `getBoundingClientRect` are provided, uses\n      // `getBoundingClientRect` to position the popover, but still treats\n      // `element` as the reference element for all other purposes. When\n      // `cacheMountedBoundingClientRect` is `true` or unspecified, this\n      // function is not called while the reference element is not mounted.\n      getBoundingClientRect: () => DOMRect;\n      // See above.\n      getClientRects?: () => DOMRectList;\n    };\n\n// Returns a modified version of `getBoundingClientRect`, if\n// `reference.element` is passed and `reference.cacheMountedBoundingClientRect`\n// is `true` or `undefined`. In the modified version, each new result is cached\n// and returned while `reference.element` is connected to the DOM. If it is no\n// longer connected, the cache is no longer updated and the last cached result\n// is used.\n//\n// In all other cases, just returns `reference.getBoundingClientRect`, or\n// `reference.element.getBoundingClientRect` if it's not defined.\nexport function getMountedBoundingClientRectCache(\n  reference: GenericPopoverReference,\n) {\n  let lastBoundingClientRect = new DOMRect();\n  const getBoundingClientRect =\n    \"getBoundingClientRect\" in reference\n      ? () => reference.getBoundingClientRect()\n      : () => reference.element.getBoundingClientRect();\n\n  return () => {\n    if (\n      reference.element &&\n      (reference.cacheMountedBoundingClientRect ?? true)\n    ) {\n      if (reference.element.isConnected) {\n        lastBoundingClientRect = getBoundingClientRect();\n      }\n\n      return lastBoundingClientRect;\n    }\n\n    return getBoundingClientRect();\n  };\n}\n\n/**\n * Merges two `whileElementsMounted` handlers into one. Both run when elements\n * mount, and both cleanup functions are called on unmount.\n */\nfunction mergeWhileElementsMounted(\n  a: UseFloatingOptions[\"whileElementsMounted\"],\n  b: UseFloatingOptions[\"whileElementsMounted\"],\n): UseFloatingOptions[\"whileElementsMounted\"] {\n  if (!a) {\n    return b;\n  }\n  if (!b) {\n    return a;\n  }\n\n  return (reference, floating, update) => {\n    const cleanupA = a(reference, floating, update);\n    const cleanupB = b(reference, floating, update);\n    return () => {\n      cleanupA?.();\n      cleanupB?.();\n    };\n  };\n}\n\nexport const GenericPopover = (\n  props: FloatingUIOptions & {\n    reference?: GenericPopoverReference;\n    children: ReactNode;\n  },\n) => {\n  const editor = useBlockNoteEditor();\n  // The ambient portal element — always a resolved, themed, registered root, as\n  // `EditorPortalContext` is only ever provided by `PortalElementOverride` (the default from\n  // `BlockNoteView`, or a controller's / the mobile toolbar's override).\n  // `null` during SSR and for the frame before resolution — handled after the\n  // hooks below.\n  const portalElement = usePortalElement();\n  const {\n    whileElementsMounted: _whileElementsMounted,\n    middleware,\n    ...restFloatingOptions\n  } = props.useFloatingOptions ?? {};\n\n  const { refs, floatingStyles, context, middlewareData } =\n    useFloating<HTMLDivElement>({\n      whileElementsMounted: mergeWhileElementsMounted(\n        autoUpdate,\n        props.useFloatingOptions?.whileElementsMounted,\n      ),\n      middleware: [...(middleware ?? []), hide()],\n      ...restFloatingOptions,\n    });\n\n  const { isMounted, styles } = useTransitionStyles(\n    context,\n    props.useTransitionStylesProps,\n  );\n  const { status } = useTransitionStatus(\n    context,\n    props.useTransitionStatusProps,\n  );\n\n  const dismiss = useDismiss(context, props.useDismissProps);\n  const hover = useHover(context, { enabled: false, ...props.useHoverProps });\n  // Also returns `getReferenceProps` but unused as the reference element may\n  // not even be managed by React, so we may be unable to set them. Seems like\n  // `refs.setReferences` attaches most of the same listeners anyway, but\n  // possible both are needed.\n  const { getFloatingProps } = useInteractions([dismiss, hover]);\n\n  const innerHTML = useRef<string>(\"\");\n  const ref = useRef<HTMLDivElement>(null);\n  const mergedRefs = useMergeRefs([ref, refs.setFloating]);\n\n  useEffect(() => {\n    if (props.reference) {\n      const element =\n        \"element\" in props.reference ? props.reference.element : undefined;\n\n      if (\n        element !== undefined &&\n        (props.focusManagerProps?.disabled || !editor.isWithinEditor(element))\n      ) {\n        // Only set domReference when FloatingFocusManager is disabled.\n        // When FloatingFocusManager is active (disabled !== false) and the\n        // reference is inside the ProseMirror editor, setting domReference\n        // causes floating-ui to call insertAdjacentElement on the reference,\n        // inserting a focus-return <span> into the PM contenteditable. This\n        // triggers PM's MutationObserver and resets the editor selection.\n        // (issue #2525)\n        refs.setReference(element);\n      }\n\n      // Forward `getClientRects` when provided, so floating-ui's `inline()`\n      // middleware can read per-line rects off a virtual reference (it calls\n      // `getClientRects()`, which virtual elements lack by default).\n      const getClientRects =\n        \"getClientRects\" in props.reference\n          ? props.reference.getClientRects\n          : undefined;\n\n      refs.setPositionReference({\n        getBoundingClientRect: getMountedBoundingClientRectCache(\n          props.reference,\n        ),\n        ...(getClientRects ? { getClientRects } : {}),\n        contextElement: element,\n      });\n    }\n  }, [props.reference, refs, props.focusManagerProps?.disabled, editor]);\n\n  // Stores the last rendered `innerHTML` of the popover while it was open. The\n  // `innerHTML` is used while the popover is closing, as the React children\n  // may rerender during this time, causing unwanted behaviour.\n  useEffect(\n    () => {\n      if (status === \"initial\" || status === \"open\") {\n        // Only store while the children have rendered something. In the\n        // render where a controller flips `open` to `false`, its children are\n        // typically already gone while `status` is still \"open\", and that\n        // empty state must not replace the snapshot the closing popover is\n        // about to show. The wrapper is never truly empty though: it always\n        // contains the `PortalElementAnchor` holder.\n        if (ref.current && hasChildrenBesidesPortalElementAnchor(ref.current)) {\n          innerHTML.current = ref.current.innerHTML;\n        }\n      }\n    },\n    // `props.children` is added to the deps, since it's ultimately the HTML of\n    // the children that we're storing.\n    [status, props.reference, props.children],\n  );\n\n  if (!isMounted || !portalElement) {\n    return false;\n  }\n\n  const mergedProps: HTMLAttributes<HTMLDivElement> = {\n    ...props.elementProps,\n    style: {\n      display: \"flex\",\n      ...props.elementProps?.style,\n      zIndex: `calc(var(--bn-ui-base-z-index, 0) + ${props.elementProps?.style?.zIndex || 0})`,\n      ...floatingStyles,\n      ...styles,\n      ...(middlewareData.hide?.referenceHidden\n        ? { visibility: \"hidden\" as const }\n        : {}),\n    },\n    ...getFloatingProps(),\n  };\n\n  if (status === \"close\") {\n    // While the popover is closing, shows its last rendered `innerHTML` while\n    // it was open, instead of the React children. This is because they may\n    // rerender during this time, causing unwanted behaviour.\n    //\n    // When we use the `GenericPopover` for BlockNote's internal UI elements\n    // this isn't a huge deal, as we only pass child components if the popover\n    // should be open. So without this fix, the popover just won't transition\n    // out and will instead appear to hide instantly.\n    return (\n      <FloatingPortal root={portalElement}>\n        <div\n          ref={mergedRefs}\n          {...mergedProps}\n          dangerouslySetInnerHTML={{ __html: innerHTML.current }}\n        />\n      </FloatingPortal>\n    );\n  }\n\n  // The children render inside a `PortalElementAnchor`: the menus and popovers\n  // they open portal into this wrapper instead of the editor container, so\n  // they share its stacking context and visibility (they paint above what the\n  // wrapper paints above, and hide when it hides) and move with it when\n  // `portalElements` relocates it. Rendering them inline instead would clip\n  // them to the toolbar, or, on iOS, to a scrolling one. See\n  // `PortalElementAnchor` for the details; behaviour is pinned by\n  // `tests/src/end-to-end/portals/floatingComponentMenus.test.tsx`.\n  if (!props.focusManagerProps?.disabled) {\n    return (\n      <FloatingPortal root={portalElement}>\n        <FloatingFocusManager {...props.focusManagerProps} context={context}>\n          <div ref={mergedRefs} {...mergedProps}>\n            <PortalElementAnchor>{props.children}</PortalElementAnchor>\n          </div>\n        </FloatingFocusManager>\n      </FloatingPortal>\n    );\n  }\n\n  return (\n    <FloatingPortal root={portalElement}>\n      <div ref={mergedRefs} {...mergedProps}>\n        <PortalElementAnchor>{props.children}</PortalElementAnchor>\n      </div>\n    </FloatingPortal>\n  );\n};\n","import {\n  ReactElement,\n  ChangeEvent,\n  ComponentType,\n  createContext,\n  CSSProperties,\n  ForwardedRef,\n  HTMLInputAutoCompleteAttribute,\n  KeyboardEvent,\n  MouseEvent,\n  ReactNode,\n  useContext,\n} from \"react\";\n\nimport { BlockNoteEditor, User } from \"@blocknote/core\";\nimport { DefaultReactGridSuggestionItem } from \"../components/SuggestionMenu/GridSuggestionMenu/types.js\";\nimport { DefaultReactSuggestionItem } from \"../components/SuggestionMenu/types.js\";\n\ntype ToolbarRootType = {\n  className?: string;\n  children?: ReactNode;\n  onMouseEnter?: () => void;\n  onMouseLeave?: () => void;\n  variant?: \"default\" | \"action-toolbar\";\n};\n\ntype ToolbarButtonType = {\n  className?: string;\n  mainTooltip?: string;\n  secondaryTooltip?: string;\n  icon?: ReactNode;\n  onClick?: (e: MouseEvent) => void;\n  isSelected?: boolean;\n  isDisabled?: boolean;\n  variant?: \"default\" | \"compact\";\n} & (\n  | { children: ReactNode; label?: string }\n  | { children?: undefined; label: string }\n);\n\ntype ToolbarSelectType = {\n  className?: string;\n  items: {\n    text: string;\n    icon: ReactNode;\n    onClick: () => void;\n    isSelected: boolean;\n    isDisabled?: boolean;\n  }[];\n  isDisabled?: boolean;\n  portalElement: HTMLElement | null;\n  /**\n   * When true, the UI library must not move focus into the surface when it\n   * opens (the mobile toolbar: a focus move blurs the editor and closes the\n   * on-screen keyboard). An input inside that asks for focus itself still\n   * gets it.\n   */\n  preventFocusOnOpen?: boolean;\n};\n\ntype MenuButtonType = {\n  className?: string;\n  onClick?: (e: MouseEvent) => void;\n  icon?: ReactNode;\n  onDragStart?: (e: React.DragEvent) => void;\n  onDragEnd?: (e: React.DragEvent) => void;\n  draggable?: boolean;\n} & (\n  | { children: ReactNode; label?: string }\n  | { children?: undefined; label: string }\n);\n\nexport type ComponentProps = {\n  FormattingToolbar: {\n    Root: ToolbarRootType;\n    Button: ToolbarButtonType;\n    Select: ToolbarSelectType;\n  };\n  FilePanel: {\n    Root: {\n      className?: string;\n      tabs: {\n        name: string;\n        tabPanel: ReactNode;\n      }[];\n      openTab: string;\n      setOpenTab: (name: string) => void;\n      defaultOpenTab: string;\n      loading: boolean;\n    };\n    Button: {\n      className?: string;\n      /**\n       * Explicit, because the skins' underlying buttons disagree on the\n       * default (Mantine's is `type=\"button\"`, shadcn's was `\"submit\"`) and\n       * a submit button inside a `Form.Root` must reliably submit on every\n       * skin. `\"submit\"` buttons need no `onClick` - the form's `onSubmit`\n       * is the single commit path, so clicking cannot fire twice.\n       */\n      type: \"button\" | \"submit\";\n      onClick?: () => void;\n    } & (\n      | { children: ReactNode; label?: string }\n      | { children?: undefined; label: string }\n    );\n    FileInput: {\n      className?: string;\n      accept: string;\n      value: File | null;\n      placeholder: string;\n      onChange: (payload: File | null) => void;\n    };\n    TabPanel: {\n      className?: string;\n      children?: ReactNode;\n    };\n    TextInput: {\n      className?: string;\n      value: string;\n      placeholder: string;\n      onChange: (event: ChangeEvent<HTMLInputElement>) => void;\n      onKeyDown?: (event: KeyboardEvent) => void;\n    };\n  };\n  LinkToolbar: {\n    Root: ToolbarRootType;\n    Button: ToolbarButtonType;\n    Select: ToolbarSelectType;\n  };\n  SideMenu: {\n    Root: {\n      className?: string;\n      children?: ReactNode;\n    };\n    Button: {\n      className?: string;\n      onClick?: (e: MouseEvent) => void;\n      icon?: ReactNode;\n      onDragStart?: (e: React.DragEvent) => void;\n      onDragEnd?: (e: React.DragEvent) => void;\n      draggable?: boolean;\n    } & (\n      | { children: ReactNode; label?: string }\n      | { children?: undefined; label: string }\n    );\n  };\n  SuggestionMenu: {\n    Root: {\n      id: string;\n      className?: string;\n      children?: ReactNode;\n    };\n    EmptyItem: {\n      className?: string;\n      children?: ReactNode;\n    };\n    Item: {\n      className?: string;\n      id: string;\n      isSelected: boolean;\n      onClick: () => void;\n      item: Omit<DefaultReactSuggestionItem, \"onItemClick\">;\n    };\n    Label: {\n      className?: string;\n      children?: ReactNode;\n    };\n    Loader: {\n      className?: string;\n    };\n  };\n  GridSuggestionMenu: {\n    Root: {\n      id: string;\n      columns: number;\n      className?: string;\n      children?: ReactNode;\n    };\n    EmptyItem: {\n      columns: number;\n      className?: string;\n      children?: ReactNode;\n    };\n    Item: {\n      className?: string;\n      id: string;\n      isSelected: boolean;\n      onClick: () => void;\n      item: DefaultReactGridSuggestionItem;\n    };\n    // Label: {\n    //   className?: string;\n    //   children?: ReactNode;\n    // };\n    Loader: {\n      columns: number;\n      className?: string;\n      children?: ReactNode;\n    };\n  };\n  TableHandle: {\n    Root: {\n      className?: string;\n      draggable: boolean;\n      onDragStart: (e: React.DragEvent) => void;\n      onDragEnd: () => void;\n      style?: CSSProperties;\n    } & (\n      | { children: ReactNode; label?: string }\n      | { children?: undefined; label: string }\n    );\n    ExtendButton: {\n      className?: string;\n      onClick: (e: React.MouseEvent) => void;\n      onMouseDown: (e: React.MouseEvent) => void;\n      children: ReactNode;\n    };\n  };\n  Comments: {\n    Card: {\n      className?: string;\n      headerText?: string;\n      selected?: boolean;\n      onFocus?: (event: React.FocusEvent) => void;\n      onBlur?: (event: React.FocusEvent) => void;\n      tabIndex?: number;\n      children?: ReactNode;\n    };\n    CardSection: {\n      className?: string;\n      children?: ReactNode;\n    };\n    ExpandSectionsPrompt: {\n      className?: string;\n      children?: ReactNode;\n    };\n    Editor: {\n      className?: string;\n      autoFocus?: boolean;\n      editable: boolean;\n      editor: BlockNoteEditor<any, any, any>;\n      onFocus?: () => void;\n      onBlur?: () => void;\n    };\n    Comment: {\n      className?: string;\n      children?: ReactNode;\n      authorInfo: \"loading\" | User;\n      timeString: string;\n      edited: boolean;\n      actions?: ReactNode;\n      showActions?: boolean | \"hover\";\n      emojiPickerOpen?: boolean;\n    };\n  };\n  Versioning: {\n    /**\n     * The scrollable container for the version-history sidebar (header +\n     * snapshot rows).\n     */\n    Sidebar: {\n      className?: string;\n      children?: ReactNode;\n    };\n    /**\n     * A single row in the version-history sidebar — the live \"current version\"\n     * entry or a stored snapshot.\n     */\n    Snapshot: {\n      className?: string;\n      /** Whether this row is the version currently shown in the editor. */\n      selected?: boolean;\n      /** Whether this row is the baseline the current diff is compared against. */\n      comparing?: boolean;\n      onClick?: () => void;\n      /** Row actions (e.g. the \"...\" menu), revealed on hover. */\n      actions?: ReactNode;\n      children?: ReactNode;\n    };\n  };\n  AttributionTooltip: {\n    /**\n     * The attribution tooltip shown when hovering a suggestion mark. Positioned\n     * by floating-ui and portaled by the controller — this only styles the box.\n     */\n    Root: {\n      className?: string;\n      /**\n       * App-supplied class from `getAttributionMarkClassName` (override path).\n       * When set, `Root` applies it and ignores `backgroundColor`.\n       */\n      markClassName?: string;\n      /**\n       * Per-user author color (default path). Applied inline when there's no\n       * `markClassName`, since the tooltip is portaled away from the mark and\n       * can't inherit the mark's color.\n       */\n      backgroundColor?: string;\n      children?: ReactNode;\n    };\n  };\n  // TODO: We should try to make everything as generic as we can\n  Generic: {\n    Badge: {\n      Root: {\n        className?: string;\n        text: string;\n        icon?: ReactNode;\n        isSelected?: boolean;\n        mainTooltip?: string;\n        secondaryTooltip?: string;\n        onClick?: (event: React.MouseEvent) => void;\n        onMouseEnter?: () => void;\n      };\n      Group: {\n        className?: string;\n        children: ReactNode;\n      };\n    };\n    Form: {\n      Root: {\n        children?: ReactNode;\n        /**\n         * Called on the form's `submit` event. Implementations must render a\n         * real `<form>` and `preventDefault`: native submission is the only\n         * path that works for every input source — mobile IMEs commit\n         * through it without dispatching any key event.\n         */\n        onSubmit?: () => void;\n        /**\n         * The form's submit control, rendered inside the `<form>`. Usually\n         * `ScreenReaderOnlySubmit`; a visible `type=\"submit\"` button to make\n         * it the form's one affordance (the embed tab); or `\"none\"` — then\n         * the form must have exactly one field, or Enter submits nothing.\n         */\n        submitButton: ReactElement | \"none\";\n      };\n      TextInput: {\n        className?: string;\n        name: string;\n        label?: string;\n        variant?: \"default\" | \"large\";\n        icon: ReactNode;\n        rightSection?: ReactNode;\n        autoFocus?: boolean;\n        placeholder?: string;\n        disabled?: boolean;\n        value: string;\n        onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;\n        onChange: (event: ChangeEvent<HTMLInputElement>) => void;\n        autoComplete?: HTMLInputAutoCompleteAttribute;\n        \"aria-activedescendant\"?: string;\n        ref?: ForwardedRef<HTMLInputElement>;\n      };\n    };\n    Menu: {\n      Root: {\n        sub?: boolean;\n        onOpenChange?: (open: boolean) => void;\n        position?:\n          | \"top\"\n          | \"right\"\n          | \"bottom\"\n          | \"left\"\n          | `${\"top\" | \"right\" | \"bottom\" | \"left\"}-${\"start\" | \"end\"}`;\n        portalElement: HTMLElement | null;\n        /** See `ToolbarSelect.preventFocusOnOpen`. */\n        preventFocusOnOpen?: boolean;\n        children?: ReactNode;\n      };\n      Divider: {\n        className?: string;\n      };\n      Dropdown: {\n        className?: string;\n        children?: ReactNode;\n        sub?: boolean;\n      };\n      Item: {\n        className?: string;\n        children?: ReactNode;\n\n        subTrigger?: boolean;\n        icon?: ReactNode;\n        checked?: boolean;\n        onClick?: () => void;\n      };\n      Label: {\n        className?: string;\n        children?: ReactNode;\n      };\n      Trigger: {\n        children?: ReactNode;\n        sub?: boolean;\n      };\n      Button: MenuButtonType;\n    };\n    Popover: {\n      Root: {\n        open?: boolean;\n        onOpenChange?: (open: boolean) => void;\n        position?:\n          | \"top\"\n          | \"right\"\n          | \"bottom\"\n          | \"left\"\n          | `${\"top\" | \"right\" | \"bottom\" | \"left\"}-${\"start\" | \"end\"}`;\n        portalElement: HTMLElement | null;\n        children?: ReactNode;\n      };\n      Content: {\n        className?: string;\n        variant: \"form-popover\" | \"panel-popover\";\n        children?: ReactNode;\n      };\n      Trigger: {\n        children?: ReactNode;\n      };\n    };\n    Toolbar: {\n      Root: ToolbarRootType;\n      Button: ToolbarButtonType;\n      Select: ToolbarSelectType;\n    };\n  };\n};\n\nexport type Components = {\n  [Components in keyof Omit<ComponentProps, \"Generic\">]: {\n    [Component in keyof ComponentProps[Components]]: ComponentType<\n      ComponentProps[Components][Component]\n    >;\n  };\n} & {\n  // only needed as Generic Root/etc elements are 1 level of nesting deeper\n  Generic: {\n    [GenericComponents in keyof ComponentProps[\"Generic\"]]: {\n      [Component in keyof ComponentProps[\"Generic\"][GenericComponents]]: ComponentType<\n        ComponentProps[\"Generic\"][GenericComponents][Component]\n      >;\n    };\n  };\n};\n\nexport const ComponentsContext = createContext<Components | undefined>(\n  undefined,\n);\n\nexport function useComponentsContext(): Components | undefined {\n  return useContext(ComponentsContext)!;\n}\n","import { Dictionary } from \"@blocknote/core\";\nimport { useBlockNoteContext } from \"../editor/BlockNoteContext.js\";\n\nexport function useDictionary(): Dictionary {\n  const ctx = useBlockNoteContext();\n  return ctx!.editor!.dictionary;\n}\n","import { posToDOMRect } from \"@tiptap/core\";\nimport { ReactNode, useMemo } from \"react\";\n\nimport { useBlockNoteEditor } from \"../../hooks/useBlockNoteEditor.js\";\nimport { useEditorDOMElement } from \"../../hooks/useEditorDomElement.js\";\nimport { FloatingUIOptions } from \"./FloatingUIOptions.js\";\nimport { GenericPopover, GenericPopoverReference } from \"./GenericPopover.js\";\n\nexport const PositionPopover = (\n  props: FloatingUIOptions & {\n    position: { from: number; to?: number } | undefined;\n    children: ReactNode;\n  },\n) => {\n  const { position, children, ...floatingUIOptions } = props;\n  const { from, to } = position || {};\n\n  const editor = useBlockNoteEditor<any, any, any>();\n  const editorDOMElement = useEditorDOMElement();\n\n  const reference = useMemo<GenericPopoverReference | undefined>(() => {\n    if (from === undefined || to === undefined) {\n      return undefined;\n    }\n\n    return {\n      // Use first child as the editor DOM element may itself be scrollable.\n      // For FloatingUI to auto-update the position during scrolling, the\n      // `contextElement` must be a descendant of the scroll container.\n      element: editorDOMElement?.firstElementChild || undefined,\n      getBoundingClientRect: () =>\n        posToDOMRect(editor.prosemirrorView, from, to ?? from),\n    };\n  }, [editor, editorDOMElement, from, to]);\n\n  return (\n    <GenericPopover reference={reference} {...floatingUIOptions}>\n      {position !== undefined && children}\n    </GenericPopover>\n  );\n};\n","import {\n  BlockNoteEditor,\n  BlockNoteEditorOptions,\n  CustomBlockNoteSchema,\n  DefaultBlockSchema,\n  DefaultInlineContentSchema,\n  DefaultStyleSchema,\n} from \"@blocknote/core\";\nimport { DependencyList, useMemo } from \"react\";\n\n/**\n * Hook to instantiate a BlockNote Editor instance in React\n */\nexport const useCreateBlockNote = <\n  Options extends Partial<BlockNoteEditorOptions<any, any, any>> | undefined,\n>(\n  options: Options = {} as Options,\n  deps: DependencyList = [],\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 useMemo(() => {\n    const editor = BlockNoteEditor.create(options) as any;\n    if (window) {\n      // for testing / dev purposes\n      (window as any).ProseMirror = editor._tiptapEditor;\n    }\n    return editor;\n  }, deps); //eslint-disable-line react-hooks/exhaustive-deps\n};\n","import { BlockNoteEditor } from \"@blocknote/core\";\nimport { ReactNode, useCallback, useEffect, useState } from \"react\";\nimport { useComponentsContext } from \"../../editor/ComponentsContext.js\";\nimport { useEditorState } from \"../../hooks/useEditorState.js\";\n\n/**\n * The CommentEditor component displays an editor for creating or editing a comment.\n * Currently, we also use the non-editable version for displaying a comment.\n *\n * It's used:\n * - to create a new comment (FloatingComposer.tsx)\n * - As the last item in a Thread, to compose a reply (Thread.tsx)\n * - To edit or display an existing comment (Comment.tsx)\n *\n */\nexport const CommentEditor = (props: {\n  autoFocus?: boolean;\n  editable: boolean;\n  actions?: (args: { isFocused: boolean; isEmpty: boolean }) => ReactNode;\n  editor: BlockNoteEditor<any, any, any>;\n}) => {\n  const [isFocused, setIsFocused] = useState(false);\n  const isEmpty = useEditorState({\n    editor: props.editor,\n    selector: ({ editor }) => editor.isEmpty,\n  });\n\n  const components = useComponentsContext()!;\n\n  const onFocus = useCallback(() => {\n    setIsFocused(true);\n  }, []);\n\n  const onBlur = useCallback(() => {\n    setIsFocused(false);\n  }, []);\n\n  // When we click the edit button on a comment, we also want to focus the\n  // comment editor\n  useEffect(() => {\n    if (props.editable && props.autoFocus) {\n      props.editor.focus();\n    }\n  }, [props.autoFocus, props.editable, props.editor]);\n\n  return (\n    <>\n      <components.Comments.Editor\n        autoFocus={props.autoFocus}\n        className=\"bn-comment-editor\"\n        editor={props.editor}\n        onFocus={onFocus}\n        onBlur={onBlur}\n        editable={props.editable}\n      />\n      {props.actions && (\n        <div className={\"bn-comment-actions-wrapper\"}>\n          {props.actions({ isFocused, isEmpty })}\n        </div>\n      )}\n    </>\n  );\n};\n","import { BlockNoteSchema, defaultStyleSpecs } from \"@blocknote/core\";\nimport { createParagraphBlockSpec } from \"@blocknote/core\";\n\n// this is quite convoluted. we'll clean this up when we make\n// it easier to extend / customize the default blocks\n\n// remove textColor, backgroundColor from styleSpecs\nconst {\n  textColor: _textColor,\n  backgroundColor: _backgroundColor,\n  ...styleSpecs\n} = defaultStyleSpecs;\n\n// the schema to use for comments\nexport const defaultCommentEditorSchema = BlockNoteSchema.create({\n  blockSpecs: {\n    paragraph: createParagraphBlockSpec(),\n  },\n  styleSpecs,\n});\n","/**\n * Decides whether comment editor content may be discarded, prompting the user\n * for confirmation when there is unsaved content and confirmation is enabled.\n *\n * Used by the comment composers (new comment, reply and edit) when they're\n * dismissed (e.g. by clicking outside or pressing Escape), so the user doesn't\n * silently lose text they've typed.\n *\n * @returns `true` when it's safe to discard (nothing unsaved, confirmation\n * disabled, or the user accepted the prompt), and `false` when the user\n * cancelled and the editor should stay open.\n */\nexport function confirmDiscardUnsavedComment(opts: {\n  /**\n   * Whether the editor(s) being dismissed currently hold unsaved content.\n   */\n  hasUnsavedContent: boolean;\n  /**\n   * Whether the confirmation prompt is enabled (see the `confirmBeforeDiscard`\n   * option on the comments extension).\n   */\n  confirmBeforeDiscard: boolean;\n  /**\n   * The message shown in the confirmation prompt.\n   */\n  message: string;\n  /**\n   * The confirm implementation. Defaults to `window.confirm`; injectable for\n   * testing.\n   */\n  confirm?: (message: string) => boolean;\n}): boolean {\n  if (!opts.hasUnsavedContent || !opts.confirmBeforeDiscard) {\n    return true;\n  }\n\n  const confirm = opts.confirm ?? ((message) => window.confirm(message));\n  return confirm(opts.message);\n}\n"],"mappings":"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"}