UNPKG

@blocknote/core

Version:

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

1 lines 76.7 kB
{"version":3,"file":"yjs.cjs","names":[],"sources":["../src/yjs/utils.ts","../src/yjs/extensions/FixUpSchema.ts","../src/yjs/extensions/YCursorPlugin.ts","../src/yjs/extensions/YSync.ts","../src/yjs/extensions/YUndo.ts","../src/yjs/extensions/ForkYDoc.ts","../src/yjs/extensions/RelativePositionMapping.ts","../src/yjs/extensions/schemaMigration/migrationRules/moveColorAttributes.ts","../src/yjs/extensions/schemaMigration/migrationRules/index.ts","../src/yjs/extensions/schemaMigration/SchemaMigration.ts","../src/yjs/extensions/Versioning.ts","../src/yjs/extensions/index.ts","../src/yjs/comments/yjsHelpers.ts","../src/yjs/comments/YjsThreadStoreBase.ts","../src/yjs/comments/RESTYjsThreadStore.ts","../src/yjs/comments/YjsThreadStore.ts"],"sourcesContent":["import {\n  prosemirrorToYDoc,\n  prosemirrorToYXmlFragment,\n  yXmlFragmentToProseMirrorRootNode,\n} from \"y-prosemirror\";\nimport * as Y from \"yjs\";\n\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\";\n\n/**\n * Find a Y.AbstractType in another Y.Doc that corresponds to the same\n * logical type in the original doc.\n */\nexport function findTypeInOtherYdoc<T extends Y.AbstractType<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    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, ytype.constructor as new () => T) as T;\n  } else {\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.XmlFragment collaborative doc into a BlockNote document (BlockNote style JSON of all blocks)\n * @param editor BlockNote editor\n * @param xmlFragment Y.XmlFragment\n * @returns BlockNote document (BlockNote style JSON of all blocks)\n */\nexport function yXmlFragmentToBlocks<\n  BSchema extends BlockSchema,\n  ISchema extends InlineContentSchema,\n  SSchema extends StyleSchema,\n>(\n  editor: BlockNoteEditor<BSchema, ISchema, SSchema>,\n  xmlFragment: Y.XmlFragment,\n) {\n  const pmNode = yXmlFragmentToProseMirrorRootNode(\n    xmlFragment,\n    editor.pmSchema,\n  );\n  return docToBlocks<BSchema, ISchema, SSchema>(pmNode);\n}\n\n/**\n * Convert blocks to a Y.XmlFragment\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 xmlFragment XML fragment name\n * @returns Y.XmlFragment\n */\nexport function blocksToYXmlFragment<\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  xmlFragment?: Y.XmlFragment,\n) {\n  return prosemirrorToYXmlFragment(\n    _blocksToProsemirrorNode(editor, blocks),\n    xmlFragment,\n  );\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 xmlFragment 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  xmlFragment = \"prosemirror\",\n) {\n  return yXmlFragmentToBlocks(editor, ydoc.getXmlFragment(xmlFragment));\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 xmlFragment 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  xmlFragment = \"prosemirror\",\n) {\n  return prosemirrorToYDoc(\n    _blocksToProsemirrorNode(editor, blocks),\n    xmlFragment,\n  );\n}\n","import { Attrs, Fragment, Mark, Node, NodeType } from \"prosemirror-model\";\n\nimport { isPlainContentNodeType } from \"../../api/pmUtil.js\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\n// Collaboration-only patches to the live ProseMirror schema. Both are applied\n// once on `create`, before y-prosemirror binds and reconstructs the document.\nexport const FixUpSchemaExtension = createExtension(({ editor }) => {\n  editor.on(\"create\", () => {\n    const schema = editor.pmSchema;\n\n    // 1. Preserve the initial block id.\n    // When y-prosemirror creates an empty document, the `blockContainer` node is created with an `id` of `null`.\n    // This causes the unique id extension to generate a new id for the initial block, which is not what we want\n    // Since it will be randomly generated & cause there to be more updates to the ydoc\n    // This is a hack to make it so that anytime `schema.doc.createAndFill` is called, the initial block id is already set to \"initialBlockId\"\n    let cache: Node | undefined = undefined;\n    // eslint-disable-next-line @typescript-eslint/unbound-method -- intentionally saving reference for monkey-patching\n    const oldCreateAndFill = schema.nodes.doc.createAndFill;\n    schema.nodes.doc.createAndFill = ((...args: any) => {\n      if (cache) {\n        return cache;\n      }\n      const ret = oldCreateAndFill.apply(schema.nodes.doc, args)!;\n\n      // create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)\n      const jsonNode = JSON.parse(JSON.stringify(ret.toJSON()));\n      jsonNode.content[0].content[0].attrs.id = \"initialBlockId\";\n\n      cache = Node.fromJSON(schema, jsonNode);\n      return cache;\n    }) as unknown as typeof schema.nodes.doc.createAndFill;\n\n    // 2. Drop marks a plain block no longer allows, instead of deleting the block.\n    // When a block's content type changes across BlockNote versions — e.g. a\n    // `codeBlock` that was `\"inline\"` (formatting marks like bold allowed) and is\n    // now `\"plain\"` (only the `\"annotation\"` mark group: comments + suggestions) —\n    // old Yjs documents carry text with marks the new node type disallows.\n    //\n    // y-prosemirror rebuilds each block by calling `schema.node(nodeName, attrs,\n    // children)`, where `children` are text nodes that may still carry those\n    // disallowed marks. `createChecked` then throws, and y-prosemirror's error\n    // handler DELETES the whole block from the Yjs doc — a deletion that propagates\n    // to every peer (data loss).\n    //\n    // Rather than mutating the Yjs doc to strip those marks, we leave them in place\n    // (marks are just format attributes; they don't affect position counting, so\n    // editing text is unaffected) and simply never materialize the disallowed ones\n    // into ProseMirror. We do that by wrapping `schema.node` to filter each text\n    // child's marks through the parent node type's `allowedMarks` before the node\n    // is built. The wrapper is scoped to plain-content blocks (the only place this\n    // migration applies); all other node types pass through untouched.\n    //\n    // NOTE: this covers marks that EXIST in the schema but aren't allowed on the\n    // node. A mark whose type is entirely absent from the schema still hits\n    // y-prosemirror's earlier `schema.mark` throw (and delete) path — that's a\n    // different migration scenario and is out of scope here.\n    // eslint-disable-next-line @typescript-eslint/unbound-method -- intentionally saving reference for monkey-patching\n    const originalNode = schema.node;\n    schema.node = function (\n      this: typeof schema,\n      type: string | NodeType,\n      attrs?: Attrs | null,\n      content?: Fragment | Node | readonly Node[],\n      marks?: readonly Mark[],\n    ) {\n      const nodeType = typeof type === \"string\" ? this.nodes[type] : type;\n      // y-prosemirror always passes `content` as a plain array of nodes; only\n      // touch text children that carry marks.\n      if (\n        nodeType &&\n        isPlainContentNodeType(this, nodeType) &&\n        Array.isArray(content)\n      ) {\n        content = (content as readonly Node[]).map((child) =>\n          child?.isText && child.marks.length\n            ? child.mark(nodeType.allowedMarks(child.marks))\n            : child,\n        );\n      }\n      return originalNode.call(this, type, attrs, content, marks);\n    } as typeof schema.node;\n  });\n\n  return {\n    key: \"fixUpSchema\",\n  } as const;\n});\n","import { defaultSelectionBuilder, yCursorPlugin } from \"y-prosemirror\";\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport type { 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    }\n\n    const handleAwarenessChange = ({\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    return {\n      key: \"yCursor\",\n      mount() {\n        if (\n          awareness &&\n          options.showCursorLabels !== \"always\" &&\n          \"on\" in awareness &&\n          typeof awareness.on === \"function\"\n        ) {\n          awareness.on(\"change\", handleAwarenessChange);\n\n          return () => {\n            if (\"off\" in awareness && typeof awareness.off === \"function\") {\n              awareness.off(\"change\", handleAwarenessChange);\n            }\n          };\n        }\n\n        return undefined;\n      },\n      prosemirrorPlugins: [\n        awareness\n          ? yCursorPlugin(awareness, {\n              selectionBuilder: defaultSelectionBuilder,\n              cursorBuilder(user: CollaborationUser, clientID: number) {\n                let cursorData = recentlyUpdatedCursors.get(clientID);\n\n                if (!cursorData) {\n                  const cursorElement = (\n                    options.renderCursor ?? defaultCursorRender\n                  )(user);\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(Boolean),\n      dependsOn: [\"ySync\"],\n      updateUser(user: { name: string; color: string; [key: string]: string }) {\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 { ySyncPlugin } from \"y-prosemirror\";\nimport {\n  ExtensionOptions,\n  createExtension,\n} from \"../../editor/BlockNoteExtension.js\";\nimport type { CollaborationOptions } from \"./index.js\";\n\nexport const YSyncExtension = createExtension(\n  ({ options }: ExtensionOptions<Pick<CollaborationOptions, \"fragment\">>) => {\n    return {\n      key: \"ySync\",\n      prosemirrorPlugins: [ySyncPlugin(options.fragment)],\n      runsBefore: [\"default\"],\n    } as const;\n  },\n);\n","import { redoCommand, undoCommand, yUndoPlugin } from \"y-prosemirror\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\n\nexport const YUndoExtension = createExtension(() => {\n  return {\n    key: \"yUndo\",\n    prosemirrorPlugins: [yUndoPlugin()],\n    dependsOn: [\"yCursor\", \"ySync\"],\n    undoCommand: undoCommand,\n    redoCommand: redoCommand,\n  } as const;\n});\n","import { ySyncPluginKey, yUndoPluginKey } from \"y-prosemirror\";\nimport * as Y from \"yjs\";\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport {\n  createExtension,\n  createStore,\n  ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport type { CollaborationOptions } from \"./index.js\";\nimport { YCursorExtension } from \"./YCursorPlugin.js\";\nimport { YSyncExtension } from \"./YSync.js\";\nimport { YUndoExtension } from \"./YUndo.js\";\nimport { findTypeInOtherYdoc } from \"../utils.js\";\n\n/**\n * Point the `ySync` plugin state at `fragment`.\n *\n * Swapping the `ySync` plugin reconfigures the ProseMirror state, and\n * ProseMirror carries over the state of plugins that share a key instead of\n * re-initializing them. So the new plugin's `binding` (which is set from its\n * view, via a transaction) ends up on the new fragment, while `type` and `doc`\n * still point at the fragment the editor was bound to before. Anything reading\n * those (e.g. `RelativePositionMappingExtension`) would then mix up the two\n * Y.Docs, so we set them explicitly here.\n */\nfunction bindYSyncPluginStateTo(\n  editor: BlockNoteEditor<any, any, any>,\n  fragment: Y.XmlFragment,\n) {\n  editor.transact((tr) =>\n    tr.setMeta(ySyncPluginKey, { type: fragment, doc: fragment.doc }),\n  );\n}\n\nexport const ForkYDocExtension = createExtension(\n  ({ editor, options }: ExtensionOptions<CollaborationOptions>) => {\n    let forkedState:\n      | {\n          originalFragment: Y.XmlFragment;\n          undoStack: Y.UndoManager[\"undoStack\"];\n          forkedFragment: Y.XmlFragment;\n        }\n      | undefined = undefined;\n\n    const store = createStore({ isForked: false });\n\n    return {\n      key: \"yForkDoc\",\n      store,\n      /**\n       * Fork the Y.js document from syncing to the remote,\n       * allowing modifications to the document without affecting the remote.\n       * These changes can later be rolled back or applied to the remote.\n       */\n      fork({\n        /**\n         * The initial update to apply to the forked document.\n         * If not provided, the current document state is used.\n         */\n        initialUpdate,\n      }: {\n        initialUpdate?: Uint8Array;\n      } = {}) {\n        if (forkedState) {\n          return;\n        }\n\n        const originalFragment = options.fragment;\n\n        if (!originalFragment) {\n          throw new Error(\"No fragment to fork from\");\n        }\n\n        const doc = new Y.Doc();\n        // Copy the original document (or apply the provided update) to a new Yjs document\n        Y.applyUpdate(\n          doc,\n          initialUpdate ?? Y.encodeStateAsUpdate(originalFragment.doc!),\n        );\n\n        // Find the forked fragment in the new Yjs document\n        const forkedFragment = findTypeInOtherYdoc(originalFragment, doc);\n\n        forkedState = {\n          undoStack: yUndoPluginKey.getState(editor.prosemirrorState)!\n            .undoManager.undoStack,\n          originalFragment,\n          forkedFragment,\n        };\n\n        const newOptions = {\n          ...options,\n          fragment: forkedFragment,\n        };\n\n        // Atomically swap the yjs plugins to avoid re-entrant dispatch issues\n        // where y-prosemirror's view hooks can dispatch a transaction between\n        // separate unregister/register calls, re-introducing stale plugins.\n        editor.replaceExtension(\n          [\"ySync\", \"yCursor\", \"yUndo\"],\n          [\n            YSyncExtension(newOptions),\n            // No need to register the cursor plugin again, it's a local fork\n            YUndoExtension(),\n          ],\n        );\n\n        bindYSyncPluginStateTo(editor, forkedFragment);\n\n        // Tell the store that the editor is now forked\n        store.setState({ isForked: true });\n      },\n\n      /**\n       * Resume syncing the Y.js document to the remote\n       * If `keepChanges` is true, any changes that have been made to the forked document will be applied to the original document.\n       * Otherwise, the original document will be restored and the changes will be discarded.\n       */\n      merge({ keepChanges }: { keepChanges: boolean }) {\n        if (!forkedState) {\n          return;\n        }\n\n        const { originalFragment, forkedFragment, undoStack } = forkedState;\n\n        // Atomically swap the forked plugins back to the original ones\n        editor.replaceExtension(\n          [\"ySync\", \"yCursor\", \"yUndo\"],\n          [\n            YSyncExtension(options),\n            YCursorExtension(options),\n            YUndoExtension(),\n          ],\n        );\n\n        bindYSyncPluginStateTo(editor, originalFragment);\n\n        // Reset the undo stack to the original undo stack\n        yUndoPluginKey.getState(\n          editor.prosemirrorState,\n        )!.undoManager.undoStack = undoStack;\n\n        if (keepChanges) {\n          // Apply any changes that have been made to the fork, onto the original doc\n          const update = Y.encodeStateAsUpdate(\n            forkedFragment.doc!,\n            Y.encodeStateVector(originalFragment.doc!),\n          );\n          // Applying this change will add to the undo stack, allowing it to be undone normally\n          Y.applyUpdate(originalFragment.doc!, update, editor);\n        }\n        // Reset the forked state\n        forkedState = undefined;\n        // Tell the store that the editor is no longer forked\n        store.setState({ isForked: false });\n      },\n    } as const;\n  },\n);\n","import {\n  absolutePositionToRelativePosition,\n  relativePositionToAbsolutePosition,\n  ySyncPluginKey,\n} from \"y-prosemirror\";\nimport { createExtension } from \"../../editor/BlockNoteExtension.js\";\nimport type { PositionMappingExtension } from \"../../extensions/index.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) {\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        // If the document is empty, it has not been synced yet\n        if (ySyncPluginState.binding.type.length === 0) {\n          // so, we just fallback to the prosemirror position mapping extension\n          // If a remote transaction or sync happens in this case. The position map will be invalidated,\n          // and the positions will be moved to the end of the document\n          // This is acceptable, because the document had not been synced so there are no positions to map properly into\n          const fallback =\n            editor.getExtension<typeof PositionMappingExtension>(\n              \"positionMapping\",\n            );\n          if (!fallback) {\n            throw new Error(\n              \"positionMapping extension is not available; cannot map position before sync\",\n            );\n          }\n          return fallback.mapPosition(position, side);\n        }\n\n        const relativePosition = absolutePositionToRelativePosition(\n          position + (side === \"right\" ? 1 : -1),\n          ySyncPluginState.binding.type,\n          ySyncPluginState.binding.mapping,\n        );\n\n        return () => {\n          const curYSyncPluginState = ySyncPluginKey.getState(\n            editor.prosemirrorState,\n          ) as typeof ySyncPluginState;\n          // Resolve against the doc that owns the currently bound type, and not\n          // against `curYSyncPluginState.doc`. Those can point at different\n          // Y.Docs (e.g. right after forking the doc, see `ForkYDocExtension`),\n          // in which case the resolved type wouldn't be part of the bound\n          // fragment and the position would be reported as \"not found\".\n          const boundType = curYSyncPluginState.binding.type;\n          const pos = relativePositionToAbsolutePosition(\n            boundType.doc,\n            boundType,\n            relativePosition,\n            curYSyncPluginState.binding.mapping,\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 * as Y from \"yjs\";\n\nimport { MigrationRule } from \"./migrationRule.js\";\nimport { defaultProps } from \"../../../../blocks/defaultProps.js\";\n\n// Helper function to recursively traverse a `Y.XMLElement` and its descendant\n// elements.\nconst traverseElement = (\n  rootElement: Y.XmlElement,\n  cb: (element: Y.XmlElement) => void,\n) => {\n  cb(rootElement);\n  rootElement.forEach((element) => {\n    if (element instanceof Y.XmlElement) {\n      traverseElement(element, cb);\n    }\n  });\n};\n\n// Moves `textColor` and `backgroundColor` attributes from `blockContainer`\n// nodes to their child `blockContent` nodes. This is due to a schema change\n// introduced in PR #TODO.\nexport const moveColorAttributes: MigrationRule = (fragment, tr) => {\n  // Stores necessary info for all `blockContainer` nodes which still have\n  // `textColor` or `backgroundColor` attributes that need to be moved.\n  const targetBlockContainers: Map<\n    string,\n    {\n      textColor: string | undefined;\n      backgroundColor: string | undefined;\n    }\n  > = new Map();\n  // Finds all elements which still have `textColor` or `backgroundColor`\n  // attributes in the current Yjs fragment.\n  fragment.forEach((element) => {\n    if (element instanceof Y.XmlElement) {\n      traverseElement(element, (element) => {\n        if (\n          element.nodeName === \"blockContainer\" &&\n          element.hasAttribute(\"id\")\n        ) {\n          const textColor = element.getAttribute(\"textColor\");\n          const backgroundColor = element.getAttribute(\"backgroundColor\");\n\n          const colors = {\n            textColor:\n              textColor === defaultProps.textColor.default\n                ? undefined\n                : textColor,\n            backgroundColor:\n              backgroundColor === defaultProps.backgroundColor.default\n                ? undefined\n                : backgroundColor,\n          };\n\n          if (colors.textColor || colors.backgroundColor) {\n            targetBlockContainers.set(element.getAttribute(\"id\")!, colors);\n          }\n        }\n      });\n    }\n  });\n\n  if (targetBlockContainers.size === 0) {\n    return false;\n  }\n\n  // Appends transactions to add the `textColor` and `backgroundColor`\n  // attributes found on each `blockContainer` node to move them to the child\n  // `blockContent` node.\n  tr.doc.descendants((node, pos) => {\n    if (\n      node.type.name === \"blockContainer\" &&\n      targetBlockContainers.has(node.attrs.id)\n    ) {\n      const el = tr.doc.nodeAt(pos + 1);\n      if (!el) {\n        throw new Error(\"No element found\");\n      }\n\n      tr.setNodeMarkup(pos + 1, undefined, {\n        // preserve existing attributes\n        ...el.attrs,\n        // add the textColor and backgroundColor attributes\n        ...targetBlockContainers.get(node.attrs.id),\n      });\n    }\n  });\n\n  return true;\n};\n","import { MigrationRule } from \"./migrationRule.js\";\nimport { moveColorAttributes } from \"./moveColorAttributes.js\";\n\nexport default [moveColorAttributes] as MigrationRule[];\n","import { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport * as Y from \"yjs\";\n\nimport {\n  createExtension,\n  ExtensionOptions,\n} from \"../../../editor/BlockNoteExtension.js\";\nimport migrationRules from \"./migrationRules/index.js\";\n\n// This plugin allows us to update collaboration YDocs whenever BlockNote's\n// underlying ProseMirror schema changes. The plugin reads the current Yjs\n// fragment and dispatches additional transactions to the ProseMirror state, in\n// case things are found in the fragment that don't adhere to the editor schema\n// and need to be fixed. These fixes are defined as `MigrationRule`s within the\n// `migrationRules` directory.\nexport const SchemaMigration = createExtension(\n  ({ options }: ExtensionOptions<{ fragment: Y.XmlFragment }>) => {\n    let migrationDone = false;\n    const pluginKey = new PluginKey(\"schemaMigration\");\n\n    return {\n      key: \"schemaMigration\",\n      prosemirrorPlugins: [\n        new Plugin({\n          key: pluginKey,\n          appendTransaction: (transactions, _oldState, newState) => {\n            if (migrationDone) {\n              return undefined;\n            }\n\n            if (\n              // If any of the transactions are not due to a yjs sync, we don't need to run the migration\n              !transactions.some((tr) => tr.getMeta(\"y-sync$\")) ||\n              // If none of the transactions result in a document change, we don't need to run the migration\n              transactions.every((tr) => !tr.docChanged) ||\n              // If the fragment is still empty, we can't run the migration (since it has not yet been applied to the Y.Doc)\n              !options.fragment.firstChild\n            ) {\n              return undefined;\n            }\n\n            const tr = newState.tr;\n            for (const migrationRule of migrationRules) {\n              migrationRule(options.fragment, tr);\n            }\n\n            migrationDone = true;\n\n            if (!tr.docChanged) {\n              return undefined;\n            }\n\n            return tr;\n          },\n        }),\n      ],\n    } as const;\n  },\n);\n","import * as Y from \"yjs\";\n\nimport type { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\nimport type { PreviewController } from \"../../extensions/Versioning/index.js\";\nimport type { CollaborationOptions } from \"./index.js\";\nimport { ForkYDocExtension } from \"./ForkYDoc.js\";\n\n/**\n * Creates a Yjs v13 adapter that provides the {@link PreviewController}\n * and `getCurrentDocument` callback required by the base\n * {@link VersioningExtension}.\n *\n * Delegates to the {@link ForkYDocExtension} for entering/exiting preview:\n * - **enterPreview**: calls `fork({ initialUpdate: snapshotContent })` to\n *   switch the editor to a temporary doc built from the snapshot.\n * - **exitPreview**: calls `merge({ keepChanges: false })` to discard the\n *   preview and restore the live document.\n * - **applyRestore**: calls `merge({ keepChanges: true })` to apply the\n *   snapshot content back to the live document.\n */\nexport function createYjsVersioningAdapter(\n  /** The BlockNote editor instance (must have ForkYDocExtension). */\n  editor: BlockNoteEditor<any, any, any>,\n  /** The full collaboration options (used for `fragment` access). */\n  options: CollaborationOptions,\n): {\n  preview: PreviewController<Uint8Array>;\n  getCurrentDocument: () => Y.XmlFragment;\n  serializeCurrentContent: () => Uint8Array;\n} {\n  const { fragment } = options;\n\n  function getForkYDoc() {\n    const ext = editor.getExtension(ForkYDocExtension);\n    if (!ext) {\n      throw new Error(\n        \"ForkYDocExtension is required for the Yjs versioning adapter. \" +\n          \"Make sure it is registered before the VersioningExtension.\",\n      );\n    }\n    return ext;\n  }\n\n  return {\n    getCurrentDocument: () => fragment,\n    serializeCurrentContent: () => Y.encodeStateAsUpdateV2(fragment.doc!),\n    preview: {\n      // Yjs v13 can only fork the document to a single snapshot; it has no way\n      // to diff two versions, so comparison is unsupported.\n      supportsComparison: false,\n      enterPreview(\n        snapshotContent: Uint8Array,\n        _compareToContent?: Uint8Array,\n      ) {\n        const forkYDoc = getForkYDoc();\n\n        // If already in a preview (forked state), exit first.\n        if (forkYDoc.store.state.isForked) {\n          forkYDoc.merge({ keepChanges: false });\n        }\n\n        forkYDoc.fork({ initialUpdate: snapshotContent });\n      },\n\n      exitPreview() {\n        const forkYDoc = getForkYDoc();\n        if (forkYDoc.store.state.isForked) {\n          forkYDoc.merge({ keepChanges: false });\n        }\n      },\n\n      applyRestore(_snapshotContent: Uint8Array) {\n        // Restoring to an older Yjs state cannot be done by merging a fork\n        // because the original doc already contains all CRDT state vectors\n        // from the snapshot. Restore must be handled at the endpoint/server\n        // level (e.g., the server creates a new Y.Doc and syncs it).\n        throw new Error(\n          \"Restore is not yet implemented for Yjs v13 versioning adapter.\",\n        );\n      },\n    },\n  };\n}\n","import type { Awareness } from \"y-protocols/awareness\";\nimport type * as Y from \"yjs\";\nimport type { BlockNoteEditorOptions } from \"../../editor/BlockNoteEditor\";\nimport {\n  createExtension,\n  type ExtensionOptions,\n} from \"../../editor/BlockNoteExtension.js\";\nimport { FixUpSchemaExtension } from \"./FixUpSchema.js\";\nimport { ForkYDocExtension } from \"./ForkYDoc.js\";\nimport { RelativePositionMappingExtension } from \"./RelativePositionMapping.js\";\nimport { SchemaMigration } from \"./schemaMigration/SchemaMigration.js\";\nimport { CollaborationUser, YCursorExtension } from \"./YCursorPlugin.js\";\nimport { YSyncExtension } from \"./YSync.js\";\nimport { YUndoExtension } from \"./YUndo.js\";\n\nexport type CollaborationOptions = {\n  /**\n   * The Yjs XML fragment that's used for collaboration.\n   */\n  fragment: Y.XmlFragment;\n  /**\n   * The user info for the current user that's shown to other collaborators.\n   */\n  user: CollaborationUser;\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\nexport const CollaborationExtension = createExtension(\n  ({ options }: ExtensionOptions<CollaborationOptions>) => {\n    return {\n      key: \"collaboration\",\n      blockNoteExtensions: [\n        FixUpSchemaExtension(),\n        ForkYDocExtension(options),\n        RelativePositionMappingExtension(),\n        SchemaMigration(options),\n        YCursorExtension(options),\n        YSyncExtension(options),\n        YUndoExtension(),\n      ],\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  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 \"./ForkYDoc.js\";\nexport * from \"./RelativePositionMapping.js\";\nexport * from \"./schemaMigration/SchemaMigration.js\";\nexport * from \"./Versioning.js\";\nexport * from \"./YCursorPlugin.js\";\nexport * from \"./YSync.js\";\nexport * from \"./YUndo.js\";\n","import * as Y from \"yjs\";\nimport type {\n  CommentData,\n  CommentReactionData,\n  ThreadData,\n} from \"../../comments/types.js\";\n\nexport function commentToYMap(comment: CommentData) {\n  const yMap = new Y.Map<any>();\n  yMap.set(\"id\", comment.id);\n  yMap.set(\"userId\", comment.userId);\n  yMap.set(\"createdAt\", comment.createdAt.getTime());\n  yMap.set(\"updatedAt\", comment.updatedAt.getTime());\n  if (comment.deletedAt) {\n    yMap.set(\"deletedAt\", comment.deletedAt.getTime());\n    yMap.set(\"body\", undefined);\n  } else {\n    yMap.set(\"body\", comment.body);\n  }\n  if (comment.reactions.length > 0) {\n    throw new Error(\"Reactions should be empty in commentToYMap\");\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 yMapToReactions).\n   */\n  yMap.set(\"reactionsByUser\", new Y.Map());\n  yMap.set(\"metadata\", comment.metadata);\n\n  return yMap;\n}\n\nexport function threadToYMap(thread: ThreadData) {\n  const yMap = new Y.Map();\n  yMap.set(\"id\", thread.id);\n  yMap.set(\"createdAt\", thread.createdAt.getTime());\n  yMap.set(\"updatedAt\", thread.updatedAt.getTime());\n  const commentsArray = new Y.Array<Y.Map<any>>();\n\n  commentsArray.push(thread.comments.map((comment) => commentToYMap(comment)));\n\n  yMap.set(\"comments\", commentsArray);\n  yMap.set(\"resolved\", thread.resolved);\n  yMap.set(\"resolvedUpdatedAt\", thread.resolvedUpdatedAt?.getTime());\n  yMap.set(\"resolvedBy\", thread.resolvedBy);\n  yMap.set(\"metadata\", thread.metadata);\n  return yMap;\n}\n\ntype SingleUserCommentReactionData = {\n  emoji: string;\n  createdAt: Date;\n  userId: string;\n};\n\nexport function yMapToReaction(\n  yMap: Y.Map<any>,\n): SingleUserCommentReactionData {\n  return {\n    emoji: yMap.get(\"emoji\"),\n    createdAt: new Date(yMap.get(\"createdAt\")),\n    userId: yMap.get(\"userId\"),\n  };\n}\n\nfunction yMapToReactions(yMap: Y.Map<any>): CommentReactionData[] {\n  const flatReactions = [...yMap.values()].map((reaction: Y.Map<any>) =>\n    yMapToReaction(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 yMapToComment(yMap: Y.Map<any>): CommentData {\n  return {\n    type: \"comment\",\n    id: yMap.get(\"id\"),\n    userId: yMap.get(\"userId\"),\n    createdAt: new Date(yMap.get(\"createdAt\")),\n    updatedAt: new Date(yMap.get(\"updatedAt\")),\n    deletedAt: yMap.get(\"deletedAt\")\n      ? new Date(yMap.get(\"deletedAt\"))\n      : undefined,\n    reactions: yMapToReactions(yMap.get(\"reactionsByUser\")),\n    metadata: yMap.get(\"metadata\"),\n    body: yMap.get(\"body\"),\n  };\n}\n\nexport function yMapToThread(yMap: Y.Map<any>): ThreadData {\n  return {\n    type: \"thread\",\n    id: yMap.get(\"id\"),\n    createdAt: new Date(yMap.get(\"createdAt\")),\n    updatedAt: new Date(yMap.get(\"updatedAt\")),\n    comments: ((yMap.get(\"comments\") as Y.Array<Y.Map<any>>) || []).map(\n      (comment) => yMapToComment(comment),\n    ),\n    resolved: yMap.get(\"resolved\"),\n    resolvedUpdatedAt: new Date(yMap.get(\"resolvedUpdatedAt\")),\n    resolvedBy: yMap.get(\"resolvedBy\"),\n    metadata: yMap.get(\"metadata\"),\n  };\n}\n","import * as Y from \"yjs\";\nimport type { ThreadData } from \"../../comments/types.js\";\nimport { ThreadStore } from \"../../comments/threadstore/ThreadStore.js\";\nimport type { ThreadStoreAuth } from \"../../comments/threadstore/ThreadStoreAuth.js\";\nimport { yMapToThread } 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 Yjs Map.\n */\nexport abstract class YjsThreadStoreBase extends ThreadStore {\n  constructor(\n    protected readonly threadsYMap: Y.Map<any>,\n    auth: ThreadStoreAuth,\n  ) {\n    super(auth);\n  }\n\n  // TODO: async / reactive interface?\n  public getThread(threadId: string) {\n    const yThread = this.threadsYMap.get(threadId);\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n    const thread = yMapToThread(yThread);\n    return thread;\n  }\n\n  public getThreads(): Map<string, ThreadData> {\n    const threadMap = new Map<string, ThreadData>();\n    this.threadsYMap.forEach((yThread, id) => {\n      if (yThread instanceof Y.Map) {\n        threadMap.set(id, yMapToThread(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.threadsYMap.observeDeep(observer);\n\n    return () => {\n      this.threadsYMap.unobserveDeep(observer);\n    };\n  }\n}\n","import * as Y from \"yjs\";\nimport {\n  absolutePositionToRelativePosition,\n  ySyncPluginKey,\n} from \"y-prosemirror\";\nimport type { CommentBody } from \"../../comments/types.js\";\nimport type { ThreadStoreAuth } from \"../../comments/threadstore/ThreadStoreAuth.js\";\nimport { YjsThreadStoreBase } from \"./YjsThreadStoreBase.js\";\nimport { BlockNoteEditor } from \"../../editor/BlockNoteEditor.js\";\n\n/**\n * This is a REST-based implementation of the YjsThreadStoreBase.\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    threadsYMap: Y.Map<any>,\n    auth: ThreadStoreAuth,\n  ) {\n    super(threadsYMap, 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    editor: BlockNoteEditor<any, any, any>;\n  }) => {\n    const { threadId, selection } = options;\n\n    // Note: the positions have to be resolved against the *binding's* type and\n    // mapping. The plugin state has a `type` of its own, but no `mapping`, and\n    // its `type` can go stale (e.g. while the doc is forked, see\n    // `ForkYDocExtension`).\n    const binding = ySyncPluginKey.getState(\n      options.editor.prosemirrorState,\n    )?.binding;\n    const yjsSelection = binding\n      ? {\n          head: absolutePositionToRelativePosition(\n            selection.head,\n            binding.type,\n            binding.mapping,\n          ),\n          anchor: absolutePositionToRelativePosition(\n            selection.anchor,\n            binding.type,\n            binding.mapping,\n          ),\n        }\n      : undefined;\n\n    return this.doRequest(`/${threadId}/addToDocument`, \"POST\", {\n      selection: {\n        prosemirror: selection,\n        yjs: yjsSelection,\n      },\n    });\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 * as Y from \"yjs\";\nimport { uuidv4 } from \"lib0/random\";\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  commentToYMap,\n  threadToYMap,\n  yMapToComment,\n  yMapToThread,\n} from \"./yjsHelpers.js\";\n\n/**\n * This is a Yjs-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    threadsYMap: Y.Map<any>,\n    auth: ThreadStoreAuth,\n  ) {\n    super(threadsYMap, 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.threadsYMap.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.threadsYMap.set(thread.id, threadToYMap(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.threadsYMap.get(options.threadId);\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      if (!this.auth.canAddComment(yMapToThread(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.get(\"comments\") as Y.Array<Y.Map<any>>).push([\n        commentToYMap(comment),\n      ]);\n\n      yThread.set(\"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.threadsYMap.get(options.threadId);\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const yCommentIndex = yArrayFindIndex(\n        yThread.get(\"comments\"),\n        (comment) => comment.get(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = yThread.get(\"comments\").get(yCommentIndex);\n\n      if (!this.auth.canUpdateComment(yMapToComment(yComment))) {\n        throw new Error(\"Not authorized\");\n      }\n\n      yComment.set(\"body\", options.comment.body);\n      yComment.set(\"updatedAt\", new Date().getTime());\n      yComment.set(\"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.threadsYMap.get(options.threadId);\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const yCommentIndex = yArrayFindIndex(\n        yThread.get(\"comments\"),\n        (comment) => comment.get(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = yThread.get(\"comments\").get(yCommentIndex);\n\n      if (!this.auth.canDeleteComment(yMapToComment(yComment))) {\n        throw new Error(\"Not authorized\");\n      }\n\n      if (yComment.get(\"deletedAt\")) {\n        throw new Error(\"Comment already deleted\");\n      }\n\n      if (options.softDelete) {\n        yComment.set(\"deletedAt\", new Date().getTime());\n        yComment.set(\"body\", undefined);\n      } else {\n        yThread.get(\"comments\").delete(yCommentIndex);\n      }\n\n      if (\n        (yThread.get(\"comments\") as Y.Array<any>)\n          .toArray()\n          .every((comment) => comment.get(\"deletedAt\"))\n      ) {\n        // all comments deleted\n        if (options.softDelete) {\n          yThread.set(\"deletedAt\", new Date().getTime());\n        } else {\n          this.threadsYMap.delete(options.threadId);\n        }\n      }\n\n      yThread.set(\"updatedAt\", new Date().getTime());\n    },\n  );\n\n  public deleteThread = this.transact((options: { threadId: string }) => {\n    if (\n      !this.auth.canDeleteThread(\n        yMapToThread(this.threadsYMap.get(options.threadId)),\n      )\n    ) {\n      throw new Error(\"Not authorized\");\n    }\n\n    this.threadsYMap.delete(options.threadId);\n  });\n\n  public resolveThread = this.transact((options: { threadId: string }) => {\n    const yThread = this.threadsYMap.get(options.threadId);\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n\n    if (!this.auth.canResolveThread(yMapToThread(yThread))) {\n      throw new Error(\"Not authorized\");\n    }\n\n    yThread.set(\"resolved\", true);\n    yThread.set(\"resolvedUpdatedAt\", new Date().getTime());\n    yThread.set(\"resolvedBy\", this.userId);\n  });\n\n  public unresolveThread = this.transact((options: { threadId: string }) => {\n    const yThread = this.threadsYMap.get(options.threadId);\n    if (!yThread) {\n      throw new Error(\"Thread not found\");\n    }\n\n    if (!this.auth.canUnresolveThread(yMapToThread(yThread))) {\n      throw new Error(\"Not authorized\");\n    }\n\n    yThread.set(\"resolved\", false);\n    yThread.set(\"resolvedUpdatedAt\", new Date().getTime());\n  });\n\n  public addReaction = this.transact(\n    (options: { threadId: string; commentId: string; emoji: string }) => {\n      const yThread = this.threadsYMap.get(options.threadId);\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const yCommentIndex = yArrayFindIndex(\n        yThread.get(\"comments\"),\n        (comment) => comment.get(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = yThread.get(\"comments\").get(yCommentIndex);\n\n      if (!this.auth.canAddReaction(yMapToComment(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.get(\"reactionsByUser\");\n\n      if (reactionsByUser.has(key)) {\n        // already exists\n        return;\n      } else {\n        const reaction = new Y.Map();\n        reaction.set(\"emoji\", options.emoji);\n        reaction.set(\"createdAt\", date.getTime());\n        reaction.set(\"userId\", this.userId);\n        reactionsByUser.set(key, reaction);\n      }\n    },\n  );\n\n  public deleteReaction = this.transact(\n    (options: { threadId: string; commentId: string; emoji: string }) => {\n      const yThread = this.threadsYMap.get(options.threadId);\n      if (!yThread) {\n        throw new Error(\"Thread not found\");\n      }\n\n      const yCommentIndex = yArrayFindIndex(\n        yThread.get(\"comments\"),\n        (comment) => comment.get(\"id\") === options.commentId,\n      );\n\n      if (yCommentIndex === -1) {\n        throw new Error(\"Comment not found\");\n      }\n\n      const yComment = yThread.get(\"comments\").get(yCommentIndex);\n\n      if (\n        !this.auth.canDeleteReaction(yMapToComment(yComment), options.emoji)\n      ) {\n        throw new Error(\"Not authorized\");\n      }\n\n      const key = `${this.userId}-${options.emoji}`;\n\n      const reactionsByUser = yComment.get(\"reactionsByUser\");\n\n      reactionsByUser.delete(key);\n    },\n  );\n}\n\nfunction yArrayFindIndex(\n  yArray: Y.Array<any>,\n  predicate: (item: any) => boolean,\n) {\n  for (let i = 0; i < yArray.length; i++) {\n    if (predicate(yArray.get(i))) {\n      return i;\n    }\n  }\n  return -1;\n}\n"],"mappings":"qVAsBA,SAAgB,EACd,EACA,EACG,CACH,IAAM,EAAO,EAAM,IACnB,GAAI,CAAC,EACH,MAAU,MAAM,2BAA2B,EAE7C,GAAI,EAAM,QAAU,KAAM,CACxB,IAAM,EAAU,MAAM,KAAK,EAAK,MAAM,KAAK,CAAC,CAAC,CAAC,KAC3C,GAAQ,EAAK,MAAM,IAAI,CAAG,IAAM,CACnC,EACA,GAAI,GAAW,KACb,MAAU,MAAM,mCAAmC,EAErD,OAAO,EAAU,IAAI,EAAS,EAAM,WAA0B,CAChE,CAAO,CACL,IAAM,EAAY,EAAM,MAClB,EAAe,EAAU,MAAM,QAAQ,IAAI,EAAU,GAAG,MAAM,GAAK,CAAC,EAEpE,EAAY,EADA,EAAE,YAAY,EAAc,EAAU,GAAG,KAC5B,GAC/B,GAAI,CAAC,EACH,MAAU,MAAM,mCAAmC,EAErD,IAAM,EAAe,EAAU,QAC/B,GAAI,CAAC,EACH,MAAU,MAAM,mCAAmC,EAErD,OAAO,EAAa,IACtB,CACF,CAQA,SAAgB,EAId,EAAoD,EAAW,CAG/D,IAAM,EAAM,EAAO,SAAS,aAAa,CAAI,EAC7C,OAAO,EAAA,GAAuC,CAAG,CACnD,CAQA,SAAgB,EAKd,EACA,EACA,CACA,IAAM,EAAU,EAAO,IAAK,GAAM,EAAA,GAAY,EAAG,EAAO,QAAQ,CAAC,EAMjE,OAJY,EAAO,SAAS,YAAY,OACtC,KACA,EAAO,SAAS,MAAM,WAAc,OAAO,KAAM,CAAO,CAEnD,CACT,CAUA,SAAgB,EAKd,EACA,EACA,CACA,IAAM,GAAA,EAAS,EAAA,kCAAA,CACb,EACA,EAAO,QACT,EACA,OAAO,EAAA,GAAuC,CAAM,CACtD,CAcA,SAAgB,EAKd,EACA,EACA,EACA,CACA,OAAA,EAAO,EAAA,0BAAA,CACL,EAAyB,EAAQ,CAAM,EACvC,CACF,CACF,CASA,SAAgB,EAKd,EACA,EACA,EAAc,cACd,CACA,OAAO,EAAqB,EAAQ,EAAK,eAAe,CAAW,CAAC,CACtE,CAWA,SAAgB,EAKd,EACA,EACA,EAAc,cACd,CACA,OAAA,EAAO,EAAA,kBAAA,CACL,EAAyB,EAAQ,CAAM,EACvC,CACF,CACF,CClLA,IAAa,EAAuB,EAAA,GAAiB,CAAE,aACrD,EAAO,GAAG,aAAgB,CACxB,IAAM,EAAS,EAAO,SAOlB,EAEE,EAAmB,EAAO,MAAM,IAAI,cAC1C,EAAO,MAAM,IAAI,gBAAkB,GAAG,IAAc,CAClD,GAAI,EACF,OAAO,EAET,IAAM,EAAM,EAAiB,MAAM,EAAO,MAAM,IAAK,CAAI,EAGnD,EAAW,KAAK,MAAM,KAAK,UAAU,EAAI,OAAO,CAAC,CAAC,EAIxD,MAHA,GAAS,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,GAAK,iBAE1C,EAAQ,EAAA,KAAK,SAAS,EAAQ,CAAQ,EAC/B,CACT,GA2BA,IAAM,EAAe,EAAO,KAC5B,EAAO,KAAO,SAEZ,EACA,EACA,EACA,EACA,CACA,IAAM,EAAW,OAAO,GAAS,SAAW,KAAK,MAAM,GAAQ,EAc/D,OAVE,GACA,EAAA,GAAuB,KAAM,CAAQ,GACrC,MAAM,QAAQ,CAAO,IAErB,EAAW,EAA4B,IAAK,GAC1C,GAAO,QAAU,EAAM,MAAM,OACzB,EAAM,KAAK,EAAS,aAAa,EAAM,KAAK,CAAC,EAC7C,CACN,GAEK,EAAa,KAAK,KAAM,EAAM,EAAO,EAAS,CAAK,CAC5D,CACF,CAAC,EAEM,CACL,IAAK,aACP,EACD,ECrED,SAAS,EAAY,EAA0B,CAC7C,IAAM,EAAQ,EAAQ,OAAO,CAAC,IAAM,IAAM,EAAQ,UAAU,EAAG,CAAC,EAAI,EAC9D,EAAI,SAAS,EAAM,UAAU,EAAG,CAAC,EAAG,EAAE,EACtC,EAAI,SAAS,EAAM,UAAU,EAAG,CAAC,EAAG,EAAE,EACtC,EAAI,SAAS,EAAM,UAAU,EAAG,CAAC,EAAG,EAAE,EAEtC,EAAI,CADQ,EAAI,IAAK,EAAI,IAAK,EAAI,GAC9B,CAAA,CAAS,IAAK,GAClB,GAAO,OACF,EAAM,QAEE,EAAM,MAAS,QAAO,GACxC,EAED,MADU,OAAS,EAAE,GAAK,MAAS,EAAE,GAAK,MAAS,EAAE,IACzC,IACd,CAEA,SAAS,EAAoB,EAAyB,CACpD,IAAM,EAAgB,SAAS,cAAc,MAAM,EAEnD,EAAc,UAAU,IAAI,+BAA+B,EAE3D,IAAM,EAAe,SAAS,cAAc,MAAM,EAClD,EAAa,aAAa,oBAAqB,OAAO,EACtD,EAAa,UAAU,IAAI,gCAAgC,EAC3D,EAAa,aACX,QACA,qBAAqB,EAAK,MAAM,WAC9B,EAAY,EAAK,KAAK,EAAI,QAAU,SAExC,EAEA,IAAM,EAAe,SAAS,cAAc,MAAM,EAiBlD,OAfA,EAAa,UAAU,IAAI,gCAAgC,EAC3D,EAAa,aACX,QACA,qBAAqB,EAAK,MAAM,WAC9B,EAAY,EAAK,KAAK,EAAI,QAAU,SAExC,EACA,EAAa,aAAa,SAAS,eAAe,EAAK,IAAI,EAAG,IAAI,EAElE,EAAa,aAAa,EAAc,IAAI,EAE5C,EAAc,aAAa,SAAS,eAAe,GAAQ,EAAG,IAAI,EAClE,EAAc,aAAa,EAAc,IAAI,EAC7C,EAAc,aAAa,SAAS,eAAe,GAAQ,EAAG,IAAI,EAE3D,CACT,CAEA,IAAa,EAAmB,EAAA,GAC7B,CAAE,aAAsD,CACvD,IAAM,EAAyB,IAAI,IAC7B,EACJ,EAAQ,UACR,cAAe,EAAQ,UACvB,OAAO,EAAQ,SAAS,WAAc,SAClC,EAAQ,SAAS,UACjB,IAAA,GACF,GAEA,uBAAwB,GACxB,OAAO,EAAU,oBAAuB,YAExC,EAAU,mBAAmB,OAAQ,EAAQ,IAAI,EAIrD,IAAM,GAAyB,CAC7B,aAKI,CACJ,IAAK,IAAM,KAAY,EAAS,CAC9B,IAAM,EAAS,EAAuB,IAAI,CAAQ,EAE9C,IACF,eAAiB,CACf,EAAO,QAAQ,aAAa,cAAe,EAAE,CAC/C,EAAG,EAAE,EAED,EAAO,aACT,aAAa,EAAO,WAAW,EAGjC,EAAuB,IAAI,EAAU,CACnC,QAAS,EAAO,QAChB,YAAa,eAAiB,CAC5B,EAAO,QAAQ,gBAAgB,aAAa,CAC9C,EAAG,GAAI,CACT,CAAC,EAEL,CACF,EAEA,MAAO,CACL,IAAK,UACL,OAAQ,CACN,GACE,GACA,EAAQ,mBAAqB,UAC7B,OAAQ,GACR,OAAO,EAAU,IAAO,WAIxB,OAFA,EAAU,GAAG,SAAU,CAAqB,MAE/B,CACP,QAAS,GAAa,OAAO,EAAU,KAAQ,YACjD,EAAU,IAAI,SAAU,CAAqB,CAEjD,CAIJ,EACA,mBAAoB,CAClB,GAAA,EACI,EAAA,cAAA,CAAc,EAAW,CACvB,iBAAkB,EAAA,wBAClB,cAAc,EAAyB,EAAkB,CACvD,IAAI,EAAa,EAAuB,IAAI,CAAQ,EAEpD,GAAI,CAAC,EAAY,CACf,IAAM,GACJ,EAAQ,cAAgB,EAAA,CACxB,CAAI,EAEF,EAAQ,mBAAqB,WAC/B,EAAc,iBAAiB,iBAAoB,CACjD,IAAM,EAAS,EAAuB,IAAI,CAAQ,EAClD,EAAO,QAAQ,aAAa,cAAe,EAAE,EAEzC,EAAO,cACT,aAAa,EAAO,WAAW,EAC/B,EAAuB,IAAI,EAAU,CACnC,QAAS,EAAO,QAChB,YAAa,IAAA,EACf,CAAC,EAEL,CAAC,EAED,EAAc,iBAAiB,iBAAoB,CACjD,IAAM,EAAS,EAAuB,IAAI,CAAQ,EAElD,EAAuB,IAAI,EAAU,CACnC,QAAS,EAAO,QAChB,YAAa,eAAiB,CAC5B,EAAO,QAAQ,gBAAgB,aAAa,CAC9C,EAAG,GAAI,CACT,CAAC,CACH,CAAC,GAGH,EAAa,CACX,QAAS,EACT,YAAa,IAAA,EACf,EAEA,EAAuB,IAAI,EAAU,CAAU,CACjD,CAEA,OAAO,EAAW,OACpB,CACF,CAAC,EACD,IAAA,EACN,CAAC,CAAC,OAAO,OAAO,EAChB,UAAW,CAAC,OAAO,EACnB,WAAW,EAA8D,CACvE,GAAW,mBAAmB,OAAQ,CAAI,CAC5C,EACA,SAAyC,CACvC,IAAM,EAAQ,GAAW,cAAc,EAClC,KAGL,OAAO,EAAM,IACf,CACF,CACF,CACF,ECjMa,EAAiB,EAAA,GAC3B,CAAE,cACM,CACL,IAAK,QACL,mBAAoB,EAAA,EAAC,EAAA,YAAA,CAAY,EAAQ,QAAQ,CAAC,EAClD,WAAY,CAAC,SAAS,CACxB,EAEJ,ECZa,EAAiB,EAAA,OACrB,CACL,IAAK,QACL,mBAAoB,EAAA,EAAC,EAAA,YAAA,CAAY,CAAC,EAClC,UAAW,CAAC,UAAW,OAAO,EACjB,YAAA,EAAA,YACA,YAAA,EAAA,WACf,EACD,ECcD,SAAS,EACP,EACA,EACA,CACA,EAAO,SAAU,GACf,EAAG,QAAQ,EAAA,eAAgB,CAAE,KAAM,EAAU,IAAK,EAAS,GAAI,CAAC,CAClE,CACF,CAEA,IAAa,EAAoB,EAAA,GAC9B,CAAE,SAAQ,aAAsD,CAC/D,IAAI,EAQE,EAAQ,EAAA,EAAY,CAAE,SAAU,EAAM,CAAC,EAE7C,MAAO,CACL,IAAK,WACL,QAMA,KAAK,CAKH,iBAGE,CAAC,EAAG,CACN,GAAI,EACF,OAGF,IAAM,EAAmB,EAAQ,SAEjC,GAAI,CAAC,EACH,MAAU,MAAM,0BAA0B,EAG5C,IAAM,EAAM,IAAI,EAAE,IAElB,EAAE,YACA,EACA,GAAiB,EAAE,oBAAoB,EAAiB,GAAI,CAC9D,EAGA,IAAM,EAAiB,EAAoB,EAAkB,CAAG,EAEhE,EAAc,CACZ,UAAW,EAAA,eAAe,SAAS,EAAO,gBAAgB,CAAC,CACxD,YAAY,UACf,mBACA,gBACF,EAEA,IAAM,EAAa,CACjB,GAAG,EACH,SAAU,CACZ,EAKA,EAAO,iBACL,CAAC,QAAS,UAAW,OAAO,EAC5B,CACE,EAAe,CAAU,EAEzB,EAAe,CACjB,CACF,EAEA,EAAuB,EAAQ,CAAc,EAG7C,EAAM,SAAS,CAAE,SAAU,EAAK,CAAC,CACnC,EAOA,MAAM,CAAE,eAAyC,CAC/C,GAAI,CAAC,EACH,OAGF,GAAM,CAAE,mBAAkB,iBAAgB,aAAc,EAmBxD,GAhBA,EAAO,iBACL,CAAC,QAAS,UAAW,OAAO,EAC5B,CACE,EAAe,CAAO,EACtB,EAAiB,CAAO,EACxB,EAAe,CACjB,CACF,EAEA,EAAuB,EAAQ,CAAgB,EAG/C,EAAA,eAAe,SACb,EAAO,gBACT,CAAC,CAAE,YAAY,UAAY,EAEvB,EAAa,CAEf,IAAM,EAAS,EAAE,oBACf,EAAe,IACf,EAAE,kBAAkB,EAAiB,GAAI,CAC3C,EAEA,EAAE,YAAY,EAAiB,IAAM,EAAQ,CAAM,CACrD,CAEA,EAAc,IAAA,GAEd,EAAM,SAAS,CAAE,SAAU,EAAM,CAAC,CACpC,CACF,CACF,CACF,ECtJa,EAAmC,EAAA,GAC7C,CAAE,aACM,CACL,IAAK,mBACL,aAAc,EAAkB,EAAyB,SAAW,CAClE,IAAM,EAAmB,EAAA,eAAe,SACtC,EAAO,gBACT,EACA,GAAI,CAAC,EACH,MAAU,MAAM,8BAA8B,EAIhD,GAAI,IAAa,EACf,UAAa,EAIf,GAAI,EAAiB,QAAQ,KAAK,SAAW,EAAG,CAK9C,IAAM,EACJ,EAAO,aACL,iBACF,EACF,GAAI,CAAC,EACH,MAAU,MACR,6EACF,EAEF,OAAO,EAAS,YAAY,EAAU,CAAI,CAC5C,CAEA,IAAM,GAAA,EAAmB,EAAA,mCAAA,CACvB,GAAY,IAAS,QAAU,EAAI,IACnC,EAAiB,QAAQ,KACzB,EAAiB,QAAQ,OAC3B,EAEA,UAAa,CACX,IAAM,EAAsB,EAAA,eAAe,SACzC,EAAO,gBACT,EAMM,EAAY,EAAoB,QAAQ,KACxC,GAAA,EAAM,EAAA,mCAAA,CACV,EAAU,IACV,EACA,EACA,EAAoB,QAAQ,OAC9B,EAGA,GAAI,IAAQ,KACV,MAAU,MAAM,4CAA4C,EAG9D,OAAO,GAAO,IAAS,QAAU,GAAK,EACxC,CACF,CACF,EAEJ,ECrEM,GACJ,EACA,IACG,CACH,EAAG,CAAW,EACd,EAAY,QAAS,GAAY,CAC3B,aAAmB,EAAE,YACvB,EAAgB,EAAS,CAAE,CAE/B,CAAC,CACH,ECdA,EAAe,EDmBoC,EAAU,IAAO,CAGlE,IAAM,EAMF,IAAI,IA0DR,OAvDA,EAAS,QAAS,GAAY,CACxB,aAAmB,EAAE,YACvB,EAAgB,EAAU,GAAY,CACpC,GACE,EAAQ,WAAa,kBACrB,EAAQ,aAAa,IAAI,EACzB,CACA,IAAM,EAAY,EAAQ,aAAa,WAAW,EAC5C,EAAkB,EAAQ,aAAa,iBAAiB,EAExD,EAAS,CACb,UACE,IAAc,EAAA,GAAa,UAAU,QACjC,IAAA,GACA,EACN,gBACE,IAAoB,EAAA,GAAa,gBAAgB,QAC7C,IAAA,GACA,CACR,GAEI,EAAO,WAAa,EAAO,kBAC7B,EAAsB,IAAI,EAAQ,aAAa,IAAI,EAAI,CAAM,CAEjE,CACF,CAAC,CAEL,CAAC,EAEG,EAAsB,OAAS,IAOnC,EAAG,IAAI,aAAa,EAAM,IAAQ,CAChC,GACE,EAAK,KAAK,OAAS,kBACnB,EAAsB,IAAI,EAAK,MAAM,EAAE,EACvC,CACA,IAAM,EAAK,EAAG,IAAI,OAAO,EAAM,CAAC,EAChC,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,EAAG,cAAc,EAAM,EAAG,IAAA,GAAW,CAEnC,GAAG,EAAG,MAEN,GAAG,EAAsB,IAAI,EAAK,MAAM,EAAE,CAC5C,CAAC,CACH,CACF,CAAC,EAEM,GACT,CCvFmC,ECYtB,EAAkB,EAAA,GAC5B,CAAE,aAA6D,CAC9D,IAAI,EAAgB,GACd,EAAY,IAAI,EAAA,UAAU,iBAAiB,EAEjD,MAAO,CACL,IAAK,kBACL,mBAAoB,CAClB,IAAI,EAAA,OAAO,CACT,IAAK,EACL,mBAAoB,EAAc,EAAW,IAAa,CAKxD,GAJI,GAMF,CAAC,EAAa,KAAM,GAAO,EAAG,QAAQ,SAAS,CAAC,GAEhD,EAAa,MAAO,GAAO,CAAC,EAAG,UAAU,GAEzC,CAAC,EAAQ,SAAS,WAElB,OAGF,IAAM,EAAK,EAAS,GACpB,IAAK,IAAM,KAAiB,EAC1B,EAAc,EAAQ,SAAU,CAAE,EAGpC,KAAgB,GAEX,EAAG,WAIR,OAAO,CACT,CACF,CAAC,CACH,CACF,CACF,CACF,ECtCA,SAAgB,EAEd,EAEA,EAKA,CACA,GAAM,CAAE,YAAa,EAErB,SAAS,GAAc,CACrB,IAAM,EAAM,EAAO,aAAa,CAAiB,EACjD,GAAI,CAAC,EACH,MAAU,MACR,0HAEF,EAEF,OAAO,CACT,CAEA,MAAO,CACL,uBAA0B,EAC1B,4BAA+B,EAAE,sBAAsB,EAAS,GAAI,EACpE,QAAS,CAGP,mBAAoB,GACpB,aACE,EACA,EACA,CACA,IAAM,EAAW,EAAY,EAGzB,EAAS,MAAM,MAAM,UACvB,EAAS,MAAM,CAAE,YAAa,EAAM,CAAC,EAGvC,EAAS,KAAK,CAAE,cAAe,CAAgB,CAAC,CAClD,EAEA,aAAc,CACZ,IAAM,EAAW,EAAY,EACzB,EAAS,MAAM,MAAM,UACvB,EAAS,MAAM,CAAE,YAAa,EAAM,CAAC,CAEzC,EAEA,aAAa,EAA8B,CAKzC,MAAU,MACR,gEACF,CACF,CACF,CACF,CACF,CCzCA,IAAa,EAAyB,EAAA,GACnC,CAAE,cACM,CACL,IAAK,gBACL,oBAAqB,CACnB,EAAqB,EACrB,EAAkB,CAAO,EACzB,EAAiC,EACjC,EAAgB,CAAO,EACvB,EAAiB,CAAO,EACxB,EAAe,CAAO,EACtB,EAAe,CACjB,CACF,EAEJ,EAEA,SAAgB,EAGd,EAMS,CACT,MAAO,CACL,GAAG,EACH,WAAY,CACV,GAAI,EAAQ,YAAc,CAAC,EAC3B,EAAuB,EAAQ,aAAa,CAC9C,EAEA,kBAAmB,CAAC,UAAW,GAAI,EAAQ,mBAAqB,CAAC,CAAE,EAGnE,eAAgB,CAAC,CAAE,KAAM,YAAa,GAAI,gBAAiB,CAAC,CAC9D,CACF,CCzEA,SAAgB,EAAc,EAAsB,CAClD,IAAM,EAAO,IAAI,EAAE,IAWnB,GAVA,EAAK,IAAI,KAAM,EAAQ,EAAE,EACzB,EAAK,IAAI,SAAU,EAAQ,MAAM,EACjC,EAAK,IAAI,YAAa,EAAQ,UAAU,QAAQ,CAAC,EACjD,EAAK,IAAI,YAAa,EAAQ,UAAU,QAAQ,CAAC,EAC7C,EAAQ,WACV,EAAK,IAAI,YAAa,EAAQ,UAAU,QAAQ,CAAC,EACjD,EAAK,IAAI,OAAQ,IAAA,EAAS,GAE1B,EAAK,IAAI,OAAQ,EAAQ,IAAI,EAE3B,EAAQ,UAAU,OAAS,EAC7B,MAAU,MAAM,4CAA4C,EAW9D,OAHA,EAAK,IAAI,kBAAmB,IAAI,EAAE,GAAK,EACvC,EAAK,IAAI,WAAY,EAAQ,QAAQ,EAE9B,CACT,CAEA,SAAgB,EAAa,EAAoB,CAC/C,IAAM,EAAO,IAAI,EAAE,IACnB,EAAK,IAAI,KAAM,EAAO,EAAE,EACxB,EAAK,IAAI,YAAa,EAAO,UAAU,QAAQ,CAAC,EAChD,EAAK,IAAI,YAAa,EAAO,UAAU,QAAQ,CAAC,EAChD,IAAM,EAAgB,IAAI,EAAE,MAS5B,OAPA,EAAc,KAAK,EAAO,SAAS,IAAK,GAAY,EAAc,CAAO,CAAC,CAAC,EAE3E,EAAK,IAAI,WAAY,CAAa,EAClC,EAAK,IAAI,WAAY,EAAO,QAAQ,EACpC,EAAK,IAAI,oBAAqB,EAAO,mBAAmB,QAAQ,CAAC,EACjE,EAAK,IAAI,aAAc,EAAO,UAAU,EACxC,EAAK,IAAI,WAAY,EAAO,QAAQ,EAC7B,CACT,CAQA,SAAgB,EACd,EAC+B,CAC/B,MAAO,CACL,MAAO,EAAK,IAAI,OAAO,EACvB,UAAW,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EACzC,OAAQ,EAAK,IAAI,QAAQ,CAC3B,CACF,CAEA,SAAS,EAAgB,EAAyC,CAKhE,MAJsB,CAAC,GAAG,EAAK,OAAO,CAAC,CAAC,CAAC,IAAK,GAC5C,EAAe,CAAQ,CAGlB,CAAA,CAAc,QAClB,EAA4B,IAA4C,CACvE,IAAM,EAAmB,EAAI,KAAM,GAAM,EAAE,QAAU,EAAS,KAAK,EAgBnE,OAfI,GACF,EAAiB,QAAQ,KAAK,EAAS,MAAM,EAC7C,EAAiB,UAAY,IAAI,KAC/B,KAAK,IACH,EAAiB,UAAU,QAAQ,EACnC,EAAS,UAAU,QAAQ,CAC7B,CACF,GAEA,EAAI,KAAK,CACP,MAAO,EAAS,MAChB,UAAW,EAAS,UACpB,QAAS,CAAC,EAAS,MAAM,CAC3B,CAAC,EAEI,CACT,EACA,CAAC,CACH,CACF,CAEA,SAAgB,EAAc,EAA+B,CAC3D,MAAO,CACL,KAAM,UACN,GAAI,EAAK,IAAI,IAAI,EACjB,OAAQ,EAAK,IAAI,QAAQ,EACzB,UAAW,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EACzC,UAAW,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EACzC,UAAW,EAAK,IAAI,WAAW,EAC3B,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EAC9B,IAAA,GACJ,UAAW,EAAgB,EAAK,IAAI,iBAAiB,CAAC,EACtD,SAAU,EAAK,IAAI,UAAU,EAC7B,KAAM,EAAK,IAAI,MAAM,CACvB,CACF,CAEA,SAAgB,EAAa,EAA8B,CACzD,MAAO,CACL,KAAM,SACN,GAAI,EAAK,IAAI,IAAI,EACjB,UAAW,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EACzC,UAAW,IAAI,KAAK,EAAK,IAAI,WAAW,CAAC,EACzC,UAAY,EAAK,IAAI,UAAU,GAA6B,CAAC,EAAA,CAAG,IAC7D,GAAY,EAAc,CAAO,CACpC,EACA,SAAU,EAAK,IAAI,UAAU,EAC7B,kBAAmB,IAAI,KAAK,EAAK,IAAI,mBAAmB,CAAC,EACzD,WAAY,EAAK,IAAI,YAAY,EACjC,SAAU,EAAK,IAAI,UAAU,CAC/B,CACF,CCpHA,IAAsB,EAAtB,cAAiD,EAAA,CAAY,CAEtC,YADrB,YACE,EACA,EACA,CACA,MAAM,CAAI,EAHS,KAAA,YAAA,CAIrB,CAGA,UAAiB,EAAkB,CACjC,IAAM,EAAU,KAAK,YAAY,IAAI,CAAQ,EAC7C,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,OADe,EAAa,CACrB,CACT,CAEA,YAA6C,CAC3C,IAAM,EAAY,IAAI,IAMtB,OALA,KAAK,YAAY,SAAS,EAAS,IAAO,CACpC,aAAmB,EAAE,KACvB,EAAU,IAAI,EAAI,EAAa,CAAO,CAAC,CAE3C,CAAC,EACM,CACT,CAEA,UAAiB,EAAgD,CAC/D,IAAM,MAAiB,CACrB,EAAG,KAAK,WAAW,CAAC,CACtB,EAIA,OAFA,KAAK,YAAY,YAAY,CAAQ,MAExB,CACX,KAAK,YAAY,cAAc,CAAQ,CACzC,CACF,CACF,ECzBa,EAAb,cAAwC,CAAmB,CAEtC,SACA,QAFnB,YACE,EACA,EACA,EACA,EACA,CACA,MAAM,EAAa,CAAI,EALN,KAAA,SAAA,EACA,KAAA,QAAA,CAKnB,CAEA,UAAoB,MAAO,EAAc,EAAgB,IAAe,CACtE,IAAM,EAAW,MAAM,MAAM,GAAG,KAAK,WAAW,IAAQ,CACtD,SACA,KAAM,KAAK,UAAU,CAAI,EACzB,QAAS,CACP,eAAgB,mBAChB,GAAG,KAAK,OACV,CACF,CAAC,EAED,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,aAAa,EAAO,GAAG,EAAK,IAAI,EAAS,YAAY,EAGvE,OAAO,EAAS,KAAK,CACvB,EAEA,oBAA6B,KAAO,IAO9B,CACJ,GAAM,CAAE,WAAU,aAAc,EAM1B,EAAU,EAAA,eAAe,SAC7B,EAAQ,OAAO,gBACjB,CAAC,EAAE,QACG,EAAe,EACjB,CACE,MAAA,EAAM,EAAA,mCAAA,CACJ,EAAU,KACV,EAAQ,KACR,EAAQ,OACV,EACA,QAAA,EAAQ,EAAA,mCAAA,CACN,EAAU,OACV,EAAQ,KACR,EAAQ,OACV,CACF,EACA,IAAA,GAEJ,OAAO,KAAK,UAAU,IAAI,EAAS,gBAAiB,OAAQ,CAC1D,UAAW,CACT,YAAa,EACb,IAAK,CACP,CACF,CAAC,CACH,EAEA,aAAsB,KAAO,IAOpB,KAAK,UAAU,GAAI,OAAQ,CAAO,EAG3C,WAAqB,GAMf,CACJ,GAAM,CAAE,WAAU,GAAG,GAAS,EAC9B,OAAO,KAAK,UAAU,IAAI,EAAS,WAAY,OAAQ,CAAI,CAC7D,EAEA,cAAwB,GAOlB,CACJ,GAAM,CAAE,WAAU,YAAW,GAAG,GAAS,EACzC,OAAO,KAAK,UAAU,IAAI,EAAS,YAAY,IAAa,MAAO,CAAI,CACzE,EAEA,cAAwB,GAIlB,CACJ,GAAM,CAAE,WAAU,YAAW,GAAG,GAAS,EACzC,OAAO,KAAK,UACV,IAAI,EAAS,YAAY,EAAU,QAAQ,CAAC,CAAC,EAAK,aAClD,QACF,CACF,EAEA,aAAuB,GACd,KAAK,UAAU,IAAI,EAAQ,WAAY,QAAQ,EAGxD,cAAwB,GACf,KAAK,UAAU,IAAI,EAAQ,SAAS,UAAW,MAAM,EAG9D,gBAA0B,GACjB,KAAK,UAAU,IAAI,EAAQ,SAAS,YAAa,MAAM,EAGhE,YAAsB,GAIhB,CACJ,GAAM,CAAE,WAAU,YAAW,GAAG,GAAS,EACzC,OAAO,KAAK,UACV,IAAI,EAAS,YAAY,EAAU,YACnC,OACA,CACF,CACF,EAEA,eAAyB,GAKhB,KAAK,UACV,IAAI,EAAQ,SAAS,YAAY,EAAQ,UAAU,aAAa,EAAQ,QACxE,QACF,CAEJ,EC/Ia,EAAb,cAAoC,CAAmB,CAElC,OADnB,YACE,EACA,EACA,EACA,CACA,MAAM,EAAa,CAAI,EAJN,KAAA,OAAA,CAKnB,CAEA,SACE,GAEO,KAAO,IACL,KAAK,YAAY,IAAK,aACpB,EAAG,CAAO,CAClB,EAIL,aAAsB,KAAK,SACxB,GAMK,CACJ,GAAI,CAAC,KAAK,KAAK,gBAAgB,EAC7B,MAAU,MAAM,gBAAgB,EAGlC,IAAM,EAAO,IAAI,KAEX,EAAuB,CAC3B,KAAM,UACN,IAAA,EAAI,EAAA,OAAA,CAAO,EACX,OAAQ,KAAK,OACb,UAAW,EACX,UAAW,EACX,UAAW,CAAC,EACZ,SAAU,EAAQ,eAAe,SACjC,KAAM,EAAQ,eAAe,IAC/B,EAEM,EAAqB,CACzB,KAAM,SACN,IAAA,EAAI,EAAA,OAAA,CAAO,EACX,UAAW,EACX,UAAW,EACX,SAAU,CAAC,CAAO,EAClB,SAAU,GACV,SAAU,EAAQ,QACpB,EAIA,OAFA,KAAK,YAAY,IAAI,EAAO,GAAI,EAAa,CAAM,CAAC,EAE7C,CACT,CACF,EAGA,oBAA6B,IAAA,GAE7B,WAAoB,KAAK,SACtB,GAMK,CACJ,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,GAAI,CAAC,KAAK,KAAK,cAAc,EAAa,CAAO,CAAC,EAChD,MAAU,MAAM,gBAAgB,EAGlC,IAAM,EAAO,IAAI,KACX,EAAuB,CAC3B,KAAM,UACN,IAAA,EAAI,EAAA,OAAA,CAAO,EACX,OAAQ,KAAK,OACb,UAAW,EACX,UAAW,EACX,UAAW,IAAA,GACX,UAAW,CAAC,EACZ,SAAU,EAAQ,QAAQ,SAC1B,KAAM,EAAQ,QAAQ,IACxB,EAOA,OALA,EAAS,IAAI,UAAU,CAAC,CAAyB,KAAK,CACpD,EAAc,CAAO,CACvB,CAAC,EAED,EAAQ,IAAI,YAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,EACtC,CACT,CACF,EAEA,cAAuB,KAAK,SACzB,GAOK,CACJ,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,IAAM,EAAgB,EACpB,EAAQ,IAAI,UAAU,EACrB,GAAY,EAAQ,IAAI,IAAI,IAAM,EAAQ,SAC7C,EAEA,GAAI,IAAkB,GACpB,MAAU,MAAM,mBAAmB,EAGrC,IAAM,EAAW,EAAQ,IAAI,UAAU,CAAC,CAAC,IAAI,CAAa,EAE1D,GAAI,CAAC,KAAK,KAAK,iBAAiB,EAAc,CAAQ,CAAC,EACrD,MAAU,MAAM,gBAAgB,EAGlC,EAAS,IAAI,OAAQ,EAAQ,QAAQ,IAAI,EACzC,EAAS,IAAI,YAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,EAC9C,EAAS,IAAI,WAAY,EAAQ,QAAQ,QAAQ,CACnD,CACF,EAEA,cAAuB,KAAK,SACzB,GAIK,CACJ,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,IAAM,EAAgB,EACpB,EAAQ,IAAI,UAAU,EACrB,GAAY,EAAQ,IAAI,IAAI,IAAM,EAAQ,SAC7C,EAEA,GAAI,IAAkB,GACpB,MAAU,MAAM,mBAAmB,EAGrC,IAAM,EAAW,EAAQ,IAAI,UAAU,CAAC,CAAC,IAAI,CAAa,EAE1D,GAAI,CAAC,KAAK,KAAK,iBAAiB,EAAc,CAAQ,CAAC,EACrD,MAAU,MAAM,gBAAgB,EAGlC,GAAI,EAAS,IAAI,WAAW,EAC1B,MAAU,MAAM,yBAAyB,EAGvC,EAAQ,YACV,EAAS,IAAI,YAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,EAC9C,EAAS,IAAI,OAAQ,IAAA,EAAS,GAE9B,EAAQ,IAAI,UAAU,CAAC,CAAC,OAAO,CAAa,EAI3C,EAAQ,IAAI,UAAU,CAAC,CACrB,QAAQ,CAAC,CACT,MAAO,GAAY,EAAQ,IAAI,WAAW,CAAC,IAG1C,EAAQ,WACV,EAAQ,IAAI,YAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,EAE7C,KAAK,YAAY,OAAO,EAAQ,QAAQ,GAI5C,EAAQ,IAAI,YAAa,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,CAC/C,CACF,EAEA,aAAsB,KAAK,SAAU,GAAkC,CACrE,GACE,CAAC,KAAK,KAAK,gBACT,EAAa,KAAK,YAAY,IAAI,EAAQ,QAAQ,CAAC,CACrD,EAEA,MAAU,MAAM,gBAAgB,EAGlC,KAAK,YAAY,OAAO,EAAQ,QAAQ,CAC1C,CAAC,EAED,cAAuB,KAAK,SAAU,GAAkC,CACtE,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,GAAI,CAAC,KAAK,KAAK,iBAAiB,EAAa,CAAO,CAAC,EACnD,MAAU,MAAM,gBAAgB,EAGlC,EAAQ,IAAI,WAAY,EAAI,EAC5B,EAAQ,IAAI,oBAAqB,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,EACrD,EAAQ,IAAI,aAAc,KAAK,MAAM,CACvC,CAAC,EAED,gBAAyB,KAAK,SAAU,GAAkC,CACxE,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,GAAI,CAAC,KAAK,KAAK,mBAAmB,EAAa,CAAO,CAAC,EACrD,MAAU,MAAM,gBAAgB,EAGlC,EAAQ,IAAI,WAAY,EAAK,EAC7B,EAAQ,IAAI,oBAAqB,IAAI,KAAK,CAAA,CAAE,QAAQ,CAAC,CACvD,CAAC,EAED,YAAqB,KAAK,SACvB,GAAoE,CACnE,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,IAAM,EAAgB,EACpB,EAAQ,IAAI,UAAU,EACrB,GAAY,EAAQ,IAAI,IAAI,IAAM,EAAQ,SAC7C,EAEA,GAAI,IAAkB,GACpB,MAAU,MAAM,mBAAmB,EAGrC,IAAM,EAAW,EAAQ,IAAI,UAAU,CAAC,CAAC,IAAI,CAAa,EAE1D,GAAI,CAAC,KAAK,KAAK,eAAe,EAAc,CAAQ,EAAG,EAAQ,KAAK,EAClE,MAAU,MAAM,gBAAgB,EAGlC,IAAM,EAAO,IAAI,KAEX,EAAM,GAAG,KAAK,OAAO,GAAG,EAAQ,QAEhC,EAAkB,EAAS,IAAI,iBAAiB,EAElD,MAAgB,IAAI,CAAG,EAGpB,CACL,IAAM,EAAW,IAAI,EAAE,IACvB,EAAS,IAAI,QAAS,EAAQ,KAAK,EACnC,EAAS,IAAI,YAAa,EAAK,QAAQ,CAAC,EACxC,EAAS,IAAI,SAAU,KAAK,MAAM,EAClC,EAAgB,IAAI,EAAK,CAAQ,CACnC,CACF,CACF,EAEA,eAAwB,KAAK,SAC1B,GAAoE,CACnE,IAAM,EAAU,KAAK,YAAY,IAAI,EAAQ,QAAQ,EACrD,GAAI,CAAC,EACH,MAAU,MAAM,kBAAkB,EAGpC,IAAM,EAAgB,EACpB,EAAQ,IAAI,UAAU,EACrB,GAAY,EAAQ,IAAI,IAAI,IAAM,EAAQ,SAC7C,EAEA,GAAI,IAAkB,GACpB,MAAU,MAAM,mBAAmB,EAGrC,IAAM,EAAW,EAAQ,IAAI,UAAU,CAAC,CAAC,IAAI,CAAa,EAE1D,GACE,CAAC,KAAK,KAAK,kBAAkB,EAAc,CAAQ,EAAG,EAAQ,KAAK,EAEnE,MAAU,MAAM,gBAAgB,EAGlC,IAAM,EAAM,GAAG,KAAK,OAAO,GAAG,EAAQ,QAItC,EAFiC,IAAI,iBAErC,CAAA,CAAgB,OAAO,CAAG,CAC5B,CACF,CACF,EAEA,SAAS,EACP,EACA,EACA,CACA,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,IACjC,GAAI,EAAU,EAAO,IAAI,CAAC,CAAC,EACzB,OAAO,EAGX,MAAO,EACT"}