{"version":3,"file":"y.cjs","names":[],"sources":["../src/y/extensions/YAttributionMarks.ts","../src/y/extensions/AttributionExtension.ts","../src/y/extensions/RelativePositionMapping.ts","../src/y/extensions/blockMatchNodes.ts","../src/y/extensions/YSync.ts","../src/y/utils.ts","../src/y/extensions/Suggestions.ts","../src/y/extensions/Versioning.ts","../src/y/extensions/YCursorPlugin.ts","../src/y/extensions/DiffVersioningExtension.ts","../src/y/extensions/index.ts","../src/y/comments/yjsHelpers.ts","../src/y/comments/YjsThreadStoreBase.ts","../src/y/comments/RESTYjsThreadStore.ts","../src/y/comments/YjsThreadStore.ts","../src/y/versioning/yhub.ts"],"sourcesContent":["import { Mark } from \"@tiptap/core\";\nimport { Mark as PMMark, MarkSpec } from \"prosemirror-model\";\nimport {\n  createExtension,\n  type ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport { BLOCK_LEVEL_SUGGESTION_GROUP } from \"../../pm-nodes/suggestionMarks.js\";\nimport { NON_FORMATTING_MARK_GROUP } from \"../../schema/markGroups.js\";\nimport {\n  fallbackColorForUserId,\n  userColorPalette,\n  userColorVarNames,\n} from \"../../user/index.js\";\n\n/**\n * Describes a suggestion mark to {@link GetAttributionMarkClassName}: whether it\n * wraps inline content or a whole block, and which kind of change it represents.\n * `modificationType: \"format\"` corresponds to the `y-attributed-format`\n * (modification) mark.\n */\nexport type AttributionMarkStyleInfo = {\n  contentType: \"inline-content\" | \"block\";\n  modificationType: \"insert\" | \"delete\" | \"format\";\n};\n\n/**\n * The class name(s) an app returns to style a suggestion mark. Either:\n * - a single string applied to *both* the mark content and its hover tooltip, or\n * - `{ content, tooltip }` to style the mark content and the tooltip\n *   independently.\n */\nexport type AttributionMarkClassNames =\n  | string\n  | { content: string; tooltip: string };\n\n/**\n * Optional callback to override suggestion-mark styling. Given a mark's\n * {@link AttributionMarkStyleInfo}, it returns the class name(s) to apply (see\n * {@link AttributionMarkClassNames}). When a class is returned, the default\n * per-user color (the `--user-color-*` custom properties and the built-in\n * `.bn-suggestion-mark` / `.bn-suggestion-node` styling) is *not* applied to the\n * mark content, so the class fully controls its appearance — letting an app\n * color suggestions by change type (e.g. green insertions, red deletions)\n * instead of by author.\n */\nexport type GetAttributionMarkClassName = (\n  info: AttributionMarkStyleInfo,\n) => AttributionMarkClassNames;\n\n/**\n * Resolve the {@link AttributionMarkClassNames} returned by a\n * {@link GetAttributionMarkClassName} callback to the class name for a single\n * target (the mark `content` or its `tooltip`).\n */\nexport const resolveAttributionMarkClassName = (\n  result: AttributionMarkClassNames | undefined,\n  target: \"content\" | \"tooltip\",\n): string | undefined =>\n  result === undefined\n    ? undefined\n    : typeof result === \"string\"\n      ? result\n      : result[target];\n\n/**\n * Shared mark view for the attribution marks (insert / delete / modification).\n * It renders the marked content and tags the wrapper with the author(s) via\n * `data-*` attributes. The attribution tooltip shown on hover is handled\n * separately by the `AttributionExtension`, which reads those attributes\n * straight from the DOM — keeping this mark view purely presentational and the\n * tooltip state off of module scope.\n *\n * Author colors are *not* baked into the (deterministic) mark attrs. Instead the\n * wrapper sets the generic `--user-color-*` custom properties the Block.css rules\n * read, sourcing them from the author's *per-user* CSS variable via\n * `var(--user-color-<id>-*, <fallback>)`. Those per-user variables are populated\n * on the editor root by `AttributionExtension` once the user resolves, so a mark\n * shows the deterministic palette fallback immediately and recolors to the\n * author's own color purely through the CSS cascade — no decoration, no doc\n * transaction. See `userColors.ts`.\n */\nconst createAttributionMarkView =\n  (\n    type: \"insert\" | \"delete\" | \"modification\",\n    options?: {\n      editor?: BlockNoteEditor<any, any, any>;\n      getAttributionMarkClassName?: GetAttributionMarkClassName;\n    },\n  ) =>\n  ({ mark, inline }: { mark: PMMark; inline: boolean }) => {\n    const editor = options?.editor;\n    // `<ins>`/`<del>` are semantic elements. The modification mark has no\n    // dedicated element, so it renders as a `<span>` inline or a `<div>` over a\n    // block, matching its `parseDOM` rules.\n    const tag =\n      type === \"insert\"\n        ? \"ins\"\n        : type === \"delete\"\n          ? \"del\"\n          : inline\n            ? \"span\"\n            : \"div\";\n    const dom = document.createElement(tag);\n\n    Object.assign(dom.dataset, {\n      userIds: JSON.stringify(mark.attrs[\"userIds\"]),\n      inline: String(inline),\n    });\n    if (type === \"modification\") {\n      dom.dataset[\"type\"] = \"modification\";\n      dom.dataset[\"format\"] = JSON.stringify(mark.attrs[\"format\"]);\n    }\n\n    // Optional app-provided override: `y-attributed-format` is exposed as\n    // `\"format\"`. When a class is returned it's applied to the content element\n    // *instead of* the built-in `.bn-suggestion-mark` / `.bn-suggestion-node`\n    // classes, and the per-user `--user-color-*` properties are omitted, so the\n    // class fully controls the appearance (background, text color, etc.) with no\n    // per-user color leaking through (see the type doc).\n    const contentClassName = resolveAttributionMarkClassName(\n      options?.getAttributionMarkClassName?.({\n        contentType: inline ? \"inline-content\" : \"block\",\n        modificationType: type === \"modification\" ? \"format\" : type,\n      }),\n      \"content\",\n    );\n\n    // The wrapper is always `display: contents` so it never generates a box of\n    // its own — an inline `<ins>`/`<del>` around block/table content (e.g. a\n    // suggestion spanning table cells) would otherwise break the normal layout.\n    // Because a `display: contents` element paints nothing, the highlight is\n    // applied to the inner content span (see `.bn-suggestion-mark` in Block.css)\n    // using the `--user-color-*` custom properties, which cascade down from here.\n    // They're sourced from the author's per-user variable (populated on the\n    // editor root by `AttributionExtension`) with the deterministic palette as a\n    // fallback, so a mark is colored before the user resolves and recolors via\n    // the cascade afterward. When an override class owns the styling, no per-user\n    // color is applied at all.\n    const userIds = (mark.attrs[\"userIds\"] as string[] | null) ?? [];\n    const firstId = userIds[0];\n    const fallback = firstId\n      ? fallbackColorForUserId(firstId)\n      : userColorPalette[0];\n    if (contentClassName) {\n      dom.style.cssText = \"display: contents\";\n    } else {\n      const light = firstId\n        ? `var(${userColorVarNames(firstId).light}, ${fallback.light})`\n        : fallback.light;\n      const dark = firstId\n        ? `var(${userColorVarNames(firstId).dark}, ${fallback.dark})`\n        : fallback.dark;\n      dom.style.cssText =\n        \"display: contents\" +\n        `; --user-color-light: ${light}; --user-color-dark: ${dark}`;\n    }\n\n    const contentDOM = document.createElement(\"span\");\n    if (inline) {\n      // Inline content: the span is a real inline box that carries the highlight\n      // (default path) or the app-provided override class.\n      contentDOM.className =\n        contentClassName ??\n        (type === \"delete\"\n          ? \"bn-suggestion-mark bn-suggestion-mark--delete\"\n          : \"bn-suggestion-mark\");\n    } else {\n      // Block-level marks wrap block/table structure (e.g. <tr>/<td>/<p>). The\n      // span must be `display: contents` so it doesn't inject an inline box into\n      // the table layout (which triggers the browser's anonymous-table fixup and\n      // breaks the table). Such a span has no box to paint a background on, so\n      // the `.bn-suggestion-node` rule highlights its children (the wrapped\n      // nodes) instead. An override class is applied to the same span, so it\n      // should likewise target its children (e.g. `.my-class > *`).\n      contentDOM.style.display = \"contents\";\n      contentDOM.className =\n        contentClassName ??\n        (type === \"delete\"\n          ? \"bn-suggestion-node bn-suggestion-node--delete\"\n          : \"bn-suggestion-node\");\n      if (type === \"delete\") {\n        // A deleted block shows a localized \"Deleted\" badge via a `::before`\n        // (see Block.css). The badge text is passed down as a CSS string token\n        // in `--deleted-label` so the stylesheet stays locale-agnostic; the\n        // wrapper is `display: contents` and can't paint a pseudo-element of its\n        // own, so the rule renders the badge on the wrapped node instead, which\n        // inherits this custom property.\n        const label = editor?.dictionary.suggestion_changes.deleted;\n        if (label) {\n          contentDOM.style.setProperty(\n            \"--deleted-label\",\n            JSON.stringify(label),\n          );\n        }\n      }\n    }\n    dom.appendChild(contentDOM);\n\n    return {\n      dom,\n      contentDOM,\n    };\n  };\n\nexport const YAttributedInsertion = Mark.create<{\n  getAttributionMarkClassName?: GetAttributionMarkClassName;\n}>({\n  name: \"y-attributed-insert\",\n  inclusive: false,\n  excludes: \"\",\n  // Two groups: `BLOCK_LEVEL_SUGGESTION_GROUP` lets the mark sit on block nodes\n  // (see `suggestionMarks`), so a whole block can be marked as inserted in\n  // suggestion mode; `NON_FORMATTING_MARK_GROUP` lets it annotate text inside\n  // `\"plain\"` blocks (e.g. code blocks) — see `nonFormattingMarks`.\n  group: `${BLOCK_LEVEL_SUGGESTION_GROUP} ${NON_FORMATTING_MARK_GROUP}`,\n  addAttributes() {\n    return {\n      userIds: { default: null },\n    };\n  },\n  addMarkView() {\n    return createAttributionMarkView(\"insert\", {\n      getAttributionMarkClassName: this.options.getAttributionMarkClassName,\n    });\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== this.name) {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n    } satisfies MarkSpec;\n  },\n});\n\nexport const YAttributedDeletion = Mark.create<{\n  editor?: BlockNoteEditor<any, any, any>;\n  getAttributionMarkClassName?: GetAttributionMarkClassName;\n}>({\n  name: \"y-attributed-delete\",\n  inclusive: false,\n  excludes: \"\",\n  group: `${BLOCK_LEVEL_SUGGESTION_GROUP} ${NON_FORMATTING_MARK_GROUP}`,\n  addAttributes() {\n    return {\n      userIds: { default: null },\n    };\n  },\n  addMarkView() {\n    return createAttributionMarkView(\"delete\", {\n      editor: this.options.editor,\n      getAttributionMarkClassName: this.options.getAttributionMarkClassName,\n    });\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== this.name) {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n    } satisfies MarkSpec;\n  },\n});\n\nexport const YAttributedFormat = Mark.create<{\n  getAttributionMarkClassName?: GetAttributionMarkClassName;\n}>({\n  name: \"y-attributed-format\",\n  inclusive: false,\n  excludes: \"\",\n  group: `${BLOCK_LEVEL_SUGGESTION_GROUP} ${NON_FORMATTING_MARK_GROUP}`,\n  addAttributes() {\n    return {\n      userIds: { default: null },\n      format: { default: null },\n    };\n  },\n  addMarkView() {\n    return createAttributionMarkView(\"modification\", {\n      getAttributionMarkClassName: this.options.getAttributionMarkClassName,\n    });\n  },\n  extendMarkSchema(extension) {\n    if (extension.name !== this.name) {\n      return {};\n    }\n    return {\n      blocknoteIgnore: true,\n    } satisfies MarkSpec;\n  },\n});\n\n/**\n * Bundles the three `y-attributed-*` suggestion marks into a single BlockNote\n * extension, so they can be registered wherever they're actually needed (the\n * Yjs collaboration extension, or a test that exercises suggestions) instead of\n * living in the default schema. The marks opt into being allowed on block nodes\n * via their `blockLevelSuggestion` option — see `suggestionMarks`.\n */\nexport const YAttributionMarksExtension = createExtension(\n  ({\n    options,\n  }: ExtensionOptions<\n    { getAttributionMarkClassName?: GetAttributionMarkClassName } | undefined\n  >) => ({\n    key: \"yAttributionMarks\",\n    tiptapExtensions: [\n      YAttributedInsertion.configure({\n        getAttributionMarkClassName: options?.getAttributionMarkClassName,\n      }),\n      YAttributedDeletion.configure({\n        getAttributionMarkClassName: options?.getAttributionMarkClassName,\n      }),\n      YAttributedFormat.configure({\n        getAttributionMarkClassName: options?.getAttributionMarkClassName,\n      }),\n    ],\n  }),\n);\n","import { getChangedRanges } from \"@tiptap/core\";\nimport { Plugin, PluginKey, type Transaction } from \"prosemirror-state\";\nimport {\n  createExtension,\n  createStore,\n  type ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport {\n  colorsForUserIds,\n  userColorVarNames,\n  normalizeToUserStore,\n  type UserStoreOrResolver,\n} from \"../../user/index.js\";\nimport {\n  resolveAttributionMarkClassName,\n  YAttributionMarksExtension,\n  type GetAttributionMarkClassName,\n} from \"./YAttributionMarks.js\";\n\n/** The attribution marks, mapped to their `modificationType`. */\nconst ATTRIBUTION_MARK_TYPES = {\n  \"y-attributed-insert\": \"insert\",\n  \"y-attributed-delete\": \"delete\",\n  \"y-attributed-format\": \"format\",\n} as const;\n\nconst ATTRIBUTION_LOAD_PLUGIN_KEY = new PluginKey(\"attributionLoadUsers\");\n\n/** Wrapper of an attribution mark; carries its author(s) in `data-user-ids`. */\nconst ATTRIBUTION_MARK_SELECTOR = \"[data-user-ids]\";\n\n/** Parse the JSON-encoded `data-user-ids` attribute; `[]` if missing/malformed. */\nconst parseUserIds = (userIdsJSON: string | undefined): string[] => {\n  if (!userIdsJSON) {\n    return [];\n  }\n  let userIds: unknown;\n  try {\n    userIds = JSON.parse(userIdsJSON);\n  } catch {\n    return [];\n  }\n  return Array.isArray(userIds) ? userIds.map(String) : [];\n};\n\n/**\n * The changed format keys from a modification mark's `data-format` (e.g.\n * `[\"bold\", \"italic\"]`); `[]` if missing/malformed or an empty change. This is\n * the raw change context — turning it into a localized label (e.g.\n * `\"Bold, Italic\"`) is a view concern owned by the React layer's\n * `formatChangeLabel`, so core stays i18n-agnostic here.\n */\nconst parseFormatKeys = (formatJSON: string | undefined): string[] => {\n  if (!formatJSON) {\n    return [];\n  }\n  let format: unknown;\n  try {\n    format = JSON.parse(formatJSON);\n  } catch {\n    return [];\n  }\n  if (typeof format !== \"object\" || format === null) {\n    return [];\n  }\n  return Object.keys(format);\n};\n\n/**\n * The element with a real box to anchor the tooltip to. The wrapper is\n * `display: contents` (no box of its own), so use its content span child,\n * falling back further for block marks.\n */\nconst getReferenceElement = (wrapper: Element): Element => {\n  const content = wrapper.firstElementChild ?? wrapper;\n  const rect = content.getBoundingClientRect();\n  if (rect.width || rect.height) {\n    return content;\n  }\n  return content.firstElementChild ?? content;\n};\n\n/**\n * The box the tooltip anchors to. The wrapper is `display: contents` (no box of\n * its own), so use its content span child, falling back further for block marks.\n * Exported for the React controller's floating-ui `getBoundingClientRect`.\n */\nexport const getReferenceRect = (wrapper: Element): DOMRect =>\n  getReferenceElement(wrapper).getBoundingClientRect();\n\n/**\n * The per-line client rects of the reference element, for floating-ui's\n * `inline()` middleware — it needs one rect per line to position off a\n * multi-line mark, and virtual elements don't get a default `getClientRects`.\n */\nexport const getReferenceClientRects = (wrapper: Element): DOMRectList =>\n  getReferenceElement(wrapper).getClientRects();\n\n/**\n * State for the currently-hovered suggestion mark's tooltip (`undefined` when\n * none). The extension computes it; a React controller renders + positions it\n * (see `AttributionTooltipController`).\n */\nexport type AttributionTooltipState = {\n  /** The wrapper element the tooltip anchors to (floating-ui reference). */\n  anchor: HTMLElement;\n  /** Per-user background color, resolved from the user store (default path). */\n  color: string;\n  /** The kind of change — `format` is the modification mark. */\n  modificationType: \"insert\" | \"delete\" | \"format\";\n  /** Whether the mark wraps inline content or a whole block. */\n  contentType: \"inline-content\" | \"block\";\n  /** Resolved usernames (falls back to raw ids), for custom renderers. */\n  users: string[];\n  /**\n   * The changed format keys (e.g. `[\"bold\", \"italic\"]`), present only for\n   * `format` marks. This is the raw change context — the view layer turns it\n   * into a localized label via its `formatChangeLabel`.\n   */\n  format?: string[];\n  /**\n   * Class name from the `getAttributionMarkClassName` callback (override path).\n   * When present, the tooltip applies this and skips the inline `color`.\n   */\n  className?: string;\n};\n\n/**\n * Resolves the attribution tooltip state for suggestion marks on hover (exposed\n * via a store for React), and loads each mark's author so its color/username\n * resolves. Marks nest, so a single delegated `mouseover` listener picks the\n * `closest` wrapper to the pointer — the innermost mark wins.\n */\nexport const AttributionExtension = createExtension(\n  ({\n    options,\n  }: ExtensionOptions<\n    | {\n        /** Resolves authors to usernames. Optional; unresolved ids show raw. */\n        resolveUsers?: UserStoreOrResolver;\n        /** See {@link GetAttributionMarkClassName}. */\n        getAttributionMarkClassName?: GetAttributionMarkClassName;\n      }\n    | undefined\n  >) => {\n    const userStore = normalizeToUserStore(options?.resolveUsers);\n    const getAttributionMarkClassName = options?.getAttributionMarkClassName;\n\n    const store = createStore<AttributionTooltipState | undefined>(undefined);\n\n    // Load the authors of the attribution marks in `tr`'s changed ranges, so\n    // their colors/usernames resolve (colors then flow to marks via `syncRootVars`).\n    // `getChangedRanges` covers mark-only steps too — which suggestion mode adds\n    // over existing text and `tr.changedRange()` would miss.\n    const loadChangedUsers = (tr: Transaction) => {\n      const ranges = getChangedRanges(tr);\n      if (ranges.length === 0) {\n        return;\n      }\n      // Most changes are local (often several steps in one small span), so scan a\n      // single range spanning all of them rather than each range individually.\n      let from = Infinity;\n      let to = -Infinity;\n      for (const { newRange } of ranges) {\n        from = Math.min(from, newRange.from);\n        to = Math.max(to, newRange.to);\n      }\n\n      const ids = new Set<string>();\n      tr.doc.nodesBetween(from, to, (node) => {\n        for (const mark of node.marks) {\n          if (\n            ATTRIBUTION_MARK_TYPES[\n              mark.type.name as keyof typeof ATTRIBUTION_MARK_TYPES\n            ]\n          ) {\n            const userIds = mark.attrs[\"userIds\"] as string[] | null;\n            userIds?.forEach((id) => ids.add(id));\n          }\n        }\n        return true;\n      });\n      if (ids.size > 0) {\n        void userStore.loadUsers(Array.from(ids));\n      }\n    };\n\n    return {\n      key: \"attribution\",\n      userStore,\n      store,\n      prosemirrorPlugins: [\n        // Marks arrive in a transaction (suggestion mode, viewing suggestions,\n        // version preview), so resolve their authors as the doc changes.\n        new Plugin({\n          key: ATTRIBUTION_LOAD_PLUGIN_KEY,\n          state: {\n            init: () => null,\n            apply: (tr) => {\n              if (tr.docChanged) {\n                loadChangedUsers(tr);\n              }\n              return null;\n            },\n          },\n        }),\n      ],\n      mount({ dom, root, signal }) {\n        // Write each resolved author's color to the editor root as per-user CSS\n        // variables (`--user-color-<id>-{light,dark}`) that the mark wrappers read\n        // via `var(..., <fallback>)`, so the cascade recolors marks once a color\n        // resolves. Color-less users have theirs removed so the fallback applies.\n        const syncRootVars = () => {\n          for (const [id, user] of userStore.store.state) {\n            const { light, dark } = userColorVarNames(id);\n            if (user.color && user.colorLight) {\n              dom.style.setProperty(light, user.colorLight);\n              dom.style.setProperty(dark, user.color);\n            } else {\n              dom.style.removeProperty(light);\n              dom.style.removeProperty(dark);\n            }\n          }\n        };\n\n        // The wrapper currently showing a tooltip, so we don't re-emit on every\n        // `mouseover` over the same mark.\n        let activeAnchor: HTMLElement | undefined;\n\n        // The mark's authors as usernames, falling back to the raw id when not\n        // cached (`getUser` is cache-only; ids load on hover, see `onPointerOver`).\n        const usersLabelArray = (userIdsJSON: string | undefined): string[] =>\n          parseUserIds(userIdsJSON).map(\n            (id) => userStore.getUser(id)?.username ?? id,\n          );\n\n        // A stable identity string for a wrapper (empty if unattributed), used to\n        // (a) test whether a mark is attributed and (b) group adjacent marks with\n        // the *same* attribution under one tooltip. It's an internal grouping key,\n        // not the displayed text — that's composed in the view from `users` and\n        // the format label — so it's built from raw `data-*` (ids + format keys)\n        // and stays free of i18n/username resolution.\n        const attributionIdentity = (wrapper: HTMLElement) => {\n          const ids = parseUserIds(wrapper.dataset[\"userIds\"]);\n          if (ids.length === 0) {\n            return \"\";\n          }\n          const format = parseFormatKeys(wrapper.dataset[\"format\"]);\n          return `${format.join(\",\")}:${ids.join(\",\")}`;\n        };\n\n        // Build the tooltip state from a wrapper's `data-*` attributes.\n        const buildState = (anchor: HTMLElement): AttributionTooltipState => {\n          const isModification = anchor.dataset[\"format\"] !== undefined;\n          const modificationType: AttributionTooltipState[\"modificationType\"] =\n            isModification\n              ? \"format\"\n              : anchor.tagName === \"INS\"\n                ? \"insert\"\n                : \"delete\";\n          const contentType: AttributionTooltipState[\"contentType\"] =\n            anchor.dataset[\"inline\"] === \"false\" ? \"block\" : \"inline-content\";\n\n          return {\n            anchor,\n            // The tooltip is portaled outside the editor root, so it can't read\n            // the cascaded per-user vars — resolve a concrete color from the store.\n            color: colorsForUserIds(\n              userStore,\n              parseUserIds(anchor.dataset[\"userIds\"]),\n            ).dark,\n            modificationType,\n            contentType,\n            users: usersLabelArray(anchor.dataset[\"userIds\"]),\n            format: isModification\n              ? parseFormatKeys(anchor.dataset[\"format\"])\n              : undefined,\n            className: resolveAttributionMarkClassName(\n              getAttributionMarkClassName?.({ contentType, modificationType }),\n              \"tooltip\",\n            ),\n          };\n        };\n\n        const hideTooltip = () => {\n          if (!activeAnchor) {\n            return;\n          }\n          activeAnchor = undefined;\n          store.setState(undefined);\n        };\n\n        // The innermost attributed mark at or above `el`, skipping unattributed\n        // wrappers so an attributed ancestor still wins.\n        const innermostAttributed = (\n          el: Element | null,\n        ): HTMLElement | undefined => {\n          while (el) {\n            const wrapper = el.closest<HTMLElement>(ATTRIBUTION_MARK_SELECTOR);\n            if (!wrapper) {\n              return undefined;\n            }\n            if (attributionIdentity(wrapper)) {\n              return wrapper;\n            }\n            el = wrapper.parentElement;\n          }\n          return undefined;\n        };\n\n        const onPointerOver = (event: Event) => {\n          const target = event.target instanceof Element ? event.target : null;\n          const innermost = innermostAttributed(target);\n          if (!innermost) {\n            // Not over an attributed mark — drop the current tooltip.\n            hideTooltip();\n            return;\n          }\n\n          const identity = attributionIdentity(innermost);\n          // Anchor on the outermost ancestor with the *same* attribution so one\n          // tooltip covers the whole region; a differently-attributed ancestor\n          // breaks the chain, and unattributed ones are climbed past.\n          let anchor = innermost;\n          let el: Element | null = innermost.parentElement;\n          while (el) {\n            const ancestor = el.closest<HTMLElement>(ATTRIBUTION_MARK_SELECTOR);\n            if (!ancestor) {\n              break;\n            }\n            const ancestorIdentity = attributionIdentity(ancestor);\n            if (ancestorIdentity === identity) {\n              anchor = ancestor;\n            } else if (ancestorIdentity) {\n              break;\n            }\n            el = ancestor.parentElement;\n          }\n\n          if (activeAnchor === anchor) {\n            return;\n          }\n\n          activeAnchor = anchor;\n          store.setState(buildState(anchor));\n\n          // First hover renders raw ids (cache-only); load the authors and refresh\n          // the resolved usernames once loaded, if this mark is still active.\n          const ids = parseUserIds(anchor.dataset[\"userIds\"]);\n          if (ids.length > 0) {\n            void userStore.loadUsers(ids).then(() => {\n              if (activeAnchor !== anchor) {\n                return;\n              }\n              store.setState(buildState(anchor));\n            });\n          }\n        };\n\n        root.addEventListener(\"mouseover\", onPointerOver, { signal });\n        signal.addEventListener(\"abort\", hideTooltip);\n\n        // Seed from the cache, then keep the vars in sync as users resolve.\n        syncRootVars();\n        const unsubscribe = userStore.store.subscribe(syncRootVars);\n        signal.addEventListener(\"abort\", unsubscribe);\n      },\n\n      // The `y-attributed-*` marks aren't in the default schema — register them\n      // here so the block specs can allow them (collaboration-only).\n      blockNoteExtensions: [\n        YAttributionMarksExtension({\n          getAttributionMarkClassName: options?.getAttributionMarkClassName,\n        }),\n      ],\n    };\n  },\n);\n","import { relativePositionStore, ySyncPluginKey } from \"@y/prosemirror\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const RelativePositionMappingExtension = createExtension(\n  ({ editor }) => {\n    return {\n      key: \"yPositionMapping\",\n      mapPosition: (position: number, side: \"left\" | \"right\" = \"left\") => {\n        const ySyncPluginState = ySyncPluginKey.getState(\n          editor.prosemirrorState,\n        );\n        if (!ySyncPluginState?.ytype) {\n          throw new Error(\"YSync plugin state not found\");\n        }\n\n        // 0 is a special case & always should map to itself\n        if (position === 0) {\n          return () => 0;\n        }\n\n        const posStore = relativePositionStore(\n          editor.prosemirrorState.doc.resolve(\n            position + (side === \"right\" ? 1 : -1),\n          ),\n          ySyncPluginState.ytype,\n          ySyncPluginState.renderer,\n        );\n\n        return () => {\n          const curYSyncPluginState = ySyncPluginKey.getState(\n            editor.prosemirrorState,\n          ) as typeof ySyncPluginState;\n          const pos = posStore(\n            editor.prosemirrorState.doc,\n            curYSyncPluginState.ytype,\n            curYSyncPluginState.renderer,\n          );\n\n          // This can happen if the element is garbage collected\n          if (pos === null) {\n            throw new Error(\"Position not found, cannot track positions\");\n          }\n\n          return pos + (side === \"right\" ? -1 : 1);\n        };\n      },\n    } as const;\n  },\n);\n","import { $prosemirrorDelta } from \"@y/prosemirror\";\nimport * as delta from \"lib0/delta\";\nimport * as schema from \"lib0/schema\";\n\n/**\n * Canonical name of a content delta's first block child (the child carried by an\n * insert op), or `null`. For a BlockNote `blockContainer` (content\n * `blockContent blockGroup?`) this is its block-content type (paragraph,\n * heading, image, ...).\n */\nconst firstChild = (\n  d: schema.Unwrap<typeof $prosemirrorDelta>,\n): schema.Unwrap<typeof $prosemirrorDelta> | null => {\n  for (const op of (d as any).children) {\n    if (delta.$insertOp.check(op)) {\n      for (const it of op.insert) {\n        if (delta.$deltaAny.check(it)) {\n          return it;\n        }\n      }\n    }\n  }\n  return null;\n};\n\n/**\n * Whether a `blockContainer` delta carries a child `blockGroup` — i.e. the block\n * has nested children. A container's content is `blockContent blockGroup?`, so\n * this is what tells a leaf block apart from a parent.\n */\nconst hasBlockGroup = (d: schema.Unwrap<typeof $prosemirrorDelta>): boolean => {\n  for (const op of (d as any).children) {\n    if (delta.$insertOp.check(op)) {\n      for (const it of op.insert) {\n        if (delta.$deltaAny.check(it) && it.name === \"blockGroup\") {\n          return true;\n        }\n      }\n    }\n  }\n  return false;\n};\n\nfunction getTableDimensions(\n  d: schema.Unwrap<typeof $prosemirrorDelta>,\n): { rows: number; cols: number } | null {\n  if (d.name !== \"table\") {\n    return null;\n  }\n\n  // Collect all rows with their cells' colspan/rowspan values.\n  const rows: Array<Array<{ colspan: number; rowspan: number }>> = [];\n  for (const op of (d as any).children) {\n    if (delta.$insertOp.check(op)) {\n      for (const tr of op.insert as Array<\n        schema.Unwrap<typeof $prosemirrorDelta>\n      >) {\n        if (tr.name !== \"tableRow\") {\n          return null;\n        }\n        const cells: Array<{ colspan: number; rowspan: number }> = [];\n        for (const trOp of (tr as any).children) {\n          if (delta.$insertOp.check(trOp)) {\n            for (const td of trOp.insert as Array<\n              schema.Unwrap<typeof $prosemirrorDelta>\n            >) {\n              if (td.name !== \"tableCell\" && td.name !== \"tableHeader\") {\n                return null;\n              }\n              cells.push({\n                colspan: Number(td.attrs.colspan) || 1,\n                rowspan: Number(td.attrs.rowspan) || 1,\n              });\n            }\n          }\n        }\n        rows.push(cells);\n      }\n    }\n  }\n\n  if (rows.length === 0) {\n    return null;\n  }\n\n  // Build an occupancy grid to determine the true column count.\n  // Each entry in `grid[r]` tracks which columns are already occupied\n  // (by a cell from a previous row with rowspan > 1).\n  const grid: boolean[][] = [];\n  for (let r = 0; r < rows.length; r++) {\n    if (!grid[r]) {\n      grid[r] = [];\n    }\n    let col = 0;\n    for (const cell of rows[r]) {\n      // Skip columns already occupied by a rowspan from above.\n      while (grid[r][col]) {\n        col++;\n      }\n      // Mark all slots this cell occupies.\n      for (let dr = 0; dr < cell.rowspan; dr++) {\n        if (!grid[r + dr]) {\n          grid[r + dr] = [];\n        }\n        for (let dc = 0; dc < cell.colspan; dc++) {\n          grid[r + dr][col + dc] = true;\n        }\n      }\n      col += cell.colspan;\n    }\n  }\n\n  const numCols = Math.max(...grid.map((row) => row.length));\n  return { rows: rows.length, cols: numCols };\n}\n\n/**\n * BlockNote's node-pairing policy for y-prosemirror's `matchNodes` option\n * (forwarded to `lib0/delta.diff`). This is the schema-specific bit that lives\n * in userland - the binding itself stays schema-agnostic.\n *\n * A `blockContainer` holds exactly one block content (`blockContent\n * blockGroup?`). Diffing a *type change* of that content as an in-place child\n * delete+insert would, under a suggestion, tombstone the old content next to the\n * new one => two block-contents in one container => schema-invalid. So we\n * declare a container's identity to be its first block-content child's type:\n * when that changes, the two containers are reported as *different*, the PM->Y\n * diff replaces the whole container, and the deleted + inserted containers sit\n * as siblings in the blockGroup (`blockGroupChild+` allows that). Each carries\n * the `y-attributed-*` node mark - which `blockContainer` already whitelists -\n * so no schema change and no storage transform are needed. A plain text edit\n * keeps the same first-child type => same identity => the diff descends and\n * merges as usual.\n *\n * @param a removed (old) node\n * @param b inserted (new) node\n * @returns whether `a` and `b` are the same node (diff in place) vs different (replace)\n */\nexport const blockMatchNodes = (\n  a: schema.Unwrap<typeof $prosemirrorDelta>,\n  b: schema.Unwrap<typeof $prosemirrorDelta>,\n): boolean => {\n  if (a.name !== b.name) {\n    return false;\n  }\n\n  if (a.name !== \"blockContainer\") {\n    return true;\n  }\n\n  const childA = firstChild(a);\n  const childB = firstChild(b);\n\n  if (childA?.name !== childB?.name) {\n    return false;\n  }\n\n  // A change in nesting is structural too: if one container gains or loses a\n  // child `blockGroup`, diffing it in place would insert/delete the blockGroup as\n  // a sibling of the block content inside a single container — schema-invalid.\n  // Treat it as different so the whole container is replaced instead, same as a\n  // content-type change. Keeps concurrent nesting merges (e.g. two users nesting\n  // a block under the same parent) from producing a lopsided in-place result.\n  if (hasBlockGroup(a) !== hasBlockGroup(b)) {\n    return false;\n  }\n\n  if (childA?.name === \"table\" && childB?.name === \"table\") {\n    const dimA = getTableDimensions(childA);\n    const dimB = getTableDimensions(childB);\n    if (\n      dimA !== null &&\n      dimB !== null &&\n      dimA.rows !== dimB.rows &&\n      dimA.cols !== dimB.cols\n    ) {\n      return false;\n    }\n  }\n\n  return true;\n};\n","import { configureYProsemirror, syncPlugin } from \"@y/prosemirror\";\nimport {\n  type ExtensionOptions,\n  createExtension,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { blockMatchNodes } from \"./blockMatchNodes.js\";\nimport { CollaborationOptions } from \"./index.js\";\n\n/**\n * Maps a Y attribution to BlockNote's `y-attributed-*` mark attrs.\n *\n * The mapper must be deterministic in `(format, attribution)` and emit attrs\n * that exactly match the declared mark schema in YAttributionMarks.ts. Any\n * mismatch causes the sync plugin to fire phantom reconcile dispatches in a\n * loop. See ATTRIBUTION.md in @y/prosemirror.\n *\n * Crucially the marks carry only stable identity (`userIds`, plus `format` for\n * the modification mark) — *not* user colors. Colors resolve asynchronously\n * from the {@link UserStore}, so baking them in here would make the mapper's\n * output change under a fixed `(format, attribution)` once a user loads, which\n * is exactly the non-determinism that triggers the reconcile loop. Instead the\n * `AttributionExtension` applies colors as a decoration layer that can\n * update independently of the mark representation.\n */\nexport const mapAttributionToMark = (\n  format: Record<string, unknown> | null,\n  attribution: {\n    insert?: readonly string[];\n    delete?: readonly string[];\n    format?: Record<string, readonly string[]>;\n    insertAt?: number;\n    deleteAt?: number;\n    formatAt?: number;\n  },\n): Record<string, unknown> => {\n  const out: Record<string, unknown> = { ...format };\n\n  if (attribution.insert) {\n    out[\"y-attributed-insert\"] = { userIds: attribution.insert };\n  }\n\n  if (attribution.delete) {\n    out[\"y-attributed-delete\"] = { userIds: attribution.delete };\n  }\n\n  if (attribution.format) {\n    const userIds = [...new Set(Object.values(attribution.format).flat())];\n    out[\"y-attributed-format\"] = { userIds, format: attribution.format };\n  }\n\n  return out;\n};\n\nexport const YSyncExtension = createExtension(\n  ({\n    options,\n    editor,\n  }: ExtensionOptions<\n    Pick<\n      CollaborationOptions,\n      | \"fragment\"\n      | \"renderer\"\n      | \"suggestionDoc\"\n      | \"provider\"\n      | \"getAttributionMarkClassName\"\n    >\n  >) => {\n    return {\n      key: \"ySync\",\n      fragment: options.fragment,\n      mount: () => {\n        const configure = () => {\n          editor.exec(\n            configureYProsemirror({\n              ytype: options.fragment,\n              // purposefully not passing `renderer` here, this is only for syncing the main doc, not switching to suggestion mode\n              // In the future, we may want view suggestion mode to be the default, and then we can decide how to indicate that through the options.\n              // For now though, we are leaving suggestion mode as experimental and must be explicitly enabled through the SuggestionsExtension.\n            }),\n          );\n        };\n\n        if (\n          options.provider &&\n          \"synced\" in options.provider &&\n          typeof options.provider.synced === \"boolean\"\n        ) {\n          if (options.provider[\"synced\"]) {\n            configure();\n          } else if (\n            \"on\" in options.provider &&\n            typeof options.provider.on === \"function\"\n          ) {\n            options.provider.on(\"synced\", (synced: boolean) => {\n              if (synced) {\n                configure();\n              }\n            });\n          } else {\n            throw new Error(\n              \"YSyncExtension: provider must have a 'synced' boolean or an 'on' method to listen for 'sync'\",\n            );\n          }\n        } else {\n          configure();\n        }\n      },\n      prosemirrorPlugins: [\n        syncPlugin({\n          suggestionDoc: options.suggestionDoc,\n          mapAttributionToMark,\n          // Node-pairing policy for the PM->Y diff: a `blockContainer` whose\n          // block-content type changes is treated as a *different* node, so the\n          // diff replaces the whole container (deleted + inserted siblings in\n          // the blockGroup) instead of producing two block-contents in one\n          // container => schema-invalid. No schema change / storage transform\n          // needed; `blockContainer` already whitelists the `y-attributed-*`\n          // marks. See blockMatchNodes.ts.\n          customCompare: blockMatchNodes,\n        }),\n      ],\n      runsBefore: [\"default\"],\n    } as const;\n  },\n);\n","import {\n  deltaAttributionToFormat,\n  deltaToPNode,\n  deltaToPSteps,\n  docToDelta,\n  nodeToDelta,\n  pmToFragment,\n} from \"@y/prosemirror\";\nimport * as d from \"lib0/delta\";\nimport { Node } from \"prosemirror-model\";\nimport { Transaction } from \"prosemirror-state\";\nimport {\n  type Block,\n  type BlockNoteEditor,\n  type BlockSchema,\n  type InlineContentSchema,\n  type PartialBlock,\n  type StyleSchema,\n  blockToNode,\n  docToBlocks,\n} from \"../index.js\";\nimport { blockMatchNodes } from \"./extensions/blockMatchNodes.js\";\nimport { mapAttributionToMark } from \"./extensions/YSync.js\";\n\nimport * as Y from \"@y/y\";\n\n/**\n * Find the equivalent of a Y.Type in another Y.Doc.\n *\n * For root types this looks up the matching shared key; for sub-types it\n * locates the item by its client/clock ID in the target doc's store.\n */\nexport function findTypeInOtherYdoc<T extends Y.Type<any>>(\n  ytype: T,\n  otherYdoc: Y.Doc,\n): T {\n  const ydoc = ytype.doc;\n  if (!ydoc) {\n    throw new Error(\"type does not have a ydoc\");\n  }\n  if (ytype._item === null) {\n    /**\n     * If is a root type, we need to find the root key in the original ydoc\n     * and use it to get the type in the other ydoc.\n     */\n    const rootKey = Array.from(ydoc.share.keys()).find(\n      (key) => ydoc.share.get(key) === ytype,\n    );\n    if (rootKey == null) {\n      throw new Error(\"type does not exist in other ydoc\");\n    }\n    return otherYdoc.get(rootKey as string, ytype.constructor as any) as T;\n  } else {\n    /**\n     * If it is a sub type, we use the item id to find the history type.\n     */\n    const ytypeItem = ytype._item;\n    const otherStructs = otherYdoc.store.clients.get(ytypeItem.id.client) ?? [];\n    const itemIndex = Y.findIndexSS(otherStructs, ytypeItem.id.clock);\n    const otherItem = otherStructs[itemIndex] as Y.Item | undefined;\n    if (!otherItem) {\n      throw new Error(\"type does not exist in other ydoc\");\n    }\n    const otherContent = otherItem.content as Y.ContentType | undefined;\n    if (!otherContent) {\n      throw new Error(\"type does not exist in other ydoc\");\n    }\n    return otherContent.type as T;\n  }\n}\n\n/**\n * Turn Prosemirror JSON to BlockNote style JSON\n * @param editor BlockNote editor\n * @param json Prosemirror JSON\n * @returns BlockNote style JSON\n */\nexport function _prosemirrorJSONToBlocks<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, ISchema, SSchema>, json: any) {\n  // note: theoretically this should also be possible without creating prosemirror nodes,\n  // but this is definitely the easiest way\n  const doc = editor.pmSchema.nodeFromJSON(json);\n  return docToBlocks<BSchema, ISchema, SSchema>(doc);\n}\n\n/**\n * Turn BlockNote JSON to Prosemirror node / state\n * @param editor BlockNote editor\n * @param blocks BlockNote blocks\n * @returns Prosemirror root node\n */\nexport function _blocksToProsemirrorNode<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n  blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n) {\n  const pmNodes = blocks.map((b) => blockToNode(b, editor.pmSchema));\n\n  const doc = editor.pmSchema.topNodeType.create(\n    null,\n    editor.pmSchema.nodes[\"blockGroup\"].create(null, pmNodes),\n  );\n  return doc;\n}\n\n/** YJS / BLOCKNOTE conversions */\n\n/**\n * Turn a Y.Type collaborative doc into a BlockNote document (BlockNote style JSON of all blocks)\n * @param editor BlockNote editor\n * @param fragment Y.Type\n * @returns BlockNote document (BlockNote style JSON of all blocks)\n */\nexport function yfragmentToBlocks<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(editor: BlockNoteEditor<BSchema, ISchema, SSchema>, fragment: Y.Type) {\n  const pmNode = deltaToPNode(fragment.toDeltaDeep(), editor.pmSchema, null);\n  if (pmNode === null) {\n    return [];\n  }\n  return docToBlocks<BSchema, ISchema, SSchema>(pmNode);\n}\n\n/**\n * Convert blocks to a Y.Type\n *\n * This can be used when importing existing content to Y.Doc for the first time,\n * note that this should not be used to rehydrate a Y.Doc from a database once\n * collaboration has begun as all history will be lost\n *\n * @param editor BlockNote editor\n * @param blocks the blocks to convert\n * @param fragment XML fragment name\n * @returns Y.Type\n */\nexport function blocksToYType<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n  blocks: Block<BSchema, ISchema, SSchema>[],\n  fragment?: Y.Type,\n) {\n  if (!fragment) {\n    fragment = new Y.Doc().get(\"prosemirror\");\n  }\n  return pmToFragment(_blocksToProsemirrorNode(editor, blocks), fragment);\n}\n\n/**\n * Turn a Y.Doc collaborative doc into a BlockNote document (BlockNote style JSON of all blocks)\n * @param editor BlockNote editor\n * @param ydoc Y.Doc\n * @param fragment XML fragment name\n * @returns BlockNote document (BlockNote style JSON of all blocks)\n */\nexport function yDocToBlocks<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n  ydoc: Y.Doc,\n  fragment = \"prosemirror\",\n) {\n  return yfragmentToBlocks(editor, ydoc.get(fragment));\n}\n\n/**\n * This can be used when importing existing content to Y.Doc for the first time,\n * note that this should not be used to rehydrate a Y.Doc from a database once\n * collaboration has begun as all history will be lost\n *\n * @param editor BlockNote editor\n * @param blocks the blocks to convert\n * @param fragment XML fragment name\n */\nexport function blocksToYDoc<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n  blocks: PartialBlock<BSchema, ISchema, SSchema>[],\n  fragment = \"prosemirror\",\n) {\n  const delta = docToDelta(_blocksToProsemirrorNode(editor, blocks));\n  const doc = new Y.Doc();\n  doc.get(fragment).applyDelta(delta);\n  return doc;\n}\n\n/**\n * Diff two ProseMirror documents into a delta, using BlockNote's node-pairing\n * policy ({@link blockMatchNodes}) so a block's content-type change is reported\n * as a replace rather than a schema-invalid in-place edit.\n */\nexport function docDiffToDelta(previousDoc: Node, newDoc: Node) {\n  const initialDelta = nodeToDelta(previousDoc);\n  const finalDelta = nodeToDelta(newDoc);\n  return d.diff(initialDelta.done(), finalDelta.done(), {\n    compare: blockMatchNodes,\n  });\n}\n\n/**\n * Build a ProseMirror transaction that turns `tr.doc` into the content of a\n * Y.Type `fragment`, applying the `renderer`'s authorship as\n * `y-attributed-*` marks. Used to render a (read-only) diff of a snapshot / a\n * version comparison into the editor.\n */\nexport function getProseMirrorTrFromYFragment({\n  tr,\n  fragment,\n  renderer,\n}: {\n  tr: Transaction;\n  fragment: Y.Type;\n  renderer?: Y.AbstractRenderer | null;\n}): Transaction {\n  const ycontent = deltaAttributionToFormat(\n    fragment.toDeltaDeep({ renderer }),\n    mapAttributionToMark,\n  );\n  // @todo it is preferred to apply the minimal diff - at least for debugging purposes. the\n  // document replacal is more reliable though\n\n  const pcontent = nodeToDelta(tr.doc, undefined, true);\n  const diff = d.diff(pcontent.done(), ycontent.done(), {\n    compare: blockMatchNodes,\n  });\n  return deltaToPSteps(tr, diff, undefined, undefined);\n}\n","import { getMarkRange, posToDOMRect } from \"@tiptap/core\";\n\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport {\n  acceptChanges,\n  rejectAllChanges,\n  rejectChanges,\n  configureYProsemirror,\n  acceptAllChanges,\n} from \"@y/prosemirror\";\nimport { CollaborationOptions } from \"./index.js\";\nimport { findTypeInOtherYdoc } from \"../utils.js\";\nimport { normalizeToUserStore } from \"../../user/index.js\";\n\nexport const SuggestionsExtension = createExtension(\n  ({ editor, options }: ExtensionOptions<CollaborationOptions>) => {\n    const suggestionDoc = options.suggestionDoc;\n    if (!suggestionDoc) {\n      throw new Error(\"Suggestion doc not found\");\n    }\n    // Shared with the rest of collaboration; the collaboration extension passes\n    // an already-built store here.\n    const userStore = normalizeToUserStore(options.resolveUsers);\n\n    function getSuggestionElementAtPos(pos: number) {\n      let currentNode = editor.prosemirrorView.nodeDOM(pos);\n      while (currentNode && currentNode.parentElement) {\n        if (currentNode.nodeName === \"INS\" || currentNode.nodeName === \"DEL\") {\n          return currentNode as HTMLElement;\n        }\n        currentNode = currentNode.parentElement;\n      }\n      return null;\n    }\n\n    function getMarkAtPos(pos: number, markType: string) {\n      return editor.transact((tr) => {\n        const resolvedPos = tr.doc.resolve(pos);\n        const mark = resolvedPos\n          .marks()\n          .find((mark) => mark.type.name === markType);\n\n        if (!mark) {\n          return;\n        }\n\n        const markRange = getMarkRange(resolvedPos, mark.type);\n        if (!markRange) {\n          return;\n        }\n\n        return {\n          range: markRange,\n          mark,\n          get text() {\n            return tr.doc.textBetween(markRange.from, markRange.to);\n          },\n          get position() {\n            // to minimize re-renders, we convert to JSON, which is the same shape anyway\n            return posToDOMRect(\n              editor.prosemirrorView,\n              markRange.from,\n              markRange.to,\n            ).toJSON() as DOMRect;\n          },\n        };\n      });\n    }\n\n    function getSuggestionAtSelection() {\n      return editor.transact((tr) => {\n        const selection = tr.selection;\n        if (!selection.empty) {\n          return undefined;\n        }\n        return (\n          getMarkAtPos(selection.anchor, \"insertion\") ||\n          getMarkAtPos(selection.anchor, \"deletion\") ||\n          getMarkAtPos(selection.anchor, \"modification\")\n        );\n      });\n    }\n\n    return {\n      key: \"suggestions\",\n      userStore,\n      runsBefore: [\"ySync\"],\n      viewSuggestions: () => {\n        if (options.renderer) {\n          options.renderer.suggestionMode = false;\n        }\n        editor.exec(\n          configureYProsemirror({\n            ytype: findTypeInOtherYdoc(options.fragment, suggestionDoc),\n            renderer: options.renderer,\n          }),\n        );\n      },\n      enableSuggestions: () => {\n        if (options.renderer) {\n          options.renderer.suggestionMode = true;\n        }\n        editor.exec(\n          configureYProsemirror({\n            ytype: findTypeInOtherYdoc(options.fragment, suggestionDoc),\n            renderer: options.renderer,\n          }),\n        );\n      },\n      disableSuggestions: () => {\n        editor.exec(\n          configureYProsemirror({\n            ytype: options.fragment,\n            renderer: null,\n          }),\n        );\n      },\n      applyAllSuggestions: () => {\n        return editor.exec(acceptAllChanges());\n      },\n      applySuggestion: (start: number, end?: number) => {\n        return editor.exec(acceptChanges(start, end));\n      },\n      revertSuggestion: (start: number, end?: number) => {\n        return editor.exec(rejectChanges(start, end));\n      },\n      revertAllSuggestions: () => {\n        return editor.exec(rejectAllChanges());\n      },\n\n      getSuggestionElementAtPos,\n      getMarkAtPos,\n      getSuggestionAtSelection,\n      getSuggestionAtCoords: (coords: { left: number; top: number }) => {\n        return editor.transact(() => {\n          const posAtCoords = editor.prosemirrorView.posAtCoords(coords);\n          if (posAtCoords === null || posAtCoords?.inside === -1) {\n            return undefined;\n          }\n\n          return (\n            getMarkAtPos(posAtCoords.pos, \"y-attributed-insert\") ||\n            getMarkAtPos(posAtCoords.pos, \"y-attributed-delete\") ||\n            getMarkAtPos(posAtCoords.pos, \"y-attributed-format\")\n          );\n        });\n      },\n      checkUnresolvedSuggestions: () => {\n        let hasUnresolvedSuggestions = false;\n\n        editor.prosemirrorState.doc.descendants((node) => {\n          if (hasUnresolvedSuggestions) {\n            return false;\n          }\n\n          hasUnresolvedSuggestions =\n            node.marks.findIndex(\n              (mark) =>\n                mark.type.name === \"y-attributed-insert\" ||\n                mark.type.name === \"y-attributed-delete\" ||\n                mark.type.name === \"y-attributed-format\",\n            ) !== -1;\n\n          return true;\n        });\n\n        return hasUnresolvedSuggestions;\n      },\n    } as const;\n  },\n);\n","import { configureYProsemirror, pauseSync } from \"@y/prosemirror\";\nimport * as Y from \"@y/y\";\n\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport type { PreviewController } from \"../../extensions/Versioning/index.js\";\nimport {\n  findTypeInOtherYdoc,\n  getProseMirrorTrFromYFragment,\n} from \"../utils.js\";\n\n/**\n * Empties the document before a {@link configureYProsemirror} refill so\n * ProseMirror rebuilds every node view instead of reusing a stale-positioned one\n * (BlockNote node views resolve their block eagerly via `getPos()` and throw on\n * a moved node). Sync is paused first so the clear never reaches the Y.Doc.\n *\n * TODO: remove once `configureYProsemirror` applies a minimal diff.\n */\nfunction clearDocumentForConfigure(editor: BlockNoteEditor<any, any, any>) {\n  // Pause sync (ytype -> null) so the deletion below stays local.\n  editor.exec(pauseSync);\n  editor.removeBlocks(editor.document);\n}\n\n/**\n * Creates a Yjs-specific adapter that provides the {@link PreviewController}\n * and `getCurrentDocument` callback required by the base\n * {@link VersioningExtension}.\n *\n * This is wired automatically by the {@link CollaborationExtension} when\n * `versioningEndpoints` is provided. You only need to call this directly if\n * you're using the `VersioningExtension` outside of the collaboration wrapper.\n */\nexport function createYjsVersioningAdapter(\n  editor: BlockNoteEditor<any, any, any>,\n  fragment: Y.Type,\n): {\n  preview: PreviewController<Uint8Array, Y.ContentMap>;\n  getCurrentDocument: () => Y.Type;\n  serializeCurrentContent: () => Uint8Array;\n} {\n  return {\n    getCurrentDocument: () => fragment,\n    // Serialise the live document as a V2 update — the same format that\n    // `getContent` returns (via `convertUpdateFormatV1ToV2`) and that\n    // `enterPreview` consumes (`applyUpdateV2`). Used to render a read-only\n    // diff of the live document against a snapshot.\n    serializeCurrentContent: () => Y.encodeStateAsUpdateV2(fragment.doc!),\n    preview: {\n      enterPreview: (\n        snapshotContent: Uint8Array,\n        compareToContent?: Uint8Array,\n        attributions?: Y.ContentMap,\n      ) => {\n        let prevSnapshot: { fragment: Y.Type } | undefined;\n        if (compareToContent) {\n          const compareToDoc = new Y.Doc({ isSuggestionDoc: true });\n          Y.applyUpdateV2(compareToDoc, compareToContent);\n          prevSnapshot = {\n            fragment: findTypeInOtherYdoc(fragment, compareToDoc),\n          };\n        }\n\n        const doc = new Y.Doc();\n        Y.applyUpdateV2(doc, snapshotContent);\n        // Empty the document before reconfiguring so ProseMirror rebuilds node\n        // views from scratch instead of reusing stale-positioned ones. See\n        // clearDocumentForConfigure.\n        clearDocumentForConfigure(editor);\n\n        editor.exec((state, dispatch) => {\n          const tr = getProseMirrorTrFromYFragment({\n            tr: state.tr,\n            fragment: findTypeInOtherYdoc(fragment, doc),\n            // Pass the optional content map as `attrs` so the diff renderer\n            // knows who/when authored each change. Without it, the renderer\n            // only produces \"what changed\" (empty userIds, null timestamps) and\n            // downstream mark tooltips show \"unknown / unknown time\".\n            renderer: prevSnapshot\n              ? Y.createDiffRenderer(\n                  prevSnapshot.fragment.doc!,\n                  doc,\n                  attributions ? { attrs: attributions } : undefined,\n                )\n              : undefined,\n          });\n          if (dispatch) {\n            dispatch(tr);\n          }\n          return true;\n        });\n      },\n      exitPreview: () => {\n        // Empty the document before reconfiguring so ProseMirror rebuilds node\n        // views from scratch instead of reusing stale-positioned ones. See\n        // clearDocumentForConfigure.\n        clearDocumentForConfigure(editor);\n        editor.exec(configureYProsemirror({ ytype: fragment }));\n      },\n      applyRestore: (_snapshotContent: Uint8Array) => {\n        // For Yjs-backed versioning, restoration happens on the server (e.g.\n        // YHub's `/rollback` endpoint) which publishes a reverting update to\n        // the document's room. That update propagates back to this client over\n        // the live sync connection and updates `fragment` automatically, so\n        // there is nothing to apply locally — we only need to leave preview\n        // mode. `exitPreview` is already called by the base extension before\n        // this runs, so this is a no-op.\n        //\n        // Note: this assumes `endpoints.restore` performs the server-side\n        // restore. The default in-memory adapter has no server, which is why\n        // this is specific to the Yjs collaboration setup.\n      },\n    },\n  };\n}\n","import { defaultSelectionBuilder, yCursorPlugin } from \"@y/prosemirror\";\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { CollaborationOptions } from \"./index.js\";\n\nexport type CollaborationUser = {\n  id?: string;\n  name: string;\n  color: string;\n  [key: string]: unknown;\n};\n\n/**\n * Determine whether the foreground color should be white or black based on a provided background color\n * Inspired by: https://stackoverflow.com/a/3943023\n */\nfunction isDarkColor(bgColor: string): boolean {\n  const color = bgColor.charAt(0) === \"#\" ? bgColor.substring(1, 7) : bgColor;\n  const r = parseInt(color.substring(0, 2), 16); // hexToR\n  const g = parseInt(color.substring(2, 4), 16); // hexToG\n  const b = parseInt(color.substring(4, 6), 16); // hexToB\n  const uicolors = [r / 255, g / 255, b / 255];\n  const c = uicolors.map((col) => {\n    if (col <= 0.03928) {\n      return col / 12.92;\n    }\n    return Math.pow((col + 0.055) / 1.055, 2.4);\n  });\n  const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];\n  return L <= 0.179;\n}\n\nfunction defaultCursorRender(user: CollaborationUser) {\n  const cursorElement = document.createElement(\"span\");\n\n  cursorElement.classList.add(\"bn-collaboration-cursor__base\");\n\n  const caretElement = document.createElement(\"span\");\n  caretElement.setAttribute(\"contentedEditable\", \"false\");\n  caretElement.classList.add(\"bn-collaboration-cursor__caret\");\n  caretElement.setAttribute(\n    \"style\",\n    `background-color: ${user.color}; color: ${\n      isDarkColor(user.color) ? \"white\" : \"black\"\n    }`,\n  );\n\n  const labelElement = document.createElement(\"span\");\n\n  labelElement.classList.add(\"bn-collaboration-cursor__label\");\n  labelElement.setAttribute(\n    \"style\",\n    `background-color: ${user.color}; color: ${\n      isDarkColor(user.color) ? \"white\" : \"black\"\n    }`,\n  );\n  labelElement.insertBefore(document.createTextNode(user.name), null);\n\n  caretElement.insertBefore(labelElement, null);\n\n  cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n  cursorElement.insertBefore(caretElement, null);\n  cursorElement.insertBefore(document.createTextNode(\"\\u2060\"), null); // Non-breaking space\n\n  return cursorElement;\n}\n\nexport const YCursorExtension = createExtension(\n  ({ options }: ExtensionOptions<CollaborationOptions>) => {\n    const recentlyUpdatedCursors = new Map();\n    const awareness =\n      options.provider &&\n      \"awareness\" in options.provider &&\n      typeof options.provider.awareness === \"object\"\n        ? options.provider.awareness\n        : undefined;\n    if (awareness) {\n      if (\n        \"setLocalStateField\" in awareness &&\n        typeof awareness.setLocalStateField === \"function\"\n      ) {\n        awareness.setLocalStateField(\"user\", options.user);\n      }\n      if (\"on\" in awareness && typeof awareness.on === \"function\") {\n        if (options.showCursorLabels !== \"always\") {\n          awareness.on(\n            \"change\",\n            ({\n              updated,\n            }: {\n              added: Array<number>;\n              updated: Array<number>;\n              removed: Array<number>;\n            }) => {\n              for (const clientID of updated) {\n                const cursor = recentlyUpdatedCursors.get(clientID);\n\n                if (cursor) {\n                  setTimeout(() => {\n                    cursor.element.setAttribute(\"data-active\", \"\");\n                  }, 10);\n\n                  if (cursor.hideTimeout) {\n                    clearTimeout(cursor.hideTimeout);\n                  }\n\n                  recentlyUpdatedCursors.set(clientID, {\n                    element: cursor.element,\n                    hideTimeout: setTimeout(() => {\n                      cursor.element.removeAttribute(\"data-active\");\n                    }, 2000),\n                  });\n                }\n              }\n            },\n          );\n        }\n      }\n    }\n\n    return {\n      key: \"yCursor\",\n      prosemirrorPlugins: [\n        awareness\n          ? yCursorPlugin(awareness, {\n              selectionBuilder: defaultSelectionBuilder,\n              cursorBuilder(user, clientID) {\n                let cursorData = recentlyUpdatedCursors.get(clientID);\n\n                if (!cursorData) {\n                  const cursorElement = (\n                    options.renderCursor ?? defaultCursorRender\n                  )(user as CollaborationUser);\n\n                  if (options.showCursorLabels !== \"always\") {\n                    cursorElement.addEventListener(\"mouseenter\", () => {\n                      const cursor = recentlyUpdatedCursors.get(clientID)!;\n                      cursor.element.setAttribute(\"data-active\", \"\");\n\n                      if (cursor.hideTimeout) {\n                        clearTimeout(cursor.hideTimeout);\n                        recentlyUpdatedCursors.set(clientID, {\n                          element: cursor.element,\n                          hideTimeout: undefined,\n                        });\n                      }\n                    });\n\n                    cursorElement.addEventListener(\"mouseleave\", () => {\n                      const cursor = recentlyUpdatedCursors.get(clientID)!;\n\n                      recentlyUpdatedCursors.set(clientID, {\n                        element: cursor.element,\n                        hideTimeout: setTimeout(() => {\n                          cursor.element.removeAttribute(\"data-active\");\n                        }, 2000),\n                      });\n                    });\n                  }\n\n                  cursorData = {\n                    element: cursorElement,\n                    hideTimeout: undefined,\n                  };\n\n                  recentlyUpdatedCursors.set(clientID, cursorData);\n                }\n\n                return cursorData.element;\n              },\n            })\n          : undefined,\n      ].filter((a) => a !== undefined),\n      dependsOn: [\"ySync\"],\n      updateUser(user: CollaborationUser) {\n        awareness?.setLocalStateField(\"user\", user);\n      },\n      getUser(): CollaborationUser | undefined {\n        const state = awareness?.getLocalState();\n        if (!state) {\n          return undefined;\n        }\n        return state[\"user\"];\n      },\n    } as const;\n  },\n);\n","import { docToDelta } from \"@y/prosemirror\";\nimport * as Y from \"@y/y\";\n\nimport type { Block } from \"../../blocks/defaultBlocks.js\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\nimport type { User } from \"../../user/index.js\";\nimport {\n  _blocksToProsemirrorNode,\n  docDiffToDelta,\n  findTypeInOtherYdoc,\n  getProseMirrorTrFromYFragment,\n} from \"../utils.js\";\nimport { AttributionExtension } from \"./AttributionExtension.js\";\nimport type { GetAttributionMarkClassName } from \"./YAttributionMarks.js\";\n\n/**\n * A version diff has a single \"author\" — the version that introduced the changes\n * — not a real user, so all `y-attributed-*` marks carry one synthetic id. That\n * id is derived from the version's label (see {@link diffAuthorId}) so it's stable\n * per version: the user store caches resolved users by id, so a per-label id keeps\n * each version's tooltip showing its own name instead of a stale cached one.\n */\nconst DIFF_AUTHOR_ID_PREFIX = \"version:\";\n\n/** The synthetic author id for a given version label. */\nconst diffAuthorId = (label: string) => DIFF_AUTHOR_ID_PREFIX + label;\n\n/** Fallback label used when a diff is rendered without a version name. */\nconst DEFAULT_DIFF_LABEL = \"This version\";\n\n/** Color used for the version diff marks. */\nconst DIFF_AUTHOR_COLOR = \"#4363d8\";\n\nexport type DiffVersioningExtensionOptions = {\n  /**\n   * The color used for the diff's attribution marks. Defaults to a blue.\n   */\n  color?: string;\n  /**\n   * See {@link GetAttributionMarkClassName}. Forwarded to the underlying\n   * {@link AttributionExtension} to override mark styling by change type.\n   */\n  getAttributionMarkClassName?: GetAttributionMarkClassName;\n};\n\n/**\n * Records the author of each transaction on `doc` into a mutable\n * {@link Y.Attributions}, so the resulting attribution marks carry a non-empty\n * `userIds` (and therefore resolve to a color/name). The listener must be\n * attached *before* the attributed transaction runs. Mirrors the store used by\n * the suggestion gallery example (`createAttributionStore`).\n */\nfunction attributeTransactionsTo(doc: Y.Doc, userId: string): Y.Attributions {\n  const attrs = new Y.Attributions();\n  doc.on(\"beforeObserverCalls\", (tr) => {\n    if (!tr.insertSet.isEmpty()) {\n      Y.insertIntoIdMap(\n        attrs.inserts,\n        Y.createIdMapFromIdSet(tr.insertSet, [\n          Y.createContentAttribute(\"insert\", userId),\n        ]),\n      );\n    }\n    if (!tr.deleteSet.isEmpty()) {\n      Y.insertIntoIdMap(\n        attrs.deletes,\n        Y.createIdMapFromIdSet(tr.deleteSet, [\n          Y.createContentAttribute(\"delete\", userId),\n        ]),\n      );\n    }\n  });\n  return attrs;\n}\n\n/**\n * An opt-in extension that renders a read-only diff between two BlockNote\n * documents (`Block[]`) directly in the editor, marking insertions/deletions\n * with the `y-attributed-*` suggestion marks — the same visual result the Yjs\n * collaboration adapter produces, but driven from two plain block arrays with\n * no server and no live Yjs sync.\n *\n * It composes {@link AttributionExtension} (which registers the attribution\n * marks and drives their colors + hover tooltips from a user store), and adds\n * the {@link renderDiff} / {@link clearDiff} capability.\n *\n * Registering this extension is what makes non-collaborative versioning\n * (`inMemoryVersioning`) capable of showing diffs: the in-memory preview\n * controller looks this up by key (`\"diffVersioning\"`) and delegates to\n * {@link renderDiff}, falling back to a static document swap when it's absent.\n *\n * `renderDiff` is also a standalone, directly-callable API — you can register\n * just this extension and call\n * `editor.getExtension(DiffVersioningExtension).renderDiff(target, baseline)`\n * to render a known diff (e.g. in tests).\n *\n * @example\n * ```ts\n * const editor = BlockNoteEditor.create({\n *   extensions: [DiffVersioningExtension()],\n * });\n * editor.getExtension(DiffVersioningExtension)!.renderDiff(target, baseline);\n * ```\n */\nexport const DiffVersioningExtension = createExtension(\n  ({\n    options,\n    editor,\n  }: {\n    options: DiffVersioningExtensionOptions | undefined;\n    editor: BlockNoteEditor<any, any, any>;\n  }) => {\n    const color = options?.color ?? DIFF_AUTHOR_COLOR;\n\n    // Resolve a synthetic author id back to its version label. The id encodes\n    // the label (`version:<label>`), so this is a pure decode — no shared mutable\n    // state, and the user store caches each version's \"user\" separately (so\n    // switching between version comparisons never shows a stale name).\n    const resolveUsers = async (ids: string[]): Promise<User[]> =>\n      ids\n        .filter((id) => id.startsWith(DIFF_AUTHOR_ID_PREFIX))\n        .map((id) => ({\n          id,\n          username: id.slice(DIFF_AUTHOR_ID_PREFIX.length),\n          avatarUrl: \"\",\n          color,\n        }));\n\n    /**\n     * Render a read-only diff of `baselineBlocks` → `snapshotBlocks` into the\n     * editor. The changes are attributed to the version that introduced them:\n     * pass `versionLabel` to label the diff marks (shown in their hover tooltip,\n     * e.g. \"Edited by: {versionLabel}\"). Uses the \"two-doc fork\" recipe so the\n     * two Y.Docs share history — a hard requirement for\n     * `createDiffRenderer`, which diffs by Yjs client/clock ids.\n     */\n    const renderDiff = (\n      snapshotBlocks: Block<any, any, any>[],\n      baselineBlocks: Block<any, any, any>[],\n      versionLabel: string = DEFAULT_DIFF_LABEL,\n    ) => {\n      const authorId = diffAuthorId(versionLabel);\n\n      if (!editor.pmSchema.marks[\"y-attributed-insert\"]) {\n        throw new Error(\n          \"DiffVersioningExtension: the y-attributed-* marks are missing from \" +\n            \"the schema. This should not happen — the extension registers them \" +\n            \"via AttributionExtension.\",\n        );\n      }\n\n      const baselineNode = _blocksToProsemirrorNode(editor, baselineBlocks);\n      const snapshotNode = _blocksToProsemirrorNode(editor, snapshotBlocks);\n\n      // gc must stay off so the attribution manager can read the full struct\n      // store (including deleted items) when diffing.\n      const prevDoc = new Y.Doc({ gc: false });\n      const prevType = prevDoc.get(\"prosemirror\");\n      prevDoc.transact(() => {\n        prevType.applyDelta(docToDelta(baselineNode) as any);\n      });\n\n      // Fork prevDoc into nextDoc so they share client/clock ids, then apply the\n      // baseline → snapshot delta as a new transaction. New content gets ids\n      // that prevDoc lacks (→ inserts); items retained-away become deletes.\n      const nextDoc = new Y.Doc({ gc: false });\n      Y.applyUpdateV2(nextDoc, Y.encodeStateAsUpdateV2(prevDoc));\n      const nextType = findTypeInOtherYdoc(prevType, nextDoc);\n\n      // Attach the author store BEFORE applying the delta so the diff\n      // transaction's inserts/deletes are attributed.\n      const attrs = attributeTransactionsTo(nextDoc, authorId);\n\n      const delta = docDiffToDelta(baselineNode, snapshotNode);\n      nextDoc.transact(() => {\n        nextType.applyDelta(delta as any);\n      }, authorId);\n\n      const renderer = Y.createDiffRenderer(prevDoc, nextDoc, { attrs });\n\n      // Clear the live doc first so ProseMirror rebuilds node views from\n      // scratch (BlockNote node views resolve their block eagerly via getPos()\n      // and throw on a moved node). The diff then inserts the attributed content\n      // against an empty doc.\n      editor.replaceBlocks(editor.document, []);\n\n      editor.exec((state, dispatch) => {\n        const tr = getProseMirrorTrFromYFragment({\n          tr: state.tr,\n          fragment: nextType,\n          renderer,\n        });\n        if (dispatch) {\n          dispatch(tr);\n        }\n        return true;\n      });\n\n      prevDoc.destroy();\n      nextDoc.destroy();\n    };\n\n    /**\n     * Leave diff view: clear the (mark-carrying) document and restore the given\n     * blocks. Clears first so stale node views for block-level marks are torn\n     * down instead of reused.\n     */\n    const clearDiff = (restore: Block<any, any, any>[]) => {\n      editor.replaceBlocks(editor.document, []);\n      editor.replaceBlocks(editor.document, restore);\n    };\n\n    return {\n      key: \"diffVersioning\",\n      // Compose AttributionExtension: registers the y-attributed-* marks and\n      // drives their colors + hover-tooltip names from the (static) user store.\n      blockNoteExtensions: [\n        AttributionExtension({\n          resolveUsers,\n          getAttributionMarkClassName: options?.getAttributionMarkClassName,\n        }),\n      ],\n      renderDiff,\n      clearDiff,\n    };\n  },\n);\n","import type { Awareness } from \"@y/protocols/awareness\";\nimport type * as Y from \"@y/y\";\nimport { BlockNoteEditorOptions } from \"../../editor/BlockNoteEditor.js\";\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport {\n  VersioningEndpoints,\n  VersioningEndpointsFactory,\n  VersioningExtension,\n} from \"../../extensions/Versioning/index.js\";\nimport { normalizeToUserStore, UserStoreOrResolver } from \"../../user/index.js\";\nimport { AttributionExtension } from \"./AttributionExtension.js\";\nimport { RelativePositionMappingExtension } from \"./RelativePositionMapping.js\";\nimport { SuggestionsExtension } from \"./Suggestions.js\";\nimport { createYjsVersioningAdapter } from \"./Versioning.js\";\nimport { CollaborationUser, YCursorExtension } from \"./YCursorPlugin.js\";\nimport type { GetAttributionMarkClassName } from \"./YAttributionMarks.js\";\nimport { YSyncExtension } from \"./YSync.js\";\n\nexport type CollaborationOptions = {\n  /**\n   * The Yjs Type that's used for collaboration.\n   */\n  fragment: Y.Type;\n  /**\n   * The user info for the current user that's shown to other collaborators.\n   */\n  user: CollaborationUser;\n  /**\n   * Resolve user information (usernames, colors) for suggestions and versions,\n   * used to drive suggestion-author tooltips, suggestion colors and\n   * version-history author labels.\n   *\n   * Either a resolver function (called with the ids of users that are not yet\n   * cached, returning their information — a user store is built from it\n   * internally) or a pre-built user store (see `createUserStore`). Pass the same\n   * store you give the comments extension so a single de-duped user cache is\n   * shared across comments, suggestions and versioning.\n   */\n  resolveUsers?: UserStoreOrResolver;\n  /**\n   * A Yjs provider (used for awareness / cursor information)\n   */\n  provider?: { awareness?: Awareness };\n  /**\n   * Optional function to customize how cursors of users are rendered\n   */\n  renderCursor?: (user: CollaborationUser) => HTMLElement;\n  /**\n   * Optional flag to set when the user label should be shown with the default\n   * collaboration cursor. Setting to \"always\" will always show the label,\n   * while \"activity\" will only show the label when the user moves the cursor\n   * or types. Defaults to \"activity\".\n   */\n  showCursorLabels?: \"always\" | \"activity\";\n  /**\n   * The renderer for the collaboration.\n   */\n  renderer?: Y.DiffRenderer;\n  /**\n   * The suggestion doc for the collaboration. If using suggestion mode\n   */\n  suggestionDoc?: Y.Doc;\n\n  /**\n   * Optional callback to override suggestion-mark styling by change type instead\n   * of by author. Given a mark's content/modification type, return a class name\n   * applied to the mark and its hover tooltip; the per-user color is then\n   * dropped for that mark. See {@link GetAttributionMarkClassName}.\n   */\n  getAttributionMarkClassName?: GetAttributionMarkClassName;\n\n  /**\n   * The endpoints for the versioning functionality.\n   */\n  versioningEndpoints?:\n    | VersioningEndpoints<Y.Type, Uint8Array>\n    | VersioningEndpointsFactory<Y.Type, Uint8Array>;\n};\n\nexport const CollaborationExtension = createExtension(\n  ({ editor, options }: ExtensionOptions<CollaborationOptions>) => {\n    // Build a single user store here (from a resolver callback or a store the\n    // consumer passed in) and hand that same store down to every sub-extension\n    // that needs it — suggestions, suggestion-mark tooltips/colors, versioning —\n    // so they share one de-duped cache. Passing the resolved store (rather than\n    // the raw resolver) is what guarantees the sharing: each child re-normalizes\n    // it to itself instead of building its own.\n    const userStore = normalizeToUserStore(options.resolveUsers);\n    const optionsWithUserStore = { ...options, resolveUsers: userStore };\n    return {\n      key: \"collaboration\",\n      userStore,\n      blockNoteExtensions: [\n        options.suggestionDoc\n          ? SuggestionsExtension(optionsWithUserStore)\n          : null,\n        RelativePositionMappingExtension(),\n        YSyncExtension(optionsWithUserStore),\n        YCursorExtension(options),\n        options.versioningEndpoints\n          ? VersioningExtension({\n              ...createYjsVersioningAdapter(editor, options.fragment),\n              endpoints: options.versioningEndpoints,\n              resolveUsers: userStore,\n            })\n          : null,\n        AttributionExtension({\n          resolveUsers: userStore,\n          getAttributionMarkClassName: options.getAttributionMarkClassName,\n        }),\n      ].filter((a) => a !== null),\n    } as const;\n  },\n);\n\nexport function withCollaboration<\n  Options extends Partial<BlockNoteEditorOptions<any, any, any>>,\n>(\n  options: Options & {\n    /**\n     * Options for configuring the collaboration functionality.\n     */\n    collaboration: CollaborationOptions;\n  },\n): Options {\n  if (options.initialContent) {\n    // eslint-disable-next-line no-console\n    console.warn(\n      \"When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider\",\n    );\n  }\n  return {\n    ...options,\n    extensions: [\n      ...(options.extensions ?? []),\n      CollaborationExtension(options.collaboration),\n    ],\n    // We disable the default prosemirror history plugin, since it's not compatible with yjs\n    disableExtensions: [\"history\", ...(options.disableExtensions ?? [])],\n    // We don't want the default initial content, since it will generate a random id for the initial block on each client,\n    // leading to conflicts when syncing happens afterwards.\n    initialContent: [{ type: \"paragraph\", id: \"initialBlockId\" }],\n  };\n}\n\nexport * from \"./AttributionExtension.js\";\nexport * from \"./DiffVersioningExtension.js\";\nexport * from \"./RelativePositionMapping.js\";\nexport * from \"./Suggestions.js\";\nexport * from \"./Versioning.js\";\nexport * from \"./YAttributionMarks.js\";\nexport * from \"./YCursorPlugin.js\";\nexport * from \"./YSync.js\";\n","import * as Y from \"@y/y\";\nimport type {\n  CommentData,\n  CommentReactionData,\n  ThreadData,\n} from \"../../comments/types.js\";\n\nexport function commentToYType(comment: CommentData) {\n  const yType = new Y.Type();\n  yType.setAttr(\"id\", comment.id);\n  yType.setAttr(\"userId\", comment.userId);\n  yType.setAttr(\"createdAt\", comment.createdAt.getTime());\n  yType.setAttr(\"updatedAt\", comment.updatedAt.getTime());\n  if (comment.deletedAt) {\n    yType.setAttr(\"deletedAt\", comment.deletedAt.getTime());\n    yType.setAttr(\"body\", undefined);\n  } else {\n    yType.setAttr(\"body\", comment.body);\n  }\n  if (comment.reactions.length > 0) {\n    throw new Error(\"Reactions should be empty in commentToYType\");\n  }\n\n  /**\n   * Reactions are stored in a map keyed by {userId-emoji},\n   * this makes it easy to add / remove reactions and in a way that works local-first.\n   * The cost is that \"reading\" the reactions is a bit more complex (see yTypeToReactions).\n   */\n  yType.setAttr(\"reactionsByUser\", new Y.Type());\n  yType.setAttr(\"metadata\", comment.metadata);\n\n  return yType;\n}\n\nexport function threadToYType(thread: ThreadData) {\n  const yType = new Y.Type();\n  yType.setAttr(\"id\", thread.id);\n  yType.setAttr(\"createdAt\", thread.createdAt.getTime());\n  yType.setAttr(\"updatedAt\", thread.updatedAt.getTime());\n  const commentsType = new Y.Type();\n\n  commentsType.push(thread.comments.map((comment) => commentToYType(comment)));\n\n  yType.setAttr(\"comments\", commentsType);\n  yType.setAttr(\"resolved\", thread.resolved);\n  yType.setAttr(\"resolvedUpdatedAt\", thread.resolvedUpdatedAt?.getTime());\n  yType.setAttr(\"resolvedBy\", thread.resolvedBy);\n  yType.setAttr(\"metadata\", thread.metadata);\n  return yType;\n}\n\ntype SingleUserCommentReactionData = {\n  emoji: string;\n  createdAt: Date;\n  userId: string;\n};\n\nexport function yTypeToReaction(yType: Y.Type): SingleUserCommentReactionData {\n  return {\n    emoji: yType.getAttr(\"emoji\"),\n    createdAt: new Date(yType.getAttr(\"createdAt\")),\n    userId: yType.getAttr(\"userId\"),\n  };\n}\n\nfunction yTypeToReactions(yType: Y.Type): CommentReactionData[] {\n  const flatReactions = [...yType.attrValues()].map((reaction: Y.Type) =>\n    yTypeToReaction(reaction),\n  );\n  // combine reactions by the same emoji\n  return flatReactions.reduce(\n    (acc: CommentReactionData[], reaction: SingleUserCommentReactionData) => {\n      const existingReaction = acc.find((r) => r.emoji === reaction.emoji);\n      if (existingReaction) {\n        existingReaction.userIds.push(reaction.userId);\n        existingReaction.createdAt = new Date(\n          Math.min(\n            existingReaction.createdAt.getTime(),\n            reaction.createdAt.getTime(),\n          ),\n        );\n      } else {\n        acc.push({\n          emoji: reaction.emoji,\n          createdAt: reaction.createdAt,\n          userIds: [reaction.userId],\n        });\n      }\n      return acc;\n    },\n    [] as CommentReactionData[],\n  );\n}\n\nexport function yTypeToComment(yType: Y.Type): CommentData {\n  return {\n    type: \"comment\",\n    id: yType.getAttr(\"id\"),\n    userId: yType.getAttr(\"userId\"),\n    createdAt: new Date(yType.getAttr(\"createdAt\")),\n    updatedAt: new Date(yType.getAttr(\"updatedAt\")),\n    deletedAt: yType.getAttr(\"deletedAt\")\n      ? new Date(yType.getAttr(\"deletedAt\"))\n      : undefined,\n    reactions: yTypeToReactions(yType.getAttr(\"reactionsByUser\")),\n    metadata: yType.getAttr(\"metadata\"),\n    body: yType.getAttr(\"body\"),\n  };\n}\n\nexport function yTypeToThread(yType: Y.Type): ThreadData {\n  return {\n    type: \"thread\",\n    id: yType.getAttr(\"id\"),\n    createdAt: new Date(yType.getAttr(\"createdAt\")),\n    updatedAt: new Date(yType.getAttr(\"updatedAt\")),\n    comments: ((yType.getAttr(\"comments\") as Y.Type)?.toArray() || []).map(\n      (comment) => yTypeToComment(comment as Y.Type),\n    ),\n    resolved: yType.getAttr(\"resolved\"),\n    resolvedUpdatedAt: new Date(yType.getAttr(\"resolvedUpdatedAt\")),\n    resolvedBy: yType.getAttr(\"resolvedBy\"),\n    metadata: yType.getAttr(\"metadata\"),\n  };\n}\n","import * as Y from \"@y/y\";\nimport type { ThreadData } from \"../../comments/types.js\";\nimport { ThreadStore } from \"../../comments/threadstore/ThreadStore.js\";\nimport type { ThreadStoreAuth } from \"../../comments/threadstore/ThreadStoreAuth.js\";\nimport { yTypeToThread } from \"./yjsHelpers.js\";\n\n/**\n * This is an abstract class that only implements the READ methods required by the ThreadStore interface.\n * The data is read from a @y/y Type used as a map (via attributes).\n */\nexport abstract class YjsThreadStoreBase extends ThreadStore {\n  constructor(\n    protected readonly threadsYType: Y.Type,\n    auth: ThreadStoreAuth,\n  ) {\n    super(auth);\n  }\n\n  // TODO: async / reactive interface?\n  public getThread(threadId: string) {\n    const yThread = this.threadsYType.getAttr(threadId);\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n    const thread = yTypeToThread(yThread);\n    return thread;\n  }\n\n  public getThreads(): Map<string, ThreadData> {\n    const threadMap = new Map<string, ThreadData>();\n    this.threadsYType.forEachAttr((yThread: any, id: string | number) => {\n      if (yThread instanceof Y.Type) {\n        threadMap.set(String(id), yTypeToThread(yThread));\n      }\n    });\n    return threadMap;\n  }\n\n  public subscribe(cb: (threads: Map<string, ThreadData>) => void) {\n    const observer = () => {\n      cb(this.getThreads());\n    };\n\n    this.threadsYType.observeDeep(observer);\n\n    return () => {\n      this.threadsYType.unobserveDeep(observer);\n    };\n  }\n}\n","import * as Y from \"@y/y\";\nimport type { CommentBody } from \"../../comments/types.js\";\nimport type { ThreadStoreAuth } from \"../../comments/threadstore/ThreadStoreAuth.js\";\nimport { YjsThreadStoreBase } from \"./YjsThreadStoreBase.js\";\n\n/**\n * This is a REST-based implementation of the YjsThreadStoreBase for @y/y (v14).\n * It Reads data directly from the underlying document (same as YjsThreadStore),\n * but for Writes, it sends data to a REST API that should:\n * - check the user has the correct permissions to make the desired changes\n * - apply the updates to the underlying Yjs document\n *\n * (see https://github.com/TypeCellOS/BlockNote-demo-nextjs-hocuspocus)\n *\n * The reason we still use the Yjs document as underlying storage is that it makes it easy to\n * sync updates in real-time to other collaborators.\n * (but technically, you could also implement a different storage altogether\n * and not store the thread related data in the Yjs document)\n */\nexport class RESTYjsThreadStore extends YjsThreadStoreBase {\n  constructor(\n    private readonly BASE_URL: string,\n    private readonly headers: Record<string, string>,\n    threadsYType: Y.Type,\n    auth: ThreadStoreAuth,\n  ) {\n    super(threadsYType, auth);\n  }\n\n  private doRequest = async (path: string, method: string, body?: any) => {\n    const response = await fetch(`${this.BASE_URL}${path}`, {\n      method,\n      body: JSON.stringify(body),\n      headers: {\n        \"Content-Type\": \"application/json\",\n        ...this.headers,\n      },\n    });\n\n    if (!response.ok) {\n      throw new Error(`Failed to ${method} ${path}: ${response.statusText}`);\n    }\n\n    return response.json();\n  };\n\n  public addThreadToDocument = async (options: {\n    threadId: string;\n    selection: {\n      head: number;\n      anchor: number;\n    };\n  }) => {\n    const { threadId, ...rest } = options;\n    return this.doRequest(`/${threadId}/addToDocument`, \"POST\", rest);\n  };\n\n  public createThread = async (options: {\n    initialComment: {\n      body: CommentBody;\n      metadata?: any;\n    };\n    metadata?: any;\n  }) => {\n    return this.doRequest(\"\", \"POST\", options);\n  };\n\n  public addComment = (options: {\n    comment: {\n      body: CommentBody;\n      metadata?: any;\n    };\n    threadId: string;\n  }) => {\n    const { threadId, ...rest } = options;\n    return this.doRequest(`/${threadId}/comments`, \"POST\", rest);\n  };\n\n  public updateComment = (options: {\n    comment: {\n      body: CommentBody;\n      metadata?: any;\n    };\n    threadId: string;\n    commentId: string;\n  }) => {\n    const { threadId, commentId, ...rest } = options;\n    return this.doRequest(`/${threadId}/comments/${commentId}`, \"PUT\", rest);\n  };\n\n  public deleteComment = (options: {\n    threadId: string;\n    commentId: string;\n    softDelete?: boolean;\n  }) => {\n    const { threadId, commentId, ...rest } = options;\n    return this.doRequest(\n      `/${threadId}/comments/${commentId}?soft=${!!rest.softDelete}`,\n      \"DELETE\",\n    );\n  };\n\n  public deleteThread = (options: { threadId: string }) => {\n    return this.doRequest(`/${options.threadId}`, \"DELETE\");\n  };\n\n  public resolveThread = (options: { threadId: string }) => {\n    return this.doRequest(`/${options.threadId}/resolve`, \"POST\");\n  };\n\n  public unresolveThread = (options: { threadId: string }) => {\n    return this.doRequest(`/${options.threadId}/unresolve`, \"POST\");\n  };\n\n  public addReaction = (options: {\n    threadId: string;\n    commentId: string;\n    emoji: string;\n  }) => {\n    const { threadId, commentId, ...rest } = options;\n    return this.doRequest(\n      `/${threadId}/comments/${commentId}/reactions`,\n      \"POST\",\n      rest,\n    );\n  };\n\n  public deleteReaction = (options: {\n    threadId: string;\n    commentId: string;\n    emoji: string;\n  }) => {\n    return this.doRequest(\n      `/${options.threadId}/comments/${options.commentId}/reactions/${options.emoji}`,\n      \"DELETE\",\n    );\n  };\n}\n","import { uuidv4 } from \"lib0/random\";\nimport * as Y from \"@y/y\";\nimport type {\n  CommentBody,\n  CommentData,\n  ThreadData,\n} from \"../../comments/types.js\";\nimport type { ThreadStoreAuth } from \"../../comments/threadstore/ThreadStoreAuth.js\";\nimport { YjsThreadStoreBase } from \"./YjsThreadStoreBase.js\";\nimport {\n  commentToYType,\n  threadToYType,\n  yTypeToComment,\n  yTypeToThread,\n} from \"./yjsHelpers.js\";\n\n/**\n * This is a @y/y (v14)-based implementation of the ThreadStore interface.\n *\n * It reads and writes thread / comments information directly to the underlying Yjs Document.\n *\n * @important While this is the easiest to add to your app, there are two challenges:\n * - The user needs to be able to write to the Yjs document to store the information.\n *   So a user without write access to the Yjs document cannot leave any comments.\n * - Even with write access, the operations are not secure. Unless your Yjs server\n *   guards against malicious operations, it's technically possible for one user to make changes to another user's comments, etc.\n *   (even though these options are not visible in the UI, a malicious user can make unauthorized changes to the underlying Yjs document)\n */\nexport class YjsThreadStore extends YjsThreadStoreBase {\n  constructor(\n    private readonly userId: string,\n    threadsYType: Y.Type,\n    auth: ThreadStoreAuth,\n  ) {\n    super(threadsYType, auth);\n  }\n\n  private transact = <T, R>(\n    fn: (options: T) => R,\n  ): ((options: T) => Promise<R>) => {\n    return async (options: T) => {\n      return this.threadsYType.doc!.transact(() => {\n        return fn(options);\n      });\n    };\n  };\n\n  public createThread = this.transact(\n    (options: {\n      initialComment: {\n        body: CommentBody;\n        metadata?: any;\n      };\n      metadata?: any;\n    }) => {\n      if (!this.auth.canCreateThread()) {\n        throw new Error(\"Not authorized\");\n      }\n\n      const date = new Date();\n\n      const comment: CommentData = {\n        type: \"comment\",\n        id: uuidv4(),\n        userId: this.userId,\n        createdAt: date,\n        updatedAt: date,\n        reactions: [],\n        metadata: options.initialComment.metadata,\n        body: options.initialComment.body,\n      };\n\n      const thread: ThreadData = {\n        type: \"thread\",\n        id: uuidv4(),\n        createdAt: date,\n        updatedAt: date,\n        comments: [comment],\n        resolved: false,\n        metadata: options.metadata,\n      };\n\n      this.threadsYType.setAttr(thread.id, threadToYType(thread));\n\n      return thread;\n    },\n  );\n\n  // YjsThreadStore does not support addThreadToDocument\n  public addThreadToDocument = undefined;\n\n  public addComment = this.transact(\n    (options: {\n      comment: {\n        body: CommentBody;\n        metadata?: any;\n      };\n      threadId: string;\n    }) => {\n      const yThread = this.threadsYType.getAttr(options.threadId) as\n        | Y.Type\n        | undefined;\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      if (!this.auth.canAddComment(yTypeToThread(yThread))) {\n        throw new Error(\"Not authorized\");\n      }\n\n      const date = new Date();\n      const comment: CommentData = {\n        type: \"comment\",\n        id: uuidv4(),\n        userId: this.userId,\n        createdAt: date,\n        updatedAt: date,\n        deletedAt: undefined,\n        reactions: [],\n        metadata: options.comment.metadata,\n        body: options.comment.body,\n      };\n\n      (yThread.getAttr(\"comments\") as Y.Type).push([commentToYType(comment)]);\n\n      yThread.setAttr(\"updatedAt\", new Date().getTime());\n      return comment;\n    },\n  );\n\n  public updateComment = this.transact(\n    (options: {\n      comment: {\n        body: CommentBody;\n        metadata?: any;\n      };\n      threadId: string;\n      commentId: string;\n    }) => {\n      const yThread = this.threadsYType.getAttr(options.threadId) as\n        | Y.Type\n        | undefined;\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const commentsType = yThread.getAttr(\"comments\") as Y.Type;\n      const yCommentIndex = yTypeFindIndex(\n        commentsType,\n        (comment) => (comment as Y.Type).getAttr(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = commentsType.get(yCommentIndex) as Y.Type;\n\n      if (!this.auth.canUpdateComment(yTypeToComment(yComment))) {\n        throw new Error(\"Not authorized\");\n      }\n\n      yComment.setAttr(\"body\", options.comment.body);\n      yComment.setAttr(\"updatedAt\", new Date().getTime());\n      yComment.setAttr(\"metadata\", options.comment.metadata);\n    },\n  );\n\n  public deleteComment = this.transact(\n    (options: {\n      threadId: string;\n      commentId: string;\n      softDelete?: boolean;\n    }) => {\n      const yThread = this.threadsYType.getAttr(options.threadId) as\n        | Y.Type\n        | undefined;\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const commentsType = yThread.getAttr(\"comments\") as Y.Type;\n      const yCommentIndex = yTypeFindIndex(\n        commentsType,\n        (comment) => (comment as Y.Type).getAttr(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = commentsType.get(yCommentIndex) as Y.Type;\n\n      if (!this.auth.canDeleteComment(yTypeToComment(yComment))) {\n        throw new Error(\"Not authorized\");\n      }\n\n      if (yComment.getAttr(\"deletedAt\")) {\n        throw new Error(\"Comment already deleted\");\n      }\n\n      if (options.softDelete) {\n        yComment.setAttr(\"deletedAt\", new Date().getTime());\n        yComment.setAttr(\"body\", undefined);\n      } else {\n        commentsType.delete(yCommentIndex);\n      }\n\n      if (\n        commentsType\n          .toArray()\n          .every((comment) => (comment as Y.Type).getAttr(\"deletedAt\"))\n      ) {\n        // all comments deleted\n        if (options.softDelete) {\n          yThread.setAttr(\"deletedAt\", new Date().getTime());\n        } else {\n          this.threadsYType.deleteAttr(options.threadId);\n        }\n      }\n\n      yThread.setAttr(\"updatedAt\", new Date().getTime());\n    },\n  );\n\n  public deleteThread = this.transact((options: { threadId: string }) => {\n    if (\n      !this.auth.canDeleteThread(\n        yTypeToThread(this.threadsYType.getAttr(options.threadId) as Y.Type),\n      )\n    ) {\n      throw new Error(\"Not authorized\");\n    }\n\n    this.threadsYType.deleteAttr(options.threadId);\n  });\n\n  public resolveThread = this.transact((options: { threadId: string }) => {\n    const yThread = this.threadsYType.getAttr(options.threadId) as\n      | Y.Type\n      | undefined;\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n\n    if (!this.auth.canResolveThread(yTypeToThread(yThread))) {\n      throw new Error(\"Not authorized\");\n    }\n\n    yThread.setAttr(\"resolved\", true);\n    yThread.setAttr(\"resolvedUpdatedAt\", new Date().getTime());\n    yThread.setAttr(\"resolvedBy\", this.userId);\n  });\n\n  public unresolveThread = this.transact((options: { threadId: string }) => {\n    const yThread = this.threadsYType.getAttr(options.threadId) as\n      | Y.Type\n      | undefined;\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n\n    if (!this.auth.canUnresolveThread(yTypeToThread(yThread))) {\n      throw new Error(\"Not authorized\");\n    }\n\n    yThread.setAttr(\"resolved\", false);\n    yThread.setAttr(\"resolvedUpdatedAt\", new Date().getTime());\n  });\n\n  public addReaction = this.transact(\n    (options: { threadId: string; commentId: string; emoji: string }) => {\n      const yThread = this.threadsYType.getAttr(options.threadId) as\n        | Y.Type\n        | undefined;\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const commentsType = yThread.getAttr(\"comments\") as Y.Type;\n      const yCommentIndex = yTypeFindIndex(\n        commentsType,\n        (comment) => (comment as Y.Type).getAttr(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = commentsType.get(yCommentIndex) as Y.Type;\n\n      if (!this.auth.canAddReaction(yTypeToComment(yComment), options.emoji)) {\n        throw new Error(\"Not authorized\");\n      }\n\n      const date = new Date();\n\n      const key = `${this.userId}-${options.emoji}`;\n\n      const reactionsByUser = yComment.getAttr(\"reactionsByUser\") as Y.Type;\n\n      if (reactionsByUser.hasAttr(key)) {\n        // already exists\n        return;\n      } else {\n        const reaction = new Y.Type();\n        reaction.setAttr(\"emoji\", options.emoji);\n        reaction.setAttr(\"createdAt\", date.getTime());\n        reaction.setAttr(\"userId\", this.userId);\n        reactionsByUser.setAttr(key, reaction);\n      }\n    },\n  );\n\n  public deleteReaction = this.transact(\n    (options: { threadId: string; commentId: string; emoji: string }) => {\n      const yThread = this.threadsYType.getAttr(options.threadId) as\n        | Y.Type\n        | undefined;\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const commentsType = yThread.getAttr(\"comments\") as Y.Type;\n      const yCommentIndex = yTypeFindIndex(\n        commentsType,\n        (comment) => (comment as Y.Type).getAttr(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = commentsType.get(yCommentIndex) as Y.Type;\n\n      if (\n        !this.auth.canDeleteReaction(yTypeToComment(yComment), options.emoji)\n      ) {\n        throw new Error(\"Not authorized\");\n      }\n\n      const key = `${this.userId}-${options.emoji}`;\n\n      const reactionsByUser = yComment.getAttr(\"reactionsByUser\") as Y.Type;\n\n      reactionsByUser.deleteAttr(key);\n    },\n  );\n}\n\nfunction yTypeFindIndex(yType: Y.Type, predicate: (item: any) => boolean) {\n  for (let i = 0; i < yType.length; i++) {\n    if (predicate(yType.get(i))) {\n      return i;\n    }\n  }\n  return -1;\n}\n","import * as Y from \"@y/y\";\nimport { decodeAny, encodeAny } from \"lib0/buffer\";\n\nimport {\n  CURRENT_VERSION_ID,\n  sortSnapshotsNewestFirst,\n  VersioningEndpointsFactory,\n  type VersioningEndpoints,\n  type VersionSnapshot,\n} from \"../../extensions/Versioning/index.js\";\nimport { uint32 } from \"lib0/random\";\nimport { YCursorExtension } from \"../extensions/YCursorPlugin.js\";\nimport { YSyncExtension } from \"../extensions/YSync.js\";\n\n/**\n * Name of the root {@link Y.Type} map on the live collaboration doc that stores\n * a mutable `versionId -> name` mapping. Because YHub attributions are\n * immutable, version names that need to be editable (renamed) live here on the\n * Y.Doc instead of (or in addition to) the immutable `name` attribution.\n */\nconst VERSION_NAMES_MAP = \"__bn_version_names\";\n\n/**\n * Options for creating a YHub versioning endpoints instance.\n */\nexport interface YHubVersioningOptions {\n  /**\n   * Base URL of the YHub API (e.g. `\"https://yhub.example.com\"`).\n   * Must **not** include a trailing slash.\n   */\n  baseUrl: string;\n\n  /** YHub organisation identifier. */\n  org: string;\n\n  /** Document identifier within the organisation. */\n  docId: string;\n\n  /**\n   * Optional headers to include in every request (e.g. authentication tokens).\n   */\n  headers?: Record<string, string>;\n\n  /**\n   * Maximum number of activity entries to fetch when listing versions.\n   * @default 50\n   */\n  activityLimit?: number;\n\n  /**\n   * When set, forwarded as the `group` query param to the YHub activity API,\n   * controlling whether adjacent edits are grouped into single entries.\n   */\n  group?: boolean;\n\n  /**\n   * Maximum gap (in ms) between edits for them to be grouped together.\n   * Forwarded as the `groupMaxGap` query param.\n   * @default 10000\n   */\n  groupMaxGap?: number;\n\n  /**\n   * Maximum total duration (in ms) a single group of edits may span.\n   * When set, forwarded as the `groupMaxDuration` query param.\n   */\n  groupMaxDuration?: number;\n\n  // TODO mergeUsers is not in standard yhub, but it exists in our fork.\n  /**\n   * When `true`, adjacent edits are grouped together even when made by\n   * *different* users (their ids accumulate in the grouped entry's `by`).\n   * When `false` (the default), only same-user adjacent edits are merged.\n   * Forwarded as the `mergeUsers` query param.\n   * @default false\n   */\n  mergeUsers?: boolean;\n}\n\n/**\n * Shape of a single activity entry returned by the YHub\n * `GET /activity/{org}/{docId}` endpoint (after `decodeAny`).\n */\ninterface YHubActivityEntry {\n  /** Start of the change window (unix-ms timestamp). */\n  from: number;\n  /** End of the change window (unix-ms timestamp). */\n  to: number;\n  /** Comma separated list of user-ids that matches the attribution */\n  by?: string;\n  /** Custom attribution key-value pairs (when `customAttributions=true`). */\n  customAttributions?: Array<{ k: string; v: string }>;\n}\n\n/**\n * Shape returned by the YHub `GET /changeset/{org}/{docId}` endpoint (after\n * `decodeAny`).\n */\ninterface YHubChangeset {\n  /** Full Y.Doc state **before** the changeset window. */\n  prevDoc?: Uint8Array;\n  /** Full Y.Doc state **after** the changeset window. */\n  nextDoc?: Uint8Array;\n  /**\n   * Encoded {@link Y.ContentMap} describing who authored each change in the\n   * window and when. Present when the changeset is requested with\n   * `attributions=true`.\n   */\n  attributions?: Uint8Array;\n}\n\n/**\n * Whether an activity entry is a version marker (created with a `type:version`\n * custom attribution) as opposed to a plain edit.\n */\nfunction isVersionEntry(entry: YHubActivityEntry): boolean {\n  return (\n    entry.customAttributions?.some(\n      (a) => a.k === \"type\" && a.v === \"version\",\n    ) ?? false\n  );\n}\n\n/**\n * Convert a YHub activity entry into a {@link VersionSnapshot}.\n *\n * Version markers (entries with a `type:version` custom attribution) map to\n * named snapshots: the `id` attribution becomes the snapshot identifier and the\n * `name` attribution its name. Any other (plain edit) entry maps to a\n * history-only snapshot with a synthetic `history-<to>-<index>` id and no name.\n * In both cases the entry's `by` user-ids are passed through raw on\n * {@link VersionSnapshot.by} — resolving them to user info is the view layer's\n * job.\n *\n * The history id embeds the entry's `index` within the activity response\n * because YHub can emit multiple activity entries sharing the same `to`\n * timestamp (e.g. distinct same-`insertAt` patches that grouping did not merge),\n * and `to` alone would then produce colliding `history-<to>` ids — duplicate\n * React keys in the sidebar. The `index` disambiguates them. The changeset\n * lookups (`getContent`/`getAttributions`/`restore`) key off\n * {@link VersionSnapshot.createdAt} (= `entry.to`), never the id, so embedding\n * the index in the id is safe.\n */\nfunction activityToSnapshot(\n  entry: YHubActivityEntry,\n  index: number,\n): VersionSnapshot | undefined {\n  const by = entry.by\n    ?.split(\",\")\n    .map((s) => s.trim())\n    .filter(Boolean);\n  const byField = by && by.length > 0 ? by : undefined;\n\n  if (isVersionEntry(entry)) {\n    const id = entry.customAttributions?.find((a) => a.k === \"id\")?.v;\n    if (id === undefined) {\n      return undefined;\n    }\n    const attributionName = entry.customAttributions?.find(\n      (a) => a.k === \"name\",\n    )?.v;\n    return {\n      id,\n      name: attributionName,\n      createdAt: entry.to,\n      updatedAt: entry.to,\n      by: byField,\n    };\n  }\n\n  return {\n    id: `history-${entry.to}-${index}`,\n    createdAt: entry.to,\n    updatedAt: entry.to,\n    by: byField,\n  };\n}\n\nasync function yhubFetch(\n  url: string,\n  headers: Record<string, string>,\n  init?: RequestInit,\n): Promise<ArrayBuffer> {\n  const res = await fetch(url, {\n    ...init,\n    headers: {\n      ...headers,\n      ...(init?.headers instanceof Headers\n        ? Object.fromEntries(init.headers.entries())\n        : Array.isArray(init?.headers)\n          ? Object.fromEntries(init.headers)\n          : init?.headers),\n    },\n  });\n  if (!res.ok) {\n    throw new Error(\n      `YHub request failed: ${res.status} ${res.statusText} (${url})`,\n    );\n  }\n  return res.arrayBuffer();\n}\n\n/**\n * Create a {@link VersioningEndpoints} implementation backed by the\n * [YHub](https://github.com/yjs/yhub) HTTP API.\n *\n * Versions are created by PATCHing the document with custom attributions\n * (`type:version` + an optional `name`). The `list` endpoint returns the full\n * activity timeline, mapping `type:version` markers to named versions and every\n * other entry to a history-only snapshot, so the sidebar can show both the\n * named versions and the complete edit history.\n *\n * A version's id lives in immutable YHub attributions (`type:version` + `id`),\n * so it is fixed at creation time. Version *names*, however, are stored in a\n * mutable `__bn_version_names` map on the live collaboration doc (see\n * {@link VERSION_NAMES_MAP}), so `rename` is supported and simply updates that\n * store.\n *\n * @example\n * ```ts\n * import { withCollaboration } from \"@blocknote/core/y\";\n * import { createYHubVersioningEndpoints } from \"@blocknote/core/y\";\n *\n * const editor = BlockNoteEditor.create(\n *   withCollaboration({\n *     collaboration: {\n *       fragment,\n *       user: { name: \"Alice\", color: \"#ff0\" },\n *       provider,\n *       versioningEndpoints: createYHubVersioningEndpoints({\n *         baseUrl: \"https://yhub.example.com\",\n *         org: \"my-org\",\n *         docId: \"my-doc\",\n *       }),\n *     },\n *   }),\n * );\n * ```\n */\nexport function createYHubVersioningEndpoints(\n  options: YHubVersioningOptions,\n): VersioningEndpointsFactory<Y.Type, Uint8Array, Y.ContentMap> {\n  const {\n    baseUrl,\n    org,\n    docId,\n    headers = {},\n    activityLimit = 50,\n    group,\n  } = options;\n\n  const activityUrl = `${baseUrl}/activity/${org}/${docId}`;\n  const changesetUrl = `${baseUrl}/changeset/${org}/${docId}`;\n  const rollbackUrl = `${baseUrl}/rollback/${org}/${docId}`;\n\n  return (editor) => {\n    /**\n     * The mutable per-id version-name store on the live collaboration doc.\n     *\n     * Returns the root {@link VERSION_NAMES_MAP} map-typed {@link Y.Type}, which\n     * uses `setAttr`/`getAttr` for keyed access (this Yjs fork has a single\n     * unified `Y.Type` rather than a distinct `Y.Map`). `undefined` until the\n     * live doc has been captured from a `create` call.\n     */\n    const getVersionNamesMap = (): Y.Type | undefined => {\n      const fragment =\n        editor.getExtension<typeof YSyncExtension>(\"ySync\")?.fragment.doc;\n      // `fragment` is undefined until the live doc has been captured (e.g. no\n      // ySync extension attached yet); return undefined rather than throwing so\n      // callers can gracefully fall back to the immutable name attribution.\n      return fragment?.get(VERSION_NAMES_MAP);\n    };\n\n    /**\n     * Build the synthetic \"current version\" snapshot, or `undefined` when the\n     * live document matches the latest saved version (no edits since).\n     *\n     * Both lookups are made here, independently of the grouped `list()` request:\n     *\n     *  - the newest activity entry of *any* kind (ungrouped, so its `to` is the\n     *    true last-edit time), and\n     *  - the newest **version marker** (via the `withCustomAttributions`\n     *    server-side filter).\n     *\n     * Deriving the marker time from `list()`'s grouped entries would be wrong:\n     * with grouping (especially `mergeUsers`) the newest marker's group absorbs\n     * the later unsaved edit, so the group's `to` equals the edit's `to` and the\n     * comparison below can never fire. Fetching the marker unmerged avoids that.\n     */\n    const getCurrentVersionEntry = async (): Promise<\n      VersionSnapshot | undefined\n    > => {\n      const latestParams = new URLSearchParams({\n        order: \"desc\",\n        limit: \"1\",\n        customAttributions: \"true\",\n      });\n      const latestVersionParams = new URLSearchParams({\n        order: \"desc\",\n        limit: \"1\",\n        customAttributions: \"true\",\n        // Server-side filter to `type:version` markers only, so this ignores the\n        // plain edits that would otherwise be the newest entries.\n        withCustomAttributions: \"type:version\",\n      });\n\n      const [latestBuf, latestVersionBuf] = await Promise.all([\n        yhubFetch(`${activityUrl}?${latestParams}`, headers),\n        yhubFetch(`${activityUrl}?${latestVersionParams}`, headers),\n      ]);\n      const latestEdit = (\n        decodeAny(new Uint8Array(latestBuf)) as YHubActivityEntry[]\n      )[0];\n      const latestVersion = (\n        decodeAny(new Uint8Array(latestVersionBuf)) as YHubActivityEntry[]\n      )[0];\n\n      if (!latestEdit || latestEdit.to <= (latestVersion?.to ?? 0)) {\n        return undefined;\n      }\n\n      // Build the synthetic entry directly rather than via `activityToSnapshot`,\n      // whose `id` comes from a string-typed wire attribution — the current\n      // entry's id is the `CURRENT_VERSION_ID` symbol, not a real version id.\n      const by =\n        latestEdit.by\n          ?.split(\",\")\n          .map((t) => t.trim())\n          .filter(Boolean) ?? [];\n      return {\n        id: CURRENT_VERSION_ID,\n        createdAt: latestEdit.to,\n        updatedAt: latestEdit.to,\n        by: by.length > 0 ? by : undefined,\n      };\n    };\n\n    /**\n     * PATCH the current document state to YHub, optionally with custom\n     * attributions. Used both for creating named version markers and for\n     * backing up the document before a restore.\n     */\n    const patchDoc = async (\n      fragment: Y.Type,\n      customAttributions: Array<{ k: string; v: any }>,\n      by?: string,\n    ) => {\n      const doc = fragment.doc;\n      if (!doc) {\n        throw new Error(\n          \"Cannot patch document: the Y.Type is not attached to a Y.Doc.\",\n        );\n      }\n\n      // YHub only records custom attributions when they attach to NEW content\n      // that survives its server-side diff. An update-less PATCH is rejected\n      // (400 — \"at least one of update or awareness must be present\"), and even\n      // if it weren't, there'd be no content for the attributions to ride on, so\n      // no activity entry is created. YHub has no metadata-only marker path.\n      //\n      // So we introduce a tiny piece of novel content for the marker to attach\n      // to: a single insert into a dedicated `__bn_version_markers` fragment that\n      // the editor never renders. A fresh Y.Doc guarantees a clientID/content the\n      // server has never seen, so the diff is non-empty and the attributions land\n      // on it. The reconstructed document at this version's timestamp still\n      // contains the full editor content — this marker only ever lives in the\n      // throwaway fragment.\n      const markerDoc = new Y.Doc();\n      markerDoc.get(\"__bn_version_markers\", \"XmlFragment\").insert(0, [\"v\"]);\n      const update = Y.encodeStateAsUpdate(markerDoc);\n\n      const body: Record<string, unknown> = { update, customAttributions };\n\n      await yhubFetch(\n        `${baseUrl}/ydoc/${org}/${docId}${by ? `?userid=${by}` : \"\"}`,\n        headers,\n        {\n          method: \"PATCH\",\n          body: encodeAny(body) as BufferSource,\n        },\n      );\n    };\n\n    /**\n     * Create a named version marker for the current document state by PATCHing\n     * it with `type:version` custom attributions.\n     */\n    const create: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"create\"] = async (fragment, options) => {\n      const id = String(uint32());\n      const now = Date.now();\n\n      if (options?.name) {\n        getVersionNamesMap()?.setAttr(id, options.name);\n      }\n\n      const customAttributions: Array<{ k: string; v: string }> = [\n        { k: \"type\", v: \"version\" },\n        { k: \"id\", v: id },\n      ];\n      if (options?.name) {\n        customAttributions.push({ k: \"name\", v: options.name });\n      }\n\n      const user = editor\n        .getExtension<typeof YCursorExtension>(\"yCursor\")\n        ?.getUser();\n      await patchDoc(fragment, customAttributions, user?.id);\n\n      return {\n        id,\n        name: options?.name,\n        createdAt: now,\n        updatedAt: now,\n        by: user?.id,\n      };\n    };\n\n    /**\n     * Reconstruct the full document state as it was at a given `to` timestamp.\n     *\n     * The changeset endpoint builds `nextDoc` purely from the `to` timestamp\n     * range — it ignores `withCustomAttributions` for doc reconstruction (that\n     * filter only scopes the attribution overlay). So historical document state\n     * can only be retrieved by timestamp, never by the version's `id`.\n     */\n    const getContentAt = async (to: number): Promise<Uint8Array> => {\n      const params = new URLSearchParams({\n        ydoc: \"true\",\n        to: String(to),\n      });\n\n      const buf = await yhubFetch(`${changesetUrl}?${params}`, headers);\n      const changeset = decodeAny(new Uint8Array(buf)) as YHubChangeset;\n\n      if (!changeset.nextDoc) {\n        throw new Error(`YHub returned no document state at timestamp ${to}.`);\n      }\n\n      return Y.convertUpdateFormatV1ToV2(changeset.nextDoc);\n    };\n\n    /**\n     * Fetch the full document content for a saved version snapshot.\n     *\n     * The snapshot's `createdAt` is the activity entry's `to` timestamp (see\n     * {@link activityToSnapshot}), which is exactly what the changeset API needs.\n     */\n    const getContent: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"getContent\"] = async (snapshot) => {\n      return getContentAt(snapshot.createdAt);\n    };\n\n    /**\n     * Fetch the authorship attributions for the changes between two snapshots\n     * (or from the start of the document when `compareTo` is omitted).\n     *\n     * Snapshots carry their `to` timestamp directly in `createdAt`, so no\n     * activity lookup is needed to resolve the changeset window.\n     */\n    const getAttributions: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"getAttributions\"] = async (snapshot, compareTo) => {\n      const to = snapshot.createdAt;\n      const from = compareTo !== undefined ? compareTo.createdAt : 0;\n\n      const params = new URLSearchParams({\n        from: String(from),\n        to: String(to),\n        attributions: \"true\",\n      });\n\n      const buf = await yhubFetch(`${changesetUrl}?${params}`, headers);\n      const changeset = decodeAny(new Uint8Array(buf)) as YHubChangeset;\n\n      if (!changeset.attributions) {\n        throw new Error(\n          `YHub returned no attributions for snapshot ${String(snapshot.id)}.`,\n        );\n      }\n\n      return Y.decodeContentMap(changeset.attributions);\n    };\n\n    /**\n     * Restore the document to a saved version: fetch the target version's\n     * content and roll back everything after it.\n     *\n     * The snapshot's `createdAt` is the activity entry's `to` timestamp.\n     */\n    const restore: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"restore\"] = async (_fragment, snapshot) => {\n      const to = snapshot.createdAt;\n      const snapshotContent = await getContentAt(to);\n\n      await yhubFetch(`${rollbackUrl}?from=${to}`, headers, {\n        method: \"POST\",\n        body: encodeAny({ from: to }) as BufferSource,\n      });\n\n      return snapshotContent;\n    };\n\n    /**\n     * Rename a saved version by updating its entry in the mutable\n     * {@link VERSION_NAMES_MAP} store on the live collaboration doc.\n     *\n     * The version's `id` remains fixed in its immutable YHub attributions —\n     * only the editable name in the map is changed. Passing an empty or\n     * `undefined` name clears the entry (falling back to the immutable `name`\n     * attribution captured at creation time).\n     */\n    const rename: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"rename\"] = async (snapshot, name) => {\n      if (typeof snapshot.id !== \"string\") {\n        // CURRENT_VERSION_ID (symbol) is not renameable.\n        return;\n      }\n      const map = getVersionNamesMap();\n      if (!map) {\n        throw new Error(\n          \"Cannot rename version: no live collaboration document is available.\",\n        );\n      }\n      if (name === undefined || name === \"\") {\n        map.deleteAttr(snapshot.id);\n      } else {\n        map.setAttr(snapshot.id, name);\n      }\n    };\n\n    /**\n     * List the full version timeline (newest first), plus a synthetic\n     * \"current version\" entry when the live document has unsaved edits.\n     *\n     * Returns the entire activity timeline: `type:version` markers are mapped\n     * to named snapshots and every other entry to a history-only snapshot (see\n     * {@link activityToSnapshot}), so the sidebar can offer both a \"named\n     * versions\" and a full \"history\" view. Author user-ids are passed through\n     * raw on {@link VersionSnapshot.by} — the view layer resolves them to user\n     * info via the versioning extension's user store.\n     */\n    const list: VersioningEndpoints<\n      Y.Type,\n      Uint8Array,\n      Y.ContentMap\n    >[\"list\"] = async () => {\n      // Read the grouping knobs fresh from `options` so a caller mutating the\n      // object it passed in reconfigures grouping on the next refresh (see the\n      // note where these are deliberately left out of the destructure above).\n      const groupMaxGap = options.groupMaxGap ?? 10000;\n      const groupMaxDuration = options.groupMaxDuration;\n      const mergeUsers = options.mergeUsers;\n\n      const params = new URLSearchParams({\n        order: \"desc\",\n        limit: String(activityLimit),\n        customAttributions: \"true\",\n      });\n      // Always send a concrete `groupMaxGap`. Sending\n      // `String(undefined)` here would make the server `parseInt(\"undefined\")`\n      // to NaN, silently disabling grouping — which surfaces every same-`to`\n      // attribution as its own history entry and produces duplicate React keys.\n      params.set(\"groupMaxGap\", String(groupMaxGap));\n      if (group !== undefined) {\n        params.set(\"group\", String(group));\n      }\n      if (groupMaxDuration !== undefined) {\n        params.set(\"groupMaxDuration\", String(groupMaxDuration));\n      }\n      if (mergeUsers !== undefined) {\n        params.set(\"mergeUsers\", String(mergeUsers));\n      }\n\n      const buf = await yhubFetch(`${activityUrl}?${params}`, headers);\n      const entries = decodeAny(new Uint8Array(buf)) as YHubActivityEntry[];\n\n      const snapshots = sortSnapshotsNewestFirst(\n        entries\n          .map((entry, i) => activityToSnapshot(entry, i))\n          .filter((s): s is VersionSnapshot => s !== undefined)\n          // Prefer the mutable per-id name from the live doc's\n          // `__bn_version_names` store over the immutable `name` attribution,\n          // so renames (which only mutate that store) are reflected here.\n          .map((snapshot) => {\n            const attributionName = snapshot.name;\n            const mappedName =\n              (typeof snapshot.id === \"string\"\n                ? (getVersionNamesMap()?.getAttr(snapshot.id) as\n                    | string\n                    | undefined)\n                : undefined) ?? attributionName;\n            return { ...snapshot, name: mappedName };\n          }),\n      );\n\n      // Surface a \"current version\" entry when the live document has edits\n      // beyond the most recent saved version marker. `getCurrentVersionEntry`\n      // makes its own unmerged lookups (see there), so it is unaffected by the\n      // grouping/mergeUsers params used for the list above.\n      //\n      // This only re-evaluates when `list()` runs (sidebar open / refresh),\n      // which matches how YHub versions load today.\n      const currentEntry = await getCurrentVersionEntry();\n      return currentEntry ? [currentEntry, ...snapshots] : snapshots;\n    };\n\n    return {\n      list,\n      create,\n      getContent,\n      getAttributions,\n      restore,\n      rename,\n    };\n  };\n}\n"],"mappings":"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"}