[
  {
    "path": ".dockerignore",
    "content": "# Git\n.git\n.gitignore\n.gitattributes\n\n# IDE\n.idea\n.vscode\n*.swp\n*.swo\n\n# Documentation\n*.md\ndocs/\nLICENSE\n\n# Development\n.venv\n__pycache__\n*.pyc\n*.pyo\n.pytest_cache\n.coverage\nhtmlcov/\n\n# macOS\n.DS_Store\n._*\n\n# Frontend source (will be built from GitHub)\nfcb-fronted/\nthemes/\n\n# Test files\ntest_*.py\ntests/\n\n# Data (should be mounted as volume)\ndata/\n\n# GitHub\n.github/\n\n# Docker\nDockerfile\ndocker-compose.yml\n.dockerignore\n\n# Claude\n.claude/\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.js linguist-language=python\n*.css linguist-language=python\n*.html linguist-language=python\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/custom.md",
    "content": "---\nname: Custom issue template\nabout: Describe this issue template's purpose here.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/docker-image.yml",
    "content": "name: Build and push Docker image\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - master\n      - dev\n    tags:\n      - 'v*'\n\nenv:\n  REGISTRY_IMAGE: ${{ secrets.DOCKER_USERNAME }}/filecodebox\n\njobs:\n  buildx:\n    runs-on: ubuntu-latest\n    timeout-minutes: 60\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          submodules: true\n\n      - name: Extract metadata\n        id: meta\n        uses: docker/metadata-action@v5\n        with:\n          images: ${{ env.REGISTRY_IMAGE }}\n          tags: |\n            type=ref,event=branch\n            type=ref,event=pr\n            type=semver,pattern={{version}}\n            type=semver,pattern={{major}}.{{minor}}\n            type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}\n            type=raw,value=beta,enable={{is_default_branch}}\n            type=raw,value=latest,enable={{is_default_branch}}\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v3\n\n      - name: Set up Docker Buildx\n        uses: docker/setup-buildx-action@v3\n        with:\n          driver-opts: |\n            image=moby/buildkit:latest\n            network=host\n\n      - name: Login to DockerHub\n        uses: docker/login-action@v3\n        with:\n          username: ${{ secrets.DOCKER_USERNAME }}\n          password: ${{ secrets.DOCKER_PASSWORD }}\n\n      - name: Build and push\n        uses: docker/build-push-action@v6\n        with:\n          context: .\n          platforms: linux/amd64,linux/arm64\n          push: true\n          tags: ${{ steps.meta.outputs.tags }}\n          labels: ${{ steps.meta.outputs.labels }}\n          cache-from: type=gha\n          cache-to: type=gha,mode=max\n          provenance: false\n"
  },
  {
    "path": ".github/workflows/docs.yml",
    "content": "# 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程\n#\nname: Deploy VitePress site to Pages\n\non:\n  # 在针对 `main` 分支的推送上运行。如果你\n  # 使用 `master` 分支作为默认分支，请将其更改为 `master`\n  push:\n    branches: [ master ]\n\n  # 允许你从 Actions 选项卡手动运行此工作流程\n  workflow_dispatch:\n\n# 设置 GITHUB_TOKEN 的权限，以允许部署到 GitHub Pages\npermissions:\n  contents: read\n  pages: write\n  id-token: write\n\n# 只允许同时进行一次部署，跳过正在运行和最新队列之间的运行队列\n# 但是，不要取消正在进行的运行，因为我们希望允许这些生产部署完成\nconcurrency:\n  group: pages\n  cancel-in-progress: false\n\njobs:\n  # 构建工作\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0 # 如果未启用 lastUpdated，则不需要\n      - uses: pnpm/action-setup@v3\n        with:\n          version: 9\n      # - uses: oven-sh/setup-bun@v1 # 如果使用 Bun，请取消注释\n      - name: Setup Node\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'pnpm'\n          cache-dependency-path: 'docs/pnpm-lock.yaml'\n      - name: Setup Pages\n        uses: actions/configure-pages@v4\n      - name: Install dependencies\n        working-directory: docs\n        run: pnpm install\n      - name: Build with VitePress\n        working-directory: docs\n        run: pnpm run docs:build\n      - name: Upload artifact\n        uses: actions/upload-pages-artifact@v3\n        with:\n          path: docs/.vitepress/dist\n\n  # 部署工作\n  deploy:\n    environment:\n      name: github-pages\n      url: ${{ steps.deployment.outputs.page_url }}\n    needs: build\n    runs-on: ubuntu-latest\n    name: Deploy\n    steps:\n      - name: Deploy to GitHub Pages\n        id: deployment\n        uses: actions/deploy-pages@v4\n"
  },
  {
    "path": ".gitignore",
    "content": "media/\nlogs/\n.idea\n/data\n__pycache__/\n*.py[cod]\n*$py.class\n# Created by .ignore support plugin (hsz.mobi)\n### Python template\n# Byte-compiled / optimized / DLL files\ndatabase.db\n# C extensions\n*.so\n*.env\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n.vite/\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test.pdf / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\ncover/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\n.pybuilder/\ntarget/\n*.db\n./filecodebox.db-shm\n./filecodebox.db-wal\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n#   For a library or package, you might want to ignore these files since the code is\n#   intended to run in multiple environments; otherwise, check them in:\n# .python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\ndata/.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# pytype static type analyzer\n.pytype/\n\n# Cython debug symbols\ncython_debug/\n\n# Project\n.vscode\n.DS_Store\nfor_test.py\n.html\n/evaluate/temp.py\n/evaluation/back.json\ndata/.env\n.backup/\n/cloc-1.64.exe\n\n# Ignore node_modules\nnode_modules/\n\nAGENTS.md\n\ndist/\n# Frontend themes (built from GitHub during Docker build)\nthemes/"
  },
  {
    "path": "Dockerfile",
    "content": "# 第一阶段：构建前端主题\nFROM node:20-alpine AS frontend-builder\n\nRUN apk add --no-cache git python3 make g++\n\nWORKDIR /build\n\n# 克隆并构建 2024 主题\nRUN git clone --depth 1 https://github.com/vastsa/FileCodeBoxFronted.git /build/fronted-2024 && \\\n    cd /build/fronted-2024 && \\\n    npm install && \\\n    npm run build\n\n# 克隆并构建 2023 主题\nRUN git clone --depth 1 https://github.com/vastsa/FileCodeBoxFronted2023.git /build/fronted-2023 && \\\n    cd /build/fronted-2023 && \\\n    npm install --legacy-peer-deps && \\\n    npm run build\n\n# 第二阶段：构建最终镜像\nFROM python:3.12-slim-bookworm\nLABEL author=\"Lan\"\nLABEL email=\"xzu@live.com\"\n\nWORKDIR /app\n\n# 复制项目文件（通过 .dockerignore 排除不必要的文件）\nCOPY . .\n\n# 设置时区\nRUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \\\n    echo 'Asia/Shanghai' > /etc/timezone\n\n# 从构建阶段复制编译好的前端主题\nCOPY --from=frontend-builder /build/fronted-2024/dist ./themes/2024\nCOPY --from=frontend-builder /build/fronted-2023/dist ./themes/2023\n\n# 安装 Python 依赖\nRUN pip install --no-cache-dir -r requirements.txt\n\n# 环境变量配置\nENV HOST=\"0.0.0.0\" \\\n    PORT=12345 \\\n    WORKERS=1 \\\n    LOG_LEVEL=\"info\"\n\nEXPOSE 12345\n\n# 生产环境启动命令\nCMD uvicorn main:app \\\n    --host $HOST \\\n    --port $PORT \\\n    --workers $WORKERS \\\n    --log-level $LOG_LEVEL \\\n    --proxy-headers \\\n    --forwarded-allow-ips \"*\""
  },
  {
    "path": "LICENSE",
    "content": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\n  This version of the GNU Lesser General Public License incorporates\nthe terms and conditions of version 3 of the GNU General Public\nLicense, supplemented by the additional permissions listed below.\n\n  0. Additional Definitions.\n\n  As used herein, \"this License\" refers to version 3 of the GNU Lesser\nGeneral Public License, and the \"GNU GPL\" refers to version 3 of the GNU\nGeneral Public License.\n\n  \"The Library\" refers to a covered work governed by this License,\nother than an Application or a Combined Work as defined below.\n\n  An \"Application\" is any work that makes use of an interface provided\nby the Library, but which is not otherwise based on the Library.\nDefining a subclass of a class defined by the Library is deemed a mode\nof using an interface provided by the Library.\n\n  A \"Combined Work\" is a work produced by combining or linking an\nApplication with the Library.  The particular version of the Library\nwith which the Combined Work was made is also called the \"Linked\nVersion\".\n\n  The \"Minimal Corresponding Source\" for a Combined Work means the\nCorresponding Source for the Combined Work, excluding any source code\nfor portions of the Combined Work that, considered in isolation, are\nbased on the Application, and not on the Linked Version.\n\n  The \"Corresponding Application Code\" for a Combined Work means the\nobject code and/or source code for the Application, including any data\nand utility programs needed for reproducing the Combined Work from the\nApplication, but excluding the System Libraries of the Combined Work.\n\n  1. Exception to Section 3 of the GNU GPL.\n\n  You may convey a covered work under sections 3 and 4 of this License\nwithout being bound by section 3 of the GNU GPL.\n\n  2. Conveying Modified Versions.\n\n  If you modify a copy of the Library, and, in your modifications, a\nfacility refers to a function or data to be supplied by an Application\nthat uses the facility (other than as an argument passed when the\nfacility is invoked), then you may convey a copy of the modified\nversion:\n\n   a) under this License, provided that you make a good faith effort to\n   ensure that, in the event an Application does not supply the\n   function or data, the facility still operates, and performs\n   whatever part of its purpose remains meaningful, or\n\n   b) under the GNU GPL, with none of the additional permissions of\n   this License applicable to that copy.\n\n  3. Object Code Incorporating Material from Library Header Files.\n\n  The object code form of an Application may incorporate material from\na header file that is part of the Library.  You may convey such object\ncode under terms of your choice, provided that, if the incorporated\nmaterial is not limited to numerical parameters, data structure\nlayouts and accessors, or small macros, inline functions and templates\n(ten or fewer lines in length), you do both of the following:\n\n   a) Give prominent notice with each copy of the object code that the\n   Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the object code with a copy of the GNU GPL and this license\n   document.\n\n  4. Combined Works.\n\n  You may convey a Combined Work under terms of your choice that,\ntaken together, effectively do not restrict modification of the\nportions of the Library contained in the Combined Work and reverse\nengineering for debugging such modifications, if you also do each of\nthe following:\n\n   a) Give prominent notice with each copy of the Combined Work that\n   the Library is used in it and that the Library and its use are\n   covered by this License.\n\n   b) Accompany the Combined Work with a copy of the GNU GPL and this license\n   document.\n\n   c) For a Combined Work that displays copyright notices during\n   execution, include the copyright notice for the Library among\n   these notices, as well as a reference directing the user to the\n   copies of the GNU GPL and this license document.\n\n   d) Do one of the following:\n\n       0) Convey the Minimal Corresponding Source under the terms of this\n       License, and the Corresponding Application Code in a form\n       suitable for, and under terms that permit, the user to\n       recombine or relink the Application with a modified version of\n       the Linked Version to produce a modified Combined Work, in the\n       manner specified by section 6 of the GNU GPL for conveying\n       Corresponding Source.\n\n       1) Use a suitable shared library mechanism for linking with the\n       Library.  A suitable mechanism is one that (a) uses at run time\n       a copy of the Library already present on the user's computer\n       system, and (b) will operate properly with a modified version\n       of the Library that is interface-compatible with the Linked\n       Version.\n\n   e) Provide Installation Information, but only if you would otherwise\n   be required to provide such information under section 6 of the\n   GNU GPL, and only to the extent that such information is\n   necessary to install and execute a modified version of the\n   Combined Work produced by recombining or relinking the\n   Application with a modified version of the Linked Version. (If\n   you use option 4d0, the Installation Information must accompany\n   the Minimal Corresponding Source and Corresponding Application\n   Code. If you use option 4d1, you must provide the Installation\n   Information in the manner specified by section 6 of the GNU GPL\n   for conveying Corresponding Source.)\n\n  5. Combined Libraries.\n\n  You may place library facilities that are a work based on the\nLibrary side by side in a single library together with other library\nfacilities that are not Applications and are not covered by this\nLicense, and convey such a combined library under terms of your\nchoice, if you do both of the following:\n\n   a) Accompany the combined library with a copy of the same work based\n   on the Library, uncombined with any other library facilities,\n   conveyed under the terms of this License.\n\n   b) Give prominent notice with the combined library that part of it\n   is a work based on the Library, and explaining where to find the\n   accompanying uncombined form of the same work.\n\n  6. Revised Versions of the GNU Lesser General Public License.\n\n  The Free Software Foundation may publish revised and/or new versions\nof the GNU Lesser General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\n  Each version is given a distinguishing version number. If the\nLibrary as you received it specifies that a certain numbered version\nof the GNU Lesser General Public License \"or any later version\"\napplies to it, you have the option of following the terms and\nconditions either of that published version or of any later version\npublished by the Free Software Foundation. If the Library as you\nreceived it does not specify a version number of the GNU Lesser\nGeneral Public License, you may choose any version of the GNU Lesser\nGeneral Public License ever published by the Free Software Foundation.\n\n  If the Library as you received it specifies that a proxy can decide\nwhether future versions of the GNU Lesser General Public License shall\napply, that proxy's public statement of acceptance of any version is\npermanent authorization for you to choose that version for the\nLibrary."
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nUse this section to tell people about which versions of your project are\ncurrently being supported with security updates.\n\n| Version | Supported          |\n| ------- | ------------------ |\n| 5.1.x   | :white_check_mark: |\n| 5.0.x   | :x:                |\n| 4.0.x   | :white_check_mark: |\n| < 4.0   | :x:                |\n\n## Reporting a Vulnerability\n\nUse this section to tell people how to report a vulnerability.\n\nTell them where to go, how often they can expect to get an update on a\nreported vulnerability, what to expect if the vulnerability is accepted or\ndeclined, etc.\n"
  },
  {
    "path": "apps/__init__.py",
    "content": "# @Time    : 2023/8/13 20:43\n# @Author  : Lan\n# @File    : __init__.py.py\n# @Software: PyCharm\n"
  },
  {
    "path": "apps/admin/__init__.py",
    "content": "# @Time    : 2023/8/14 14:38\n# @Author  : Lan\n# @File    : __init__.py.py\n# @Software: PyCharm\n"
  },
  {
    "path": "apps/admin/dependencies.py",
    "content": "# @Time    : 2023/8/15 17:43\n# @Author  : Lan\n# @File    : depends.py\n# @Software: PyCharm\nfrom fastapi import Header, HTTPException, Depends\nfrom fastapi.requests import Request\nimport base64\nimport hmac\nimport json\nimport time\nfrom core.settings import settings\nfrom apps.admin.services import FileService, ConfigService, LocalFileService\n\n\ndef create_token(data: dict, expires_in: int = 3600 * 24 * 30) -> str:\n    \"\"\"\n    创建JWT token\n    :param data: 数据负载\n    :param expires_in: 过期时间(秒)\n    \"\"\"\n    header = base64.b64encode(\n        json.dumps({\"alg\": \"HS256\", \"typ\": \"JWT\"}).encode()\n    ).decode()\n    payload = base64.b64encode(\n        json.dumps({**data, \"exp\": int(time.time()) + expires_in}).encode()\n    ).decode()\n\n    signature = hmac.new(\n        settings.admin_token.encode(), f\"{header}.{payload}\".encode(), \"sha256\"\n    ).digest()\n    signature = base64.b64encode(signature).decode()\n\n    return f\"{header}.{payload}.{signature}\"\n\n\ndef verify_token(token: str) -> dict:\n    \"\"\"\n    验证JWT token\n    :param token: JWT token\n    :return: 解码后的数据\n    \"\"\"\n    try:\n        header_b64, payload_b64, signature_b64 = token.split(\".\")\n\n        # 验证签名\n        expected_signature = hmac.new(\n            settings.admin_token.encode(),\n            f\"{header_b64}.{payload_b64}\".encode(),\n            \"sha256\",\n        ).digest()\n        expected_signature_b64 = base64.b64encode(expected_signature).decode()\n\n        if not hmac.compare_digest(signature_b64, expected_signature_b64):\n            raise ValueError(\"无效的签名\")\n\n        # 解码payload\n        payload = json.loads(base64.b64decode(payload_b64))\n\n        # 检查是否过期\n        if payload.get(\"exp\", 0) < time.time():\n            raise ValueError(\"token已过期\")\n\n        return payload\n    except Exception as e:\n        raise ValueError(f\"token验证失败: {str(e)}\")\n\n\ndef _extract_bearer_token(authorization: str) -> str:\n    if not authorization or not authorization.startswith(\"Bearer \"):\n        raise HTTPException(status_code=401, detail=\"未授权或授权校验失败\")\n    token = authorization.split(\" \", 1)[1].strip()\n    if not token:\n        raise HTTPException(status_code=401, detail=\"未授权或授权校验失败\")\n    return token\n\n\ndef _require_admin_payload(authorization: str) -> dict:\n    token = _extract_bearer_token(authorization)\n    try:\n        payload = verify_token(token)\n    except ValueError as e:\n        raise HTTPException(status_code=401, detail=str(e))\n    if not payload.get(\"is_admin\", False):\n        raise HTTPException(status_code=401, detail=\"未授权或授权校验失败\")\n    return payload\n\n\nADMIN_PUBLIC_ENDPOINTS = {(\"POST\", \"/admin/login\")}\n\n\nasync def admin_required(\n    authorization: str = Header(default=None), request: Request = None\n):\n    \"\"\"\n    验证管理员权限\n    \"\"\"\n    if request and (request.method, request.url.path) in ADMIN_PUBLIC_ENDPOINTS:\n        return None\n    return _require_admin_payload(authorization)\n\n\nasync def share_required_login(authorization: str = Header(default=None)):\n    \"\"\"\n    验证分享上传权限\n    \n    当settings.openUpload为False时，要求用户必须登录并具有管理员权限\n    当settings.openUpload为True时，允许游客上传\n    \n    :param authorization: 认证头信息\n    :param request: 请求对象\n    :return: 验证结果\n    \"\"\"\n    if not settings.openUpload:\n        if not authorization or not authorization.startswith(\"Bearer \"):\n            raise HTTPException(\n                status_code=403, detail=\"本站未开启游客上传，如需上传请先登录后台\"\n            )\n        _require_admin_payload(authorization)\n\n    return True\n\n\nasync def get_file_service():\n    return FileService()\n\n\nasync def get_config_service():\n    return ConfigService()\n\n\nasync def get_local_file_service():\n    return LocalFileService()\n"
  },
  {
    "path": "apps/admin/schemas.py",
    "content": "import datetime\nfrom typing import Optional, Union\n\nfrom pydantic import BaseModel\n\n\nclass IDData(BaseModel):\n    id: int\n\n\nclass ShareItem(BaseModel):\n    expire_value: int\n    expire_style: str = \"day\"\n    filename: str\n\n\nclass DeleteItem(BaseModel):\n    filename: str\n\n\nclass LoginData(BaseModel):\n    password: str\n\n\nclass UpdateFileData(BaseModel):\n    id: int\n    code: Optional[str] = None\n    prefix: Optional[str] = None\n    suffix: Optional[str] = None\n    expired_at: Optional[Union[datetime.datetime, str]] = None\n    expired_count: Optional[int] = None\n"
  },
  {
    "path": "apps/admin/services.py",
    "content": "import os\nimport time\n\nfrom core.response import APIResponse\nfrom core.storage import FileStorageInterface, storages\nfrom core.settings import settings\nfrom apps.base.models import FileCodes, KeyValue, file_codes_pydantic\nfrom apps.base.utils import get_expire_info, get_file_path_name\nfrom fastapi import HTTPException\nfrom core.settings import data_root\nfrom core.utils import hash_password, is_password_hashed\n\n\nclass FileService:\n    def __init__(self):\n        self.file_storage: FileStorageInterface = storages[settings.file_storage]()\n\n    async def delete_file(self, file_id: int):\n        file_code = await FileCodes.get(id=file_id)\n        await self.file_storage.delete_file(file_code)\n        await file_code.delete()\n\n    async def list_files(self, page: int, size: int, keyword: str = \"\"):\n        offset = (page - 1) * size\n        files = (\n            await FileCodes.filter(prefix__icontains=keyword).limit(size).offset(offset)\n        )\n        total = await FileCodes.filter(prefix__icontains=keyword).count()\n        files_pydantic = [\n            await file_codes_pydantic.from_tortoise_orm(f) for f in files\n        ]\n        return files_pydantic, total\n\n    async def download_file(self, file_id: int):\n        file_code = await FileCodes.filter(id=file_id).first()\n        if not file_code:\n            raise HTTPException(status_code=404, detail=\"文件不存在\")\n        if file_code.text:\n            return APIResponse(detail=file_code.text)\n        else:\n            return await self.file_storage.get_file_response(file_code)\n\n    async def share_local_file(self, item):\n        local_file = LocalFileClass(item.filename)\n        if not await local_file.exists():\n            raise HTTPException(status_code=404, detail=\"文件不存在\")\n\n        text = await local_file.read()\n        expired_at, expired_count, used_count, code = await get_expire_info(\n            item.expire_value, item.expire_style\n        )\n        path, suffix, prefix, uuid_file_name, save_path = await get_file_path_name(item)\n\n        await self.file_storage.save_file(text, save_path)\n\n        await FileCodes.create(\n            code=code,\n            prefix=prefix,\n            suffix=suffix,\n            uuid_file_name=uuid_file_name,\n            file_path=path,\n            size=local_file.size,\n            expired_at=expired_at,\n            expired_count=expired_count,\n            used_count=used_count,\n        )\n\n        return {\n            \"code\": code,\n            \"name\": local_file.file,\n        }\n\n\nclass ConfigService:\n    def get_config(self):\n        return dict(settings.items())\n\n    async def update_config(self, data: dict):\n        admin_token = data.get(\"admin_token\")\n        if admin_token is None or admin_token == \"\":\n            raise HTTPException(status_code=400, detail=\"管理员密码不能为空\")\n\n        if not is_password_hashed(admin_token):\n            data[\"admin_token\"] = hash_password(admin_token)\n\n        for key, value in data.items():\n            if key not in settings.default_config:\n                continue\n            if key in [\n                \"errorCount\",\n                \"errorMinute\",\n                \"max_save_seconds\",\n                \"onedrive_proxy\",\n                \"openUpload\",\n                \"port\",\n                \"s3_proxy\",\n                \"uploadCount\",\n                \"uploadMinute\",\n                \"uploadSize\",\n            ]:\n                data[key] = int(value)\n            elif key in [\"opacity\"]:\n                data[key] = float(value)\n            else:\n                data[key] = value\n\n        await KeyValue.filter(key=\"settings\").update(value=data)\n        for k, v in data.items():\n            settings.__setattr__(k, v)\n\n\nclass LocalFileService:\n    async def list_files(self):\n        files = []\n        if not os.path.exists(data_root / \"local\"):\n            os.makedirs(data_root / \"local\")\n        for file in os.listdir(data_root / \"local\"):\n            local_file = LocalFileClass(file)\n            files.append({\n                \"file\": local_file.file,\n                \"ctime\": local_file.ctime,\n                \"size\": local_file.size,\n            })\n        return files\n\n    async def delete_file(self, filename: str):\n        file = LocalFileClass(filename)\n        if await file.exists():\n            await file.delete()\n            return \"删除成功\"\n        raise HTTPException(status_code=404, detail=\"文件不存在\")\n\n\nclass LocalFileClass:\n    def __init__(self, file):\n        self.file = file\n        self.path = data_root / \"local\" / file\n        if os.path.exists(self.path):\n            self.ctime = time.strftime(\n                \"%Y-%m-%d %H:%M:%S\", time.localtime(os.path.getctime(self.path))\n            )\n            self.size = os.path.getsize(self.path)\n        else:\n            self.ctime = None\n            self.size = None\n\n    async def read(self):\n        return open(self.path, \"rb\")\n\n    async def write(self, data):\n        with open(self.path, \"w\") as f:\n            f.write(data)\n\n    async def delete(self):\n        os.remove(self.path)\n\n    async def exists(self):\n        return os.path.exists(self.path)\n"
  },
  {
    "path": "apps/admin/views.py",
    "content": "# @Time    : 2023/8/14 14:38\n# @Author  : Lan\n# @File    : views.py\n# @Software: PyCharm\nimport datetime\n\nfrom fastapi import APIRouter, Depends, HTTPException\nfrom apps.admin.services import FileService, ConfigService, LocalFileService\nfrom apps.admin.dependencies import (\n    admin_required,\n    get_file_service,\n    get_config_service,\n    get_local_file_service,\n)\nfrom apps.admin.schemas import IDData, ShareItem, DeleteItem, LoginData, UpdateFileData\nfrom core.response import APIResponse\nfrom apps.base.models import FileCodes, KeyValue\nfrom apps.admin.dependencies import create_token\nfrom core.settings import settings\nfrom core.utils import get_now, verify_password\n\nadmin_api = APIRouter(\n    prefix=\"/admin\", tags=[\"管理\"], dependencies=[Depends(admin_required)]\n)\n\n\n@admin_api.post(\"/login\")\nasync def login(data: LoginData):\n    if not verify_password(data.password, settings.admin_token):\n        raise HTTPException(status_code=401, detail=\"密码错误\")\n\n    token = create_token({\"is_admin\": True})\n    return APIResponse(detail={\"token\": token, \"token_type\": \"Bearer\"})\n\n\n@admin_api.get(\"/dashboard\")\nasync def dashboard():\n    all_codes = await FileCodes.all()\n    all_size = str(sum([code.size for code in all_codes]))\n    sys_start = await KeyValue.filter(key=\"sys_start\").first()\n    now = await get_now()\n    today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)\n    yesterday_start = today_start - datetime.timedelta(days=1)\n    yesterday_end = today_start - datetime.timedelta(microseconds=1)\n    yesterday_codes = FileCodes.filter(\n        created_at__gte=yesterday_start, created_at__lte=yesterday_end\n    )\n    today_codes = FileCodes.filter(created_at__gte=today_start)\n    return APIResponse(\n        detail={\n            \"totalFiles\": len(all_codes),\n            \"storageUsed\": all_size,\n            \"sysUptime\": sys_start.value,\n            \"yesterdayCount\": await yesterday_codes.count(),\n            \"yesterdaySize\": str(sum([code.size for code in await yesterday_codes])),\n            \"todayCount\": await today_codes.count(),\n            \"todaySize\": str(sum([code.size for code in await today_codes])),\n        }\n    )\n\n\n@admin_api.delete(\"/file/delete\")\nasync def file_delete(\n    data: IDData,\n    file_service: FileService = Depends(get_file_service),\n):\n    await file_service.delete_file(data.id)\n    return APIResponse()\n\n\n@admin_api.get(\"/file/list\")\nasync def file_list(\n    page: int = 1,\n    size: int = 10,\n    keyword: str = \"\",\n    file_service: FileService = Depends(get_file_service),\n):\n    files, total = await file_service.list_files(page, size, keyword)\n    return APIResponse(\n        detail={\n            \"page\": page,\n            \"size\": size,\n            \"data\": files,\n            \"total\": total,\n        }\n    )\n\n\n@admin_api.get(\"/config/get\")\nasync def get_config(\n    config_service: ConfigService = Depends(get_config_service),\n):\n    return APIResponse(detail=config_service.get_config())\n\n\n@admin_api.patch(\"/config/update\")\nasync def update_config(\n    data: dict,\n    config_service: ConfigService = Depends(get_config_service),\n):\n    data.pop(\"themesChoices\")\n    await config_service.update_config(data)\n    return APIResponse()\n\n\n@admin_api.get(\"/file/download\")\nasync def file_download(\n    id: int,\n    file_service: FileService = Depends(get_file_service),\n):\n    file_content = await file_service.download_file(id)\n    return file_content\n\n\n@admin_api.get(\"/local/lists\")\nasync def get_local_lists(\n    local_file_service: LocalFileService = Depends(get_local_file_service),\n):\n    files = await local_file_service.list_files()\n    return APIResponse(detail=files)\n\n\n@admin_api.delete(\"/local/delete\")\nasync def delete_local_file(\n    item: DeleteItem,\n    local_file_service: LocalFileService = Depends(get_local_file_service),\n):\n    result = await local_file_service.delete_file(item.filename)\n    return APIResponse(detail=result)\n\n\n@admin_api.post(\"/local/share\")\nasync def share_local_file(\n    item: ShareItem,\n    file_service: FileService = Depends(get_file_service),\n):\n    share_info = await file_service.share_local_file(item)\n    return APIResponse(detail=share_info)\n\n\n@admin_api.patch(\"/file/update\")\nasync def update_file(\n    data: UpdateFileData,\n):\n    file_code = await FileCodes.filter(id=data.id).first()\n    if not file_code:\n        raise HTTPException(status_code=404, detail=\"文件不存在\")\n    update_data = {}\n\n    if data.code is not None and data.code != file_code.code:\n        # 判断code是否存在\n        if await FileCodes.filter(code=data.code).first():\n            raise HTTPException(status_code=400, detail=\"code已存在\")\n        update_data[\"code\"] = data.code\n    if data.prefix is not None and data.prefix != file_code.prefix:\n        update_data[\"prefix\"] = data.prefix\n    if data.suffix is not None and data.suffix != file_code.suffix:\n        update_data[\"suffix\"] = data.suffix\n    if (\n        data.expired_at is not None\n        and data.expired_at != \"\"\n        and data.expired_at != file_code.expired_at\n    ):\n        update_data[\"expired_at\"] = data.expired_at\n    if data.expired_count is not None and data.expired_count != file_code.expired_count:\n        update_data[\"expired_count\"] = data.expired_count\n\n    await file_code.update_from_dict(update_data).save()\n    return APIResponse(detail=\"更新成功\")\n"
  },
  {
    "path": "apps/base/__init__.py",
    "content": "# @Time    : 2023/8/13 20:43\n# @Author  : Lan\n# @File    : __init__.py.py\n# @Software: PyCharm\n"
  },
  {
    "path": "apps/base/dependencies.py",
    "content": "from typing import Dict, Union\nfrom datetime import datetime, timedelta\nfrom fastapi import HTTPException, Request\n\n\nclass IPRateLimit:\n    def __init__(self, count: int, minutes: int):\n        self.ips: Dict[str, Dict[str, Union[int, datetime]]] = {}\n        self.count = count\n        self.minutes = minutes\n\n    def check_ip(self, ip: str) -> bool:\n        if ip in self.ips:\n            ip_info = self.ips[ip]\n            if ip_info[\"count\"] >= self.count:\n                if ip_info[\"time\"] + timedelta(minutes=self.minutes) > datetime.now():\n                    return False\n                self.ips.pop(ip)\n        return True\n\n    def add_ip(self, ip: str) -> int:\n        ip_info = self.ips.get(ip, {\"count\": 0, \"time\": datetime.now()})\n        ip_info[\"count\"] += 1\n        ip_info[\"time\"] = datetime.now()\n        self.ips[ip] = ip_info\n        return ip_info[\"count\"]\n\n    async def remove_expired_ip(self) -> None:\n        now = datetime.now()\n        expiration = timedelta(minutes=self.minutes)\n        self.ips = {\n            ip: info\n            for ip, info in self.ips.items()\n            if info[\"time\"] + expiration >= now\n        }\n\n    def __call__(self, request: Request) -> str:\n        ip = (\n            request.headers.get(\"X-Real-IP\")\n            or request.headers.get(\"X-Forwarded-For\")\n            or request.client.host\n        )\n        if not self.check_ip(ip):\n            raise HTTPException(status_code=423, detail=\"请求次数过多，请稍后再试\")\n        return ip\n"
  },
  {
    "path": "apps/base/migrations/migrations_001.py",
    "content": "from tortoise import connections\n\n\nasync def create_file_codes_table():\n    conn = connections.get(\"default\")\n    await conn.execute_script(\n        \"\"\"\n        CREATE TABLE IF NOT EXISTS filecodes\n        (\n            id             INTEGER                                not null\n                primary key autoincrement,\n            code           VARCHAR(255)                           not null\n                unique,\n            prefix         VARCHAR(255) default ''                not null,\n            suffix         VARCHAR(255) default ''                not null,\n            uuid_file_name VARCHAR(255),\n            file_path      VARCHAR(255),\n            size           INT          default 0                 not null,\n            text           TEXT,\n            expired_at     TIMESTAMP,\n            expired_count  INT          default 0                 not null,\n            used_count     INT          default 0                 not null,\n            created_at     TIMESTAMP    default CURRENT_TIMESTAMP not null\n        );\n        CREATE INDEX IF NOT EXISTS idx_filecodes_code_1c7ee7\n            on filecodes (code);\n    \"\"\"\n    )\n\n\nasync def create_key_value_table():\n    conn = connections.get(\"default\")\n    await conn.execute_script(\n        \"\"\"\n        CREATE TABLE IF NOT EXISTS keyvalue\n        (\n            id         INTEGER                             not null\n                primary key autoincrement,\n            key        VARCHAR(255)                        not null\n                unique,\n            value      JSON,\n            created_at TIMESTAMP default CURRENT_TIMESTAMP not null\n        );\n        CREATE INDEX IF NOT EXISTS idx_keyvalue_key_eab890\n            on keyvalue (key);\n    \"\"\"\n    )\n\n\nasync def migrate():\n    await create_file_codes_table()\n    await create_key_value_table()\n"
  },
  {
    "path": "apps/base/migrations/migrations_002.py",
    "content": "from tortoise import connections\n\n\nasync def create_upload_chunk_and_update_file_codes_table():\n    conn = connections.get(\"default\")\n    await conn.execute_script(\n        \"\"\"\n        ALTER TABLE \"filecodes\" ADD \"file_hash\" VARCHAR(128);\n        ALTER TABLE \"filecodes\" ADD \"is_chunked\" BOOL NOT NULL DEFAULT False;\n        ALTER TABLE \"filecodes\" ADD \"upload_id\" VARCHAR(128);\n        CREATE TABLE \"uploadchunk\" (\n            id  INTEGER  not null   primary key autoincrement,\n            \"upload_id\" VARCHAR(36) NOT NULL,\n            \"chunk_index\" INT NOT NULL,\n            \"chunk_hash\" VARCHAR(128) NOT NULL,\n            \"total_chunks\" INT NOT NULL,\n            \"file_size\" BIGINT NOT NULL,\n            \"chunk_size\" INT NOT NULL,\n            \"created_at\" TIMESTAMPTZ NOT NULL,\n            \"file_name\" VARCHAR(255) NOT NULL,\n            \"completed\" BOOL NOT NULL\n        );\n    \"\"\"\n    )\n\n\nasync def migrate():\n    await create_upload_chunk_and_update_file_codes_table()\n"
  },
  {
    "path": "apps/base/migrations/migrations_003.py",
    "content": "from tortoise import connections\n\n\nasync def create_presign_upload_session_table():\n    conn = connections.get(\"default\")\n    await conn.execute_script(\n        \"\"\"\n        CREATE TABLE IF NOT EXISTS presignuploadsession (\n            id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n            upload_id VARCHAR(36) NOT NULL UNIQUE,\n            file_name VARCHAR(255) NOT NULL,\n            file_size BIGINT NOT NULL,\n            save_path VARCHAR(512) NOT NULL,\n            mode VARCHAR(10) NOT NULL,\n            expire_value INT NOT NULL DEFAULT 1,\n            expire_style VARCHAR(20) NOT NULL DEFAULT 'day',\n            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n            expires_at TIMESTAMP NOT NULL\n        );\n        CREATE INDEX IF NOT EXISTS idx_presignuploadsession_upload_id ON presignuploadsession (upload_id);\n        \"\"\"\n    )\n\n\nasync def migrate():\n    await create_presign_upload_session_table()\n"
  },
  {
    "path": "apps/base/migrations/migrations_004.py",
    "content": "from tortoise import connections\n\n\nasync def add_save_path_to_uploadchunk():\n    conn = connections.get(\"default\")\n    await conn.execute_script(\n        \"\"\"\n        ALTER TABLE uploadchunk ADD COLUMN save_path VARCHAR(512) NULL;\n        \"\"\"\n    )\n\n\nasync def migrate():\n    await add_save_path_to_uploadchunk()\n"
  },
  {
    "path": "apps/base/migrations/migrations_005.py",
    "content": "from tortoise import connections\n\n\ndef _need_upgrade(columns: list[tuple]) -> bool:\n    for column in columns:\n        # PRAGMA table_info 返回 (cid, name, type, notnull, dflt_value, pk)\n        if column[1] == \"size\":\n            column_type = (column[2] or \"\").upper()\n            return \"BIGINT\" not in column_type\n    return False\n\n\nasync def migrate():\n    conn = connections.get(\"default\")\n    result = await conn.execute_query(\"PRAGMA table_info(filecodes)\")\n    columns = result[1] if result and len(result) > 1 else []\n\n    if not columns or not _need_upgrade(columns):\n        return\n\n    await conn.execute_script(\n        \"\"\"\n        BEGIN;\n        CREATE TABLE IF NOT EXISTS filecodes_new\n        (\n            id             INTEGER                                not null\n                primary key autoincrement,\n            code           VARCHAR(255)                           not null\n                unique,\n            prefix         VARCHAR(255) default ''                not null,\n            suffix         VARCHAR(255) default ''                not null,\n            uuid_file_name VARCHAR(255),\n            file_path      VARCHAR(255),\n            size           BIGINT       default 0                 not null,\n            text           TEXT,\n            expired_at     TIMESTAMP,\n            expired_count  INT          default 0                 not null,\n            used_count     INT          default 0                 not null,\n            created_at     TIMESTAMP    default CURRENT_TIMESTAMP not null,\n            file_hash      VARCHAR(128),\n            is_chunked     BOOL         default False             not null,\n            upload_id      VARCHAR(128)\n        );\n\n        INSERT INTO filecodes_new (id, code, prefix, suffix, uuid_file_name, file_path, size, text,\n                                   expired_at, expired_count, used_count, created_at, file_hash,\n                                   is_chunked, upload_id)\n        SELECT id,\n               code,\n               prefix,\n               suffix,\n               uuid_file_name,\n               file_path,\n               size,\n               text,\n               expired_at,\n               expired_count,\n               used_count,\n               created_at,\n               file_hash,\n               is_chunked,\n               upload_id\n        FROM filecodes;\n\n        DROP TABLE filecodes;\n        ALTER TABLE filecodes_new\n            RENAME TO filecodes;\n        CREATE INDEX IF NOT EXISTS idx_filecodes_code_1c7ee7\n            on filecodes (code);\n        COMMIT;\n        \"\"\"\n    )\n"
  },
  {
    "path": "apps/base/models.py",
    "content": "# @Time    : 2023/8/13 20:43\n# @Author  : Lan\n# @File    : models.py\n# @Software: PyCharm\nfrom typing import Optional\n\nfrom tortoise.models import Model\nfrom tortoise.contrib.pydantic import pydantic_model_creator\n\nfrom tortoise import fields, models\nfrom datetime import datetime\nfrom core.utils import get_now\n\n\nclass FileCodes(models.Model):\n    id = fields.IntField(pk=True)\n    code = fields.CharField(max_length=255, unique=True, index=True)\n    prefix = fields.CharField(max_length=255, default=\"\")\n    suffix = fields.CharField(max_length=255, default=\"\")\n    uuid_file_name = fields.CharField(max_length=255, null=True)\n    file_path = fields.CharField(max_length=255, null=True)\n    size = fields.BigIntField(default=0)\n    text = fields.TextField(null=True)\n    expired_at = fields.DatetimeField(null=True)\n    expired_count = fields.IntField(default=0)\n    used_count = fields.IntField(default=0)\n    created_at = fields.DatetimeField(auto_now_add=True)\n    file_hash = fields.CharField(max_length=64, null=True)\n    is_chunked = fields.BooleanField(default=False)\n    upload_id = fields.CharField(max_length=36, null=True)\n\n    async def is_expired(self):\n        if self.expired_at is None:\n            return False\n        if self.expired_at and self.expired_count < 0:\n            return self.expired_at < await get_now()\n        return self.expired_count <= 0\n\n    async def get_file_path(self):\n        return f\"{self.file_path}/{self.uuid_file_name}\"\n\n\nclass UploadChunk(models.Model):\n    id = fields.IntField(pk=True)\n    upload_id = fields.CharField(max_length=36, index=True)\n    chunk_index = fields.IntField()\n    chunk_hash = fields.CharField(max_length=64)\n    total_chunks = fields.IntField()\n    file_size = fields.BigIntField()\n    chunk_size = fields.IntField()\n    file_name = fields.CharField(max_length=255)\n    save_path = fields.CharField(max_length=512, null=True)\n    created_at = fields.DatetimeField(auto_now_add=True)\n    completed = fields.BooleanField(default=False)\n\n\nclass KeyValue(Model):\n    id: Optional[int] = fields.IntField(pk=True)\n    key: Optional[str] = fields.CharField(\n        max_length=255, description=\"键\", index=True, unique=True\n    )\n    value: Optional[str] = fields.JSONField(description=\"值\", null=True)\n    created_at: Optional[datetime] = fields.DatetimeField(\n        auto_now_add=True, description=\"创建时间\"\n    )\n\n\nclass PresignUploadSession(models.Model):\n    \"\"\"预签名上传会话模型\"\"\"\n\n    id = fields.IntField(pk=True)\n    upload_id = fields.CharField(max_length=36, unique=True, index=True)\n    file_name = fields.CharField(max_length=255)\n    file_size = fields.BigIntField()\n    save_path = fields.CharField(max_length=512)\n    mode = fields.CharField(max_length=10)  # \"direct\" 或 \"proxy\"\n    expire_value = fields.IntField(default=1)\n    expire_style = fields.CharField(max_length=20, default=\"day\")\n    created_at = fields.DatetimeField(auto_now_add=True)\n    expires_at = fields.DatetimeField()  # 会话过期时间\n\n    async def is_expired(self):\n        \"\"\"检查会话是否已过期\"\"\"\n        return self.expires_at < await get_now()\n\n\nfile_codes_pydantic = pydantic_model_creator(FileCodes, name=\"FileCodes\")\nupload_chunk_pydantic = pydantic_model_creator(UploadChunk, name=\"UploadChunk\")\nkey_value_pydantic = pydantic_model_creator(KeyValue, name=\"KeyValue\")\npresign_upload_session_pydantic = pydantic_model_creator(\n    PresignUploadSession, name=\"PresignUploadSession\"\n)\n"
  },
  {
    "path": "apps/base/schemas.py",
    "content": "from pydantic import BaseModel\nfrom typing import Optional\n\n\nclass SelectFileModel(BaseModel):\n    code: str\n\n\nclass InitChunkUploadModel(BaseModel):\n    file_name: str\n    chunk_size: int = 5 * 1024 * 1024\n    file_size: int\n    file_hash: str\n\n\nclass CompleteUploadModel(BaseModel):\n    expire_value: int\n    expire_style: str\n\n\n# 预签名上传相关模型\nclass PresignUploadInitRequest(BaseModel):\n    \"\"\"预签名上传初始化请求\"\"\"\n    file_name: str\n    file_size: int\n    expire_value: int = 1\n    expire_style: str = \"day\"\n\n\nclass PresignUploadInitResponse(BaseModel):\n    \"\"\"预签名上传初始化响应\"\"\"\n    upload_id: str\n    upload_url: str\n    mode: str  # \"direct\" 或 \"proxy\"\n    save_path: str\n    expires_in: int  # URL过期时间（秒）\n"
  },
  {
    "path": "apps/base/utils.py",
    "content": "import datetime\nimport hashlib\nimport os\nimport uuid\nfrom urllib.parse import unquote\n\nfrom fastapi import UploadFile, HTTPException\nfrom typing import Optional, Tuple\n\nfrom apps.base.dependencies import IPRateLimit\nfrom apps.base.models import FileCodes\nfrom core.settings import settings\nfrom core.utils import (\n    get_random_num,\n    get_random_string,\n    max_save_times_desc,\n    sanitize_filename,\n    get_now,\n)\n\n\nasync def get_file_path_name(file: UploadFile) -> Tuple[str, str, str, str, str]:\n    today = await get_now()\n    storage_path = settings.storage_path.strip(\"/\")\n    file_uuid = uuid.uuid4().hex\n    filename = await sanitize_filename(unquote(file.filename or \"\"))\n    base_path = f\"share/data/{today.strftime('%Y/%m/%d')}/{file_uuid}\"\n    path = f\"{storage_path}/{base_path}\" if storage_path else base_path\n    prefix, suffix = os.path.splitext(filename)\n    save_path = f\"{path}/{filename}\"\n    return path, suffix, prefix, filename, save_path\n\n\nasync def get_chunk_file_path_name(\n    file_name: str, upload_id: str\n) -> Tuple[str, str, str, str, str]:\n    today = await get_now()\n    storage_path = settings.storage_path.strip(\"/\")\n    base_path = f\"share/data/{today.strftime('%Y/%m/%d')}/{upload_id}\"\n    path = f\"{storage_path}/{base_path}\" if storage_path else base_path\n    prefix, suffix = os.path.splitext(file_name)\n    save_path = f\"{path}/{prefix}{suffix}\"\n    return path, suffix, prefix, file_name, save_path\n\n\nasync def get_expire_info(\n    expire_value: int, expire_style: str\n) -> Tuple[Optional[datetime.datetime], int, int, str]:\n    expired_count, used_count = -1, 0\n    now = await get_now()\n    code = None\n\n    max_timedelta = (\n        datetime.timedelta(seconds=settings.max_save_seconds)\n        if settings.max_save_seconds > 0\n        else datetime.timedelta(days=7)\n    )\n    detail = (\n        await max_save_times_desc(settings.max_save_seconds)\n        if settings.max_save_seconds > 0\n        else \"7天\"\n    )\n    detail = f\"限制最长时间为 {detail[0]}，可换用其他方式\"\n\n    expire_styles = {\n        \"day\": lambda: now + datetime.timedelta(days=expire_value),\n        \"hour\": lambda: now + datetime.timedelta(hours=expire_value),\n        \"minute\": lambda: now + datetime.timedelta(minutes=expire_value),\n        \"count\": lambda: (now + datetime.timedelta(days=1), expire_value),\n        \"forever\": lambda: (None, None),\n    }\n\n    if expire_style in expire_styles:\n        result = expire_styles[expire_style]()\n        if isinstance(result, tuple):\n            expired_at, extra = result\n            if expire_style == \"count\":\n                expired_count = extra\n            elif expire_style == \"forever\":\n                code = await get_random_code(style=\"string\")\n        else:\n            expired_at = result\n        if expired_at and expired_at - now > max_timedelta:\n            raise HTTPException(status_code=403, detail=detail)\n    else:\n        expired_at = now + datetime.timedelta(days=1)\n\n    if not code:\n        code = await get_random_code()\n\n    return expired_at, expired_count, used_count, code\n\n\nasync def get_random_code(style: str = \"num\") -> str:\n    while True:\n        code = await get_random_num() if style == \"num\" else await get_random_string()\n        if not await FileCodes.filter(code=code).exists():\n            return str(code)\n\n\nasync def calculate_file_hash(file: UploadFile, chunk_size=1024 * 1024) -> str:\n    sha = hashlib.sha256()\n    await file.seek(0)\n    while True:\n        chunk = await file.read(chunk_size)\n        if not chunk:\n            break\n        sha.update(chunk)\n    await file.seek(0)\n    return sha.hexdigest()\n\n\nip_limit = {\n    \"error\": IPRateLimit(count=settings.errorCount, minutes=settings.errorMinute),\n    \"upload\": IPRateLimit(count=settings.uploadCount, minutes=settings.uploadMinute),\n}\n"
  },
  {
    "path": "apps/base/views.py",
    "content": "import datetime\nimport hashlib\nimport os\nimport uuid\nfrom datetime import timedelta\nfrom urllib.parse import unquote\n\nfrom typing import Optional, Tuple, Union\n\nfrom fastapi import APIRouter, Form, UploadFile, File, Depends, HTTPException\nfrom starlette import status\n\nfrom apps.admin.dependencies import share_required_login\nfrom apps.base.models import FileCodes, UploadChunk, PresignUploadSession\nfrom apps.base.schemas import (\n    SelectFileModel,\n    InitChunkUploadModel,\n    CompleteUploadModel,\n    PresignUploadInitRequest,\n)\nfrom apps.base.utils import (\n    get_expire_info,\n    get_file_path_name,\n    ip_limit,\n    get_chunk_file_path_name,\n)\nfrom core.response import APIResponse\nfrom core.settings import settings\nfrom core.storage import storages, FileStorageInterface\nfrom core.utils import get_select_token, get_now, sanitize_filename\n\nshare_api = APIRouter(prefix=\"/share\", tags=[\"分享\"])\n\n\n# ============ 公共服务层 ============\nclass FileUploadService:\n    \"\"\"统一的文件上传服务\"\"\"\n\n    @staticmethod\n    async def generate_file_path(\n        file_name: str, upload_id: Optional[str] = None\n    ) -> tuple[str, str, str, str, str]:\n        \"\"\"统一的路径生成\"\"\"\n        today = datetime.datetime.now()\n        storage_path = settings.storage_path.strip(\"/\")\n        file_uuid = upload_id or uuid.uuid4().hex\n        filename = await sanitize_filename(unquote(file_name))\n        base_path = f\"share/data/{today.strftime('%Y/%m/%d')}/{file_uuid}\"\n        path = f\"{storage_path}/{base_path}\" if storage_path else base_path\n        prefix, suffix = os.path.splitext(filename)\n        save_path = f\"{path}/{filename}\"\n        return path, suffix, prefix, filename, save_path\n\n    @staticmethod\n    async def create_file_record(\n        file_name: str,\n        file_size: int,\n        file_path: str,\n        expire_value: int,\n        expire_style: str,\n        **extra_fields,\n    ) -> str:\n        \"\"\"统一创建FileCodes记录，返回code\"\"\"\n        expired_at, expired_count, used_count, code = await get_expire_info(\n            expire_value, expire_style\n        )\n        prefix, suffix = os.path.splitext(file_name)\n\n        await FileCodes.create(\n            code=code,\n            prefix=prefix,\n            suffix=suffix,\n            uuid_file_name=file_name,\n            file_path=file_path,\n            size=file_size,\n            expired_at=expired_at,\n            expired_count=expired_count,\n            used_count=used_count,\n            **extra_fields,\n        )\n        return code\n\n\nasync def validate_file_size(file: UploadFile, max_size: int) -> int:\n    size = file.size\n    if size is None:\n        await file.seek(0, 2)  # type: ignore[arg-type]\n        size = file.file.tell()\n        await file.seek(0)\n    if size > max_size:\n        max_size_mb = max_size / (1024 * 1024)\n        raise HTTPException(\n            status_code=403, detail=f\"大小超过限制,最大为{max_size_mb:.2f} MB\"\n        )\n    return size\n\n\nasync def create_file_code(code, **kwargs):\n    return await FileCodes.create(code=code, **kwargs)\n\n\n@share_api.post(\"/text/\", dependencies=[Depends(share_required_login)])\nasync def share_text(\n    text: str = Form(...),\n    expire_value: int = Form(default=1, gt=0),\n    expire_style: str = Form(default=\"day\"),\n    ip: str = Depends(ip_limit[\"upload\"]),\n):\n    text_size = len(text.encode(\"utf-8\"))\n    max_txt_size = 222 * 1024\n    if text_size > max_txt_size:\n        raise HTTPException(status_code=403, detail=\"内容过多,建议采用文件形式\")\n\n    expired_at, expired_count, used_count, code = await get_expire_info(\n        expire_value, expire_style\n    )\n    await create_file_code(\n        code=code,\n        text=text,\n        expired_at=expired_at,\n        expired_count=expired_count,\n        used_count=used_count,\n        size=len(text),\n        prefix=\"Text\",\n    )\n    ip_limit[\"upload\"].add_ip(ip)\n    return APIResponse(detail={\"code\": code})\n\n\n@share_api.post(\"/file/\", dependencies=[Depends(share_required_login)])\nasync def share_file(\n    expire_value: int = Form(default=1, gt=0),\n    expire_style: str = Form(default=\"day\"),\n    file: UploadFile = File(...),\n    ip: str = Depends(ip_limit[\"upload\"]),\n):\n    file_size = await validate_file_size(file, settings.uploadSize)\n    if expire_style not in settings.expireStyle:\n        raise HTTPException(status_code=400, detail=\"过期时间类型错误\")\n    expired_at, expired_count, used_count, code = await get_expire_info(\n        expire_value, expire_style\n    )\n    path, suffix, prefix, uuid_file_name, save_path = await get_file_path_name(file)\n    file_storage: FileStorageInterface = storages[settings.file_storage]()\n    await file_storage.save_file(file, save_path)\n    await create_file_code(\n        code=code,\n        prefix=prefix,\n        suffix=suffix,\n        uuid_file_name=uuid_file_name,\n        file_path=path,\n        size=file_size,\n        expired_at=expired_at,\n        expired_count=expired_count,\n        used_count=used_count,\n    )\n    ip_limit[\"upload\"].add_ip(ip)\n    return APIResponse(detail={\"code\": code, \"name\": file.filename})\n\n\nasync def get_code_file_by_code(\n    code: str, check: bool = True\n) -> Tuple[bool, Union[FileCodes, str]]:\n    file_code = await FileCodes.filter(code=code).first()\n    if not file_code:\n        return False, \"文件不存在\"\n    if await file_code.is_expired() and check:\n        return False, \"文件已过期\"\n    return True, file_code\n\n\nasync def update_file_usage(file_code: FileCodes) -> None:\n    file_code.used_count += 1\n    if file_code.expired_count > 0:\n        file_code.expired_count -= 1\n    await file_code.save()\n\n\n@share_api.get(\"/select/\")\nasync def get_code_file(code: str, ip: str = Depends(ip_limit[\"error\"])):\n    file_storage: FileStorageInterface = storages[settings.file_storage]()\n    has, file_code = await get_code_file_by_code(code)\n    if not has:\n        ip_limit[\"error\"].add_ip(ip)\n        return APIResponse(code=404, detail=file_code)\n\n    assert isinstance(file_code, FileCodes)\n    await update_file_usage(file_code)\n    return await file_storage.get_file_response(file_code)\n\n\n@share_api.post(\"/select/\")\nasync def select_file(data: SelectFileModel, ip: str = Depends(ip_limit[\"error\"])):\n    file_storage: FileStorageInterface = storages[settings.file_storage]()\n    has, file_code = await get_code_file_by_code(data.code)\n    if not has:\n        ip_limit[\"error\"].add_ip(ip)\n        return APIResponse(code=404, detail=file_code)\n\n    assert isinstance(file_code, FileCodes)\n    await update_file_usage(file_code)\n    return APIResponse(\n        detail={\n            \"code\": file_code.code,\n            \"name\": file_code.prefix + file_code.suffix,\n            \"size\": file_code.size,\n            \"text\": (\n                file_code.text\n                if file_code.text is not None\n                else await file_storage.get_file_url(file_code)\n            ),\n        }\n    )\n\n\n@share_api.get(\"/download\")\nasync def download_file(key: str, code: str, ip: str = Depends(ip_limit[\"error\"])):\n    file_storage: FileStorageInterface = storages[settings.file_storage]()\n    if await get_select_token(code) != key:\n        ip_limit[\"error\"].add_ip(ip)\n        raise HTTPException(status_code=403, detail=\"下载鉴权失败\")\n    has, file_code = await get_code_file_by_code(code, False)\n    if not has:\n        return APIResponse(code=404, detail=\"文件不存在\")\n    assert isinstance(file_code, FileCodes)\n    return (\n        APIResponse(detail=file_code.text)\n        if file_code.text\n        else await file_storage.get_file_response(file_code)\n    )\n\n\nchunk_api = APIRouter(prefix=\"/chunk\", tags=[\"切片\"])\n\n\n@chunk_api.post(\"/upload/init/\", dependencies=[Depends(share_required_login)])\nasync def init_chunk_upload(data: InitChunkUploadModel):\n    # 服务端校验：根据 total_chunks * chunk_size 计算理论最大上传量\n    total_chunks = (data.file_size + data.chunk_size - 1) // data.chunk_size\n    max_possible_size = total_chunks * data.chunk_size\n    if max_possible_size > settings.uploadSize:\n        max_size_mb = settings.uploadSize / (1024 * 1024)\n        raise HTTPException(\n            status_code=403, detail=f\"文件大小超过限制，最大为 {max_size_mb:.2f} MB\"\n        )\n\n    # # 秒传检查\n    # existing = await FileCodes.filter(file_hash=data.file_hash).first()\n    # if existing:\n    #     if await existing.is_expired():\n    #         file_storage: FileStorageInterface = storages[settings.file_storage](\n    #         )\n    #         await file_storage.delete_file(existing)\n    #         await existing.delete()\n    #     else:\n    #         return APIResponse(detail={\n    #             \"code\": existing.code,\n    #             \"existed\": True,\n    #             \"name\": f'{existing.prefix}{existing.suffix}'\n    #         })\n\n    # 断点续传：检查是否存在相同文件的未完成上传会话\n    existing_session = await UploadChunk.filter(\n        chunk_hash=data.file_hash,\n        chunk_index=-1,\n        file_size=data.file_size,\n        file_name=data.file_name,\n    ).first()\n\n    if existing_session:\n        if not existing_session.save_path:\n            await UploadChunk.filter(upload_id=existing_session.upload_id).delete()\n        else:\n            uploaded_chunks = await UploadChunk.filter(\n                upload_id=existing_session.upload_id, completed=True\n            ).values_list(\"chunk_index\", flat=True)\n            return APIResponse(\n                detail={\n                    \"existed\": False,\n                    \"upload_id\": existing_session.upload_id,\n                    \"chunk_size\": existing_session.chunk_size,\n                    \"total_chunks\": existing_session.total_chunks,\n                    \"uploaded_chunks\": list(uploaded_chunks),\n                }\n            )\n\n    # 创建新的上传会话\n    upload_id = uuid.uuid4().hex\n    _, _, _, _, save_path = await get_chunk_file_path_name(data.file_name, upload_id)\n    await UploadChunk.create(\n        upload_id=upload_id,\n        chunk_index=-1,\n        total_chunks=total_chunks,\n        file_size=data.file_size,\n        chunk_size=data.chunk_size,\n        chunk_hash=data.file_hash,\n        file_name=data.file_name,\n        save_path=save_path,\n    )\n    return APIResponse(\n        detail={\n            \"existed\": False,\n            \"upload_id\": upload_id,\n            \"chunk_size\": data.chunk_size,\n            \"total_chunks\": total_chunks,\n            \"uploaded_chunks\": [],\n        }\n    )\n\n\n@chunk_api.post(\n    \"/upload/chunk/{upload_id}/{chunk_index}\",\n    dependencies=[Depends(share_required_login)],\n)\nasync def upload_chunk(\n    upload_id: str,\n    chunk_index: int,\n    chunk: UploadFile = File(...),\n):\n    # 获取上传会话信息\n    chunk_info = await UploadChunk.filter(upload_id=upload_id, chunk_index=-1).first()\n    if not chunk_info:\n        raise HTTPException(status.HTTP_404_NOT_FOUND, detail=\"上传会话不存在\")\n\n    # 检查分片索引有效性\n    if chunk_index < 0 or chunk_index >= chunk_info.total_chunks:\n        raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=\"无效的分片索引\")\n\n    # 检查是否已上传（支持断点续传）\n    existing_chunk = await UploadChunk.filter(\n        upload_id=upload_id, chunk_index=chunk_index, completed=True\n    ).first()\n    if existing_chunk:\n        return APIResponse(\n            detail={\"chunk_hash\": existing_chunk.chunk_hash, \"skipped\": True}\n        )\n\n    # 读取分片数据并计算哈希\n    chunk_data = await chunk.read()\n    chunk_size = len(chunk_data)\n\n    # 校验分片大小不超过声明的 chunk_size\n    if chunk_size > chunk_info.chunk_size:\n        raise HTTPException(\n            status.HTTP_400_BAD_REQUEST,\n            detail=f\"分片大小超过声明值: 最大 {chunk_info.chunk_size}, 实际 {chunk_size}\",\n        )\n\n    # 计算已上传分片数，校验累计大小不超限（用分片数 * chunk_size 估算）\n    uploaded_count = await UploadChunk.filter(\n        upload_id=upload_id, completed=True\n    ).count()\n    # 已上传分片的最大可能大小 + 当前分片\n    max_uploaded_size = uploaded_count * chunk_info.chunk_size + chunk_size\n    if max_uploaded_size > settings.uploadSize:\n        max_size_mb = settings.uploadSize / (1024 * 1024)\n        raise HTTPException(\n            status_code=403, detail=f\"累计上传大小超过限制，最大为 {max_size_mb:.2f} MB\"\n        )\n\n    chunk_hash = hashlib.sha256(chunk_data).hexdigest()\n\n    save_path = chunk_info.save_path\n\n    # 保存分片到存储\n    storage = storages[settings.file_storage]()\n    try:\n        await storage.save_chunk(\n            upload_id, chunk_index, chunk_data, chunk_hash, save_path\n        )\n    except Exception as e:\n        raise HTTPException(\n            status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f\"分片保存失败: {str(e)}\"\n        )\n\n    # 更新或创建分片记录（保存成功后再记录）\n    await UploadChunk.update_or_create(\n        upload_id=upload_id,\n        chunk_index=chunk_index,\n        defaults={\n            \"chunk_hash\": chunk_hash,\n            \"completed\": True,\n            \"file_size\": chunk_info.file_size,\n            \"total_chunks\": chunk_info.total_chunks,\n            \"chunk_size\": chunk_info.chunk_size,\n            \"file_name\": chunk_info.file_name,\n            \"save_path\": chunk_info.save_path,\n        },\n    )\n    return APIResponse(detail={\"chunk_hash\": chunk_hash})\n\n\n@chunk_api.delete(\"/upload/{upload_id}\", dependencies=[Depends(share_required_login)])\nasync def cancel_upload(upload_id: str):\n    \"\"\"取消上传并清理临时文件\"\"\"\n    chunk_info = await UploadChunk.filter(upload_id=upload_id, chunk_index=-1).first()\n    if not chunk_info:\n        raise HTTPException(status.HTTP_404_NOT_FOUND, detail=\"上传会话不存在\")\n\n    save_path = chunk_info.save_path\n\n    # 清理存储中的临时文件\n    storage = storages[settings.file_storage]()\n    if save_path:\n        try:\n            await storage.clean_chunks(upload_id, save_path)\n        except Exception as e:\n            pass\n\n    # 清理数据库记录\n    await UploadChunk.filter(upload_id=upload_id).delete()\n\n    return APIResponse(detail={\"message\": \"上传已取消\"})\n\n\n@chunk_api.get(\n    \"/upload/status/{upload_id}\", dependencies=[Depends(share_required_login)]\n)\nasync def get_upload_status(upload_id: str):\n    \"\"\"获取上传状态\"\"\"\n    chunk_info = await UploadChunk.filter(upload_id=upload_id, chunk_index=-1).first()\n    if not chunk_info:\n        raise HTTPException(status.HTTP_404_NOT_FOUND, detail=\"上传会话不存在\")\n\n    # 获取已上传的分片列表\n    uploaded_chunks = await UploadChunk.filter(\n        upload_id=upload_id, completed=True\n    ).values_list(\"chunk_index\", flat=True)\n\n    return APIResponse(\n        detail={\n            \"upload_id\": upload_id,\n            \"file_name\": chunk_info.file_name,\n            \"file_size\": chunk_info.file_size,\n            \"chunk_size\": chunk_info.chunk_size,\n            \"total_chunks\": chunk_info.total_chunks,\n            \"uploaded_chunks\": list(uploaded_chunks),\n            \"progress\": len(uploaded_chunks) / chunk_info.total_chunks * 100,\n        }\n    )\n\n\n@chunk_api.post(\n    \"/upload/complete/{upload_id}\", dependencies=[Depends(share_required_login)]\n)\nasync def complete_upload(\n    upload_id: str, data: CompleteUploadModel, ip: str = Depends(ip_limit[\"upload\"])\n):\n    # 获取上传基本信息\n    chunk_info = await UploadChunk.filter(upload_id=upload_id, chunk_index=-1).first()\n    if not chunk_info:\n        raise HTTPException(status.HTTP_404_NOT_FOUND, detail=\"上传会话不存在\")\n\n    storage = storages[settings.file_storage]()\n    # 验证所有分片\n    completed_chunks_list = await UploadChunk.filter(\n        upload_id=upload_id, completed=True\n    ).all()\n    if len(completed_chunks_list) != chunk_info.total_chunks:\n        raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=\"分片不完整\")\n\n    # 用分片数 * chunk_size 校验最大可能大小\n    max_total_size = len(completed_chunks_list) * chunk_info.chunk_size\n    if max_total_size > settings.uploadSize:\n        save_path = chunk_info.save_path\n        if save_path:\n            try:\n                await storage.clean_chunks(upload_id, save_path)\n            except Exception:\n                pass\n        await UploadChunk.filter(upload_id=upload_id).delete()\n        max_size_mb = settings.uploadSize / (1024 * 1024)\n        raise HTTPException(\n            status_code=403, detail=f\"实际上传大小超过限制，最大为 {max_size_mb:.2f} MB\"\n        )\n\n    save_path = chunk_info.save_path\n    path = os.path.dirname(save_path) if save_path else \"\"\n    prefix, suffix = os.path.splitext(chunk_info.file_name)\n\n    try:\n        # 合并文件并计算哈希\n        _, file_hash = await storage.merge_chunks(upload_id, chunk_info, save_path)\n        # 创建文件记录\n        expired_at, expired_count, used_count, code = await get_expire_info(\n            data.expire_value, data.expire_style\n        )\n        await FileCodes.create(\n            code=code,\n            file_hash=file_hash,  # 使用合并后计算的哈希\n            is_chunked=True,\n            upload_id=upload_id,\n            size=chunk_info.file_size,\n            expired_at=expired_at,\n            expired_count=expired_count,\n            used_count=used_count,\n            file_path=path,\n            uuid_file_name=f\"{prefix}{suffix}\",\n            prefix=prefix,\n            suffix=suffix,\n        )\n        # 清理临时文件\n        await storage.clean_chunks(upload_id, save_path)\n        # 清理数据库中的分片记录\n        await UploadChunk.filter(upload_id=upload_id).delete()\n        ip_limit[\"upload\"].add_ip(ip)\n        return APIResponse(detail={\"code\": code, \"name\": chunk_info.file_name})\n    except ValueError as e:\n        raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(e))\n    except Exception as e:\n        # 合并失败时清理临时文件\n        try:\n            await storage.clean_chunks(upload_id, save_path)\n        except Exception:\n            pass\n        raise HTTPException(\n            status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f\"文件合并失败: {str(e)}\"\n        )\n\n\n# ============ 预签名上传API ============\npresign_api = APIRouter(prefix=\"/presign\", tags=[\"预签名上传\"])\n\nPRESIGN_SESSION_EXPIRES = 900  # 15分钟\n\n\nasync def _get_valid_session(\n    upload_id: str, expected_mode: Optional[str] = None\n) -> PresignUploadSession:\n    \"\"\"获取并验证会话\"\"\"\n    session = await PresignUploadSession.filter(upload_id=upload_id).first()\n    if not session:\n        raise HTTPException(404, \"上传会话不存在\")\n    if await session.is_expired():\n        await session.delete()\n        raise HTTPException(404, \"上传会话已过期\")\n    if expected_mode and session.mode != expected_mode:\n        raise HTTPException(400, f\"此会话不支持{expected_mode}模式\")\n    return session\n\n\n@presign_api.post(\"/upload/init\", dependencies=[Depends(share_required_login)])\nasync def presign_upload_init(\n    data: PresignUploadInitRequest, ip: str = Depends(ip_limit[\"upload\"])\n):\n    \"\"\"初始化预签名上传，S3返回直传URL，其他存储返回代理URL\"\"\"\n    if data.file_size > settings.uploadSize:\n        raise HTTPException(\n            403,\n            f\"文件大小超过限制，最大为 {settings.uploadSize / (1024 * 1024):.2f} MB\",\n        )\n    if data.expire_style not in settings.expireStyle:\n        raise HTTPException(400, \"过期时间类型错误\")\n\n    upload_id = uuid.uuid4().hex\n    path, _, _, filename, save_path = await FileUploadService.generate_file_path(\n        data.file_name, upload_id\n    )\n\n    storage: FileStorageInterface = storages[settings.file_storage]()\n    presigned_url = await storage.generate_presigned_upload_url(\n        save_path, PRESIGN_SESSION_EXPIRES\n    )\n\n    mode = \"direct\" if presigned_url else \"proxy\"\n    upload_url = presigned_url or f\"/api/presign/upload/proxy/{upload_id}\"\n\n    await PresignUploadSession.create(\n        upload_id=upload_id,\n        file_name=filename,\n        file_size=data.file_size,\n        save_path=save_path,\n        mode=mode,\n        expire_value=data.expire_value,\n        expire_style=data.expire_style,\n        expires_at=await get_now() + timedelta(seconds=PRESIGN_SESSION_EXPIRES),\n    )\n\n    ip_limit[\"upload\"].add_ip(ip)\n    return APIResponse(\n        detail={\n            \"upload_id\": upload_id,\n            \"upload_url\": upload_url,\n            \"mode\": mode,\n            \"expires_in\": PRESIGN_SESSION_EXPIRES,\n        }\n    )\n\n\n@presign_api.put(\n    \"/upload/proxy/{upload_id}\", dependencies=[Depends(share_required_login)]\n)\nasync def presign_upload_proxy(\n    upload_id: str, file: UploadFile = File(...), ip: str = Depends(ip_limit[\"upload\"])\n):\n    \"\"\"代理模式上传，服务器转存到存储后端\"\"\"\n    session = await _get_valid_session(upload_id, expected_mode=\"proxy\")\n\n    file_size = await validate_file_size(file, settings.uploadSize)\n    if abs(file_size - session.file_size) > 1024:\n        raise HTTPException(400, \"文件大小与声明不符\")\n\n    storage: FileStorageInterface = storages[settings.file_storage]()\n    try:\n        await storage.save_file(file, session.save_path)\n    except Exception as e:\n        raise HTTPException(500, f\"文件保存失败: {str(e)}\")\n\n    code = await FileUploadService.create_file_record(\n        session.file_name,\n        file_size,\n        os.path.dirname(session.save_path),\n        session.expire_value,\n        session.expire_style,\n    )\n\n    await session.delete()\n    ip_limit[\"upload\"].add_ip(ip)\n    return APIResponse(detail={\"code\": code, \"name\": session.file_name})\n\n\n@presign_api.post(\n    \"/upload/confirm/{upload_id}\", dependencies=[Depends(share_required_login)]\n)\nasync def presign_upload_confirm(upload_id: str, ip: str = Depends(ip_limit[\"upload\"])):\n    \"\"\"直传确认，客户端完成S3直传后调用获取分享码\"\"\"\n    session = await _get_valid_session(upload_id, expected_mode=\"direct\")\n\n    storage: FileStorageInterface = storages[settings.file_storage]()\n    if not await storage.file_exists(session.save_path):\n        raise HTTPException(404, \"文件未上传或上传失败\")\n\n    code = await FileUploadService.create_file_record(\n        session.file_name,\n        session.file_size,\n        os.path.dirname(session.save_path),\n        session.expire_value,\n        session.expire_style,\n    )\n\n    await session.delete()\n    ip_limit[\"upload\"].add_ip(ip)\n    return APIResponse(detail={\"code\": code, \"name\": session.file_name})\n\n\n@presign_api.get(\n    \"/upload/status/{upload_id}\", dependencies=[Depends(share_required_login)]\n)\nasync def presign_upload_status(upload_id: str):\n    \"\"\"查询上传会话状态\"\"\"\n    session = await PresignUploadSession.filter(upload_id=upload_id).first()\n    if not session:\n        raise HTTPException(404, \"上传会话不存在\")\n\n    return APIResponse(\n        detail={\n            \"upload_id\": session.upload_id,\n            \"file_name\": session.file_name,\n            \"file_size\": session.file_size,\n            \"mode\": session.mode,\n            \"created_at\": session.created_at.isoformat(),\n            \"expires_at\": session.expires_at.isoformat(),\n            \"is_expired\": await session.is_expired(),\n        }\n    )\n\n\n@presign_api.delete(\"/upload/{upload_id}\", dependencies=[Depends(share_required_login)])\nasync def presign_upload_cancel(upload_id: str):\n    \"\"\"取消上传会话\"\"\"\n    session = await PresignUploadSession.filter(upload_id=upload_id).first()\n    if not session:\n        raise HTTPException(404, \"上传会话不存在\")\n\n    if session.mode == \"direct\":\n        storage: FileStorageInterface = storages[settings.file_storage]()\n        try:\n            if await storage.file_exists(session.save_path):\n                temp_file_code = FileCodes(\n                    file_path=os.path.dirname(session.save_path),\n                    uuid_file_name=os.path.basename(session.save_path),\n                )\n                await storage.delete_file(temp_file_code)\n        except Exception:\n            pass\n\n    await session.delete()\n    return APIResponse(detail={\"message\": \"上传会话已取消\"})\n"
  },
  {
    "path": "core/__init__.py",
    "content": "# @Time    : 2023/8/11 20:06\n# @Author  : Lan\n# @File    : __init__.py.py\n# @Software: PyCharm\n"
  },
  {
    "path": "core/config.py",
    "content": "from apps.base.models import KeyValue\nfrom apps.base.utils import ip_limit\nfrom core.settings import DEFAULT_CONFIG, settings\n\n\nasync def ensure_settings_row() -> None:\n    await KeyValue.get_or_create(key=\"settings\", defaults={\"value\": DEFAULT_CONFIG})\n\n\ndef _sync_ip_limits() -> None:\n    ip_limit[\"error\"].minutes = settings.errorMinute\n    ip_limit[\"error\"].count = settings.errorCount\n    ip_limit[\"upload\"].minutes = settings.uploadMinute\n    ip_limit[\"upload\"].count = settings.uploadCount\n\n\nasync def refresh_settings() -> None:\n    \"\"\"从数据库读取最新配置并应用到运行时。\"\"\"\n    config_record = await KeyValue.filter(key=\"settings\").first()\n    settings.user_config = config_record.value if config_record and config_record.value else {}\n    _sync_ip_limits()\n"
  },
  {
    "path": "core/database.py",
    "content": "import asyncio\nimport glob\nimport importlib\nimport os\nfrom contextlib import asynccontextmanager\nfrom typing import IO\n\nfrom tortoise import Tortoise\n\nfrom core.logger import logger\nfrom core.settings import data_root\n\n\n_DB_FILE = os.path.join(data_root, \"filecodebox.db\")\n_STARTUP_LOCK_FILE = os.path.join(data_root, \"filecodebox.startup.lock\")\n\n\ndef get_db_config() -> dict:\n    return {\n        \"connections\": {\n            \"default\": {\n                \"engine\": \"tortoise.backends.sqlite\",\n                \"credentials\": {\n                    \"file_path\": _DB_FILE,\n                    \"journal_mode\": \"WAL\",\n                    \"busy_timeout\": 10000,\n                    \"foreign_keys\": \"ON\",\n                },\n            }\n        },\n        \"apps\": {\n            \"models\": {\n                \"models\": [\"apps.base.models\"],\n                \"default_connection\": \"default\",\n            }\n        },\n        \"use_tz\": False,\n        \"timezone\": \"Asia/Shanghai\",\n    }\n\n\ndef _lock_file(file_obj: IO[str]) -> None:\n    if os.name == \"nt\":\n        import msvcrt\n\n        # Windows 需要锁定至少 1 字节\n        if os.fstat(file_obj.fileno()).st_size == 0:\n            file_obj.write(\"0\")\n            file_obj.flush()\n        msvcrt.locking(file_obj.fileno(), msvcrt.LK_LOCK, 1)\n    else:\n        import fcntl\n\n        fcntl.flock(file_obj.fileno(), fcntl.LOCK_EX)\n\n\ndef _unlock_file(file_obj: IO[str]) -> None:\n    if os.name == \"nt\":\n        import msvcrt\n\n        msvcrt.locking(file_obj.fileno(), msvcrt.LK_UNLCK, 1)\n    else:\n        import fcntl\n\n        fcntl.flock(file_obj.fileno(), fcntl.LOCK_UN)\n\n\n@asynccontextmanager\nasync def db_startup_lock():\n    os.makedirs(data_root, exist_ok=True)\n    lock_file = open(_STARTUP_LOCK_FILE, \"a+\", encoding=\"utf-8\")\n    try:\n        await asyncio.to_thread(_lock_file, lock_file)\n        yield\n    finally:\n        await asyncio.to_thread(_unlock_file, lock_file)\n        lock_file.close()\n\n\nasync def init_db():\n    try:\n        db_config = get_db_config()\n\n        if not Tortoise._inited:\n            await Tortoise.init(config=db_config)\n\n        async with db_startup_lock():\n            # 创建migrations表\n            await Tortoise.get_connection(\"default\").execute_script(\"\"\"\n                CREATE TABLE IF NOT EXISTS migrates (\n                    id INTEGER PRIMARY KEY AUTOINCREMENT,\n                    migration_file VARCHAR(255) NOT NULL UNIQUE,\n                    executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n                )\n            \"\"\")\n\n            # 执行迁移\n            await execute_migrations()\n\n    except Exception as e:\n        logger.error(f\"数据库初始化失败: {str(e)}\")\n        raise\n\n\nasync def execute_migrations():\n    \"\"\"执行数据库迁移\"\"\"\n    try:\n        # 收集迁移文件\n        migration_files = []\n        for root, dirs, files in os.walk(\"apps\"):\n            if \"migrations\" in dirs:\n                migration_path = os.path.join(root, \"migrations\")\n                migration_files.extend(glob.glob(os.path.join(migration_path, \"migrations_*.py\")))\n\n        # 按文件名排序\n        migration_files.sort()\n\n        for migration_file in migration_files:\n            file_name = os.path.basename(migration_file)\n\n            # 检查是否已执行\n            executed = await Tortoise.get_connection(\"default\").execute_query(\n                \"SELECT id FROM migrates WHERE migration_file = ?\", [file_name]\n            )\n\n            if not executed[1]:\n                logger.info(f\"执行迁移: {file_name}\")\n                # 导入并执行migration\n                module_path = migration_file.replace(\"/\", \".\").replace(\"\\\\\", \".\").replace(\".py\", \"\")\n                try:\n                    migration_module = importlib.import_module(module_path)\n                    if hasattr(migration_module, \"migrate\"):\n                        await migration_module.migrate()\n                        # 记录执行\n                        await Tortoise.get_connection(\"default\").execute_query(\n                            \"INSERT INTO migrates (migration_file) VALUES (?)\",\n                            [file_name]\n                        )\n                        logger.info(f\"迁移完成: {file_name}\")\n                except Exception as e:\n                    logger.error(f\"迁移 {file_name} 执行失败: {str(e)}\")\n                    raise\n\n    except Exception as e:\n        logger.error(f\"迁移过程发生错误: {str(e)}\")\n        raise\n"
  },
  {
    "path": "core/logger.py",
    "content": "import logging\nimport sys\n\n\ndef setup_logger():\n    # 创建logger对象\n    _logger = logging.getLogger('FileCodeBox')\n    _logger.setLevel(logging.INFO)\n\n    # 创建控制台处理器\n    console_handler = logging.StreamHandler(sys.stdout)\n    console_handler.setLevel(logging.INFO)\n\n    # 设置日志格式\n    formatter = logging.Formatter(\n        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n        datefmt='%Y-%m-%d %H:%M:%S'\n    )\n    console_handler.setFormatter(formatter)\n\n    # 添加处理器到logger\n    _logger.addHandler(console_handler)\n\n    return _logger\n\n\n# 创建全局logger实例\nlogger = setup_logger()\n"
  },
  {
    "path": "core/response.py",
    "content": "# @Time    : 2023/8/14 11:48\n# @Author  : Lan\n# @File    : response.py\n# @Software: PyCharm\nfrom typing import Generic, Optional, TypeVar\n\nfrom pydantic import BaseModel\n\nT = TypeVar(\"T\")\n\n\nclass APIResponse(BaseModel, Generic[T]):\n    code: int = 200\n    message: str = \"ok\"\n    detail: Optional[T] = None\n"
  },
  {
    "path": "core/settings.py",
    "content": "# @Time    : 2023/8/15 09:51\n# @Author  : Lan\n# @File    : settings.py\n# @Software: PyCharm\nfrom pathlib import Path\n\nBASE_DIR = Path(__file__).resolve().parent.parent\ndata_root = BASE_DIR / \"data\"\n\nif not data_root.exists():\n    data_root.mkdir(parents=True, exist_ok=True)\n\nDEFAULT_CONFIG = {\n    \"file_storage\": \"local\",\n    \"storage_path\": \"\",\n    \"name\": \"文件快递柜 - FileCodeBox\",\n    \"description\": \"开箱即用的文件快传系统\",\n    \"notify_title\": \"系统通知\",\n    \"notify_content\": '欢迎使用 FileCodeBox，本程序开源于 <a href=\"https://github.com/vastsa/FileCodeBox\" target=\"_blank\">Github</a> ，欢迎Star和Fork。',\n    \"page_explain\": \"请勿上传或分享违法内容。根据《中华人民共和国网络安全法》、《中华人民共和国刑法》、《中华人民共和国治安管理处罚法》等相关规定。 传播或存储违法、违规内容，会受到相关处罚，严重者将承担刑事责任。本站坚决配合相关部门，确保网络内容的安全，和谐，打造绿色网络环境。\",\n    \"keywords\": \"FileCodeBox, 文件快递柜, 口令传送箱, 匿名口令分享文本, 文件\",\n    \"s3_access_key_id\": \"\",\n    \"s3_secret_access_key\": \"\",\n    \"s3_bucket_name\": \"\",\n    \"s3_endpoint_url\": \"\",\n    \"s3_region_name\": \"auto\",\n    \"s3_signature_version\": \"s3v2\",\n    \"s3_hostname\": \"\",\n    \"s3_proxy\": 0,\n    \"max_save_seconds\": 0,\n    \"aws_session_token\": \"\",\n    \"onedrive_domain\": \"\",\n    \"onedrive_client_id\": \"\",\n    \"onedrive_username\": \"\",\n    \"onedrive_password\": \"\",\n    \"onedrive_root_path\": \"filebox_storage\",\n    \"onedrive_proxy\": 0,\n    \"webdav_root_path\": \"filebox_storage\",\n    \"webdav_proxy\": 0,\n    \"admin_token\": \"FileCodeBox2023\",\n    \"openUpload\": 1,\n    \"uploadSize\": 1024 * 1024 * 10,\n    \"expireStyle\": [\"day\", \"hour\", \"minute\", \"forever\", \"count\"],\n    \"uploadMinute\": 1,\n    \"enableChunk\": 0,\n    \"webdav_url\": \"\",\n    \"webdav_password\": \"\",\n    \"webdav_username\": \"\",\n    \"opacity\": 0.9,\n    \"background\": \"\",\n    \"uploadCount\": 10,\n    \"themesChoices\": [\n        {\n            \"name\": \"2023\",\n            \"key\": \"themes/2023\",\n            \"author\": \"Lan\",\n            \"version\": \"1.0\",\n        },\n        {\n            \"name\": \"2024\",\n            \"key\": \"themes/2024\",\n            \"author\": \"Lan\",\n            \"version\": \"1.0\",\n        },\n    ],\n    \"themesSelect\": \"themes/2024\",\n    \"errorMinute\": 1,\n    \"errorCount\": 10,\n    \"serverWorkers\": 1,\n    \"serverHost\": \"0.0.0.0\",\n    \"serverPort\": 12345,\n    \"showAdminAddr\": 0,\n    \"robotsText\": \"User-agent: *\\nDisallow: /\",\n}\n\n\nclass Settings:\n    def __init__(self, defaults=None):\n        self.default_config = defaults or {}\n        self.user_config = {}\n\n    def __getattr__(self, attr):\n        if attr in self.user_config:\n            return self.user_config[attr]\n        if attr in self.default_config:\n            return self.default_config[attr]\n        raise AttributeError(\n            f\"'{self.__class__.__name__}' object has no attribute '{attr}'\"\n        )\n\n    def __setattr__(self, key, value):\n        if key in [\"default_config\", \"user_config\"]:\n            super().__setattr__(key, value)\n        else:\n            self.user_config[key] = value\n\n    def items(self):\n        return {**self.default_config, **self.user_config}.items()\n\n\nsettings = Settings(DEFAULT_CONFIG)\n"
  },
  {
    "path": "core/storage.py",
    "content": "# @Time    : 2023/8/11 20:06\n# @Author  : Lan\n# @File    : storage.py\n# @Software: PyCharm\nimport base64\nimport hashlib\nimport os\nimport tempfile\nfrom core.logger import logger\nimport shutil\nfrom typing import Optional\nfrom urllib.parse import quote, unquote\n\nimport aiofiles\nimport aiohttp\nimport asyncio\nfrom pathlib import Path\nimport datetime\nimport re\nimport aioboto3\nfrom botocore.config import Config\nfrom fastapi import HTTPException, Response, UploadFile\nfrom core.response import APIResponse\nfrom core.settings import data_root, settings\nfrom apps.base.models import FileCodes, UploadChunk\nfrom core.utils import get_file_url, sanitize_filename\nfrom fastapi.responses import FileResponse, StreamingResponse\n\n\nclass FileStorageInterface:\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        \"\"\"\n        保存文件\n        \"\"\"\n        raise NotImplementedError\n\n    async def delete_file(self, file_code: FileCodes):\n        \"\"\"\n        删除文件\n        \"\"\"\n        raise NotImplementedError\n\n    async def get_file_url(self, file_code: FileCodes):\n        \"\"\"\n        获取文件分享的url\n\n        如果服务不支持直接访问文件，可以通过服务器中转下载。\n        此时，此方法可以调用 utils.py 中的 `get_file_url` 方法，获取服务器中转下载的url\n        \"\"\"\n        raise NotImplementedError\n\n    async def get_file_response(self, file_code: FileCodes):\n        \"\"\"\n        获取文件响应\n\n        如果服务不支持直接访问文件，则需要实现该方法，返回文件响应\n        其余情况，可以不实现该方法\n        \"\"\"\n        raise NotImplementedError\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"\n        保存分片文件\n        :param upload_id: 上传会话ID\n        :param chunk_index: 分片索引\n        :param chunk_data: 分片数据\n        :param chunk_hash: 分片哈希值\n        :param save_path: 文件保存路径\n        \"\"\"\n        raise NotImplementedError\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"\n        合并分片文件并返回文件路径和完整哈希值\n        :param upload_id: 上传会话ID\n        :param chunk_info: 分片信息\n        :param save_path: 文件保存路径\n        :return: (文件路径, 文件哈希值)\n        \"\"\"\n        raise NotImplementedError\n\n    async def generate_presigned_upload_url(self, save_path: str, expires_in: int = 900) -> Optional[str]:\n        \"\"\"\n        生成预签名上传URL\n        :param save_path: 文件保存路径\n        :param expires_in: URL过期时间（秒），默认15分钟\n        :return: 预签名URL，如果不支持直传则返回None\n        \"\"\"\n        return None  # 默认不支持直传，使用代理模式\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        raise NotImplementedError\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"\n        清理临时分片文件\n        :param upload_id: 上传会话ID\n        :param save_path: 文件保存路径\n        \"\"\"\n        raise NotImplementedError\n\n\nclass SystemFileStorage(FileStorageInterface):\n    def __init__(self):\n        self.chunk_size = 256 * 1024\n        self.root_path = data_root\n\n    def _save(self, file, save_path):\n        with open(save_path, \"wb\") as f:\n            chunk = file.read(self.chunk_size)\n            while chunk:\n                f.write(chunk)\n                chunk = file.read(self.chunk_size)\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        path_obj = Path(save_path)\n        directory = str(path_obj.parent)\n        # 提取原始文件名并进行清理\n        filename = await sanitize_filename(path_obj.name)\n        # 构建安全的完整保存路径\n        safe_save_path = self.root_path / directory / filename\n        # 确保目录存在\n        if not safe_save_path.parent.exists():\n            safe_save_path.parent.mkdir(parents=True)\n        await asyncio.to_thread(self._save, file.file, safe_save_path)\n\n    async def delete_file(self, file_code: FileCodes):\n        save_path = self.root_path / await file_code.get_file_path()\n        if save_path.exists():\n            save_path.unlink()\n\n    async def get_file_url(self, file_code: FileCodes):\n        return await get_file_url(file_code.code)\n\n    async def get_file_response(self, file_code: FileCodes):\n        file_path = self.root_path / await file_code.get_file_path()\n        if not file_path.exists():\n            return APIResponse(code=404, detail=\"文件已过期删除\")\n        filename = f\"{file_code.prefix}{file_code.suffix}\"\n        encoded_filename = quote(filename, safe='')\n        content_disposition = f\"attachment; filename*=UTF-8''{encoded_filename}\"\n        \n        # 尝试获取文件系统大小，如果成功则设置 Content-Length\n        headers = {\"Content-Disposition\": content_disposition}\n        try:\n            content_length = file_path.stat().st_size\n            headers[\"Content-Length\"] = str(content_length)\n        except Exception:\n            # 如果获取文件大小失败，则不提供 Content-Length\n            pass\n        \n        return FileResponse(\n            file_path,\n            media_type=\"application/octet-stream\",\n            headers=headers,\n            filename=filename  # 保留原始文件名以备某些场景使用\n        )\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"\n        保存分片文件到本地文件系统\n        :param upload_id: 上传会话ID\n        :param chunk_index: 分片索引\n        :param chunk_data: 分片数据\n        :param chunk_hash: 分片哈希值\n        :param save_path: 文件保存路径\n        \"\"\"\n        chunk_dir = self.root_path / save_path\n        chunk_path = chunk_dir.parent / 'chunks' / upload_id / f\"{chunk_index}.part\"\n        if not chunk_path.parent.exists():\n            chunk_path.parent.mkdir(parents=True, exist_ok=True)\n        # 使用临时文件写入，确保原子性\n        temp_path = chunk_path.with_suffix('.tmp')\n        try:\n            async with aiofiles.open(temp_path, \"wb\") as f:\n                await f.write(chunk_data)\n            # 原子重命名\n            temp_path.rename(chunk_path)\n        except Exception as e:\n            if temp_path.exists():\n                temp_path.unlink()\n            raise e\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"\n        合并本地文件系统的分片文件并返回文件路径和完整哈希值\n        :param upload_id: 上传会话ID\n        :param chunk_info: 分片信息\n        :param save_path: 文件保存路径\n        :return: (文件路径, 文件哈希值)\n        \"\"\"\n        output_path = self.root_path / save_path\n        output_path.parent.mkdir(parents=True, exist_ok=True)\n        chunk_base_dir = output_path.parent / 'chunks' / upload_id\n        file_sha256 = hashlib.sha256()\n        \n        # 使用临时文件写入，确保原子性\n        temp_output = output_path.with_suffix('.merging')\n        try:\n            async with aiofiles.open(temp_output, \"wb\") as out_file:\n                for i in range(chunk_info.total_chunks):\n                    # 获取分片记录\n                    chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()\n                    if not chunk_record:\n                        raise ValueError(f\"分片{i}记录不存在\")\n                    chunk_path = chunk_base_dir / f\"{i}.part\"\n                    if not chunk_path.exists():\n                        raise ValueError(f\"分片{i}文件不存在\")\n                    async with aiofiles.open(chunk_path, \"rb\") as in_file:\n                        chunk_data = await in_file.read()\n                        current_hash = hashlib.sha256(chunk_data).hexdigest()\n                        if current_hash != chunk_record.chunk_hash:\n                            raise ValueError(f\"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}\")\n                        file_sha256.update(chunk_data)\n                        await out_file.write(chunk_data)\n            # 原子重命名\n            temp_output.rename(output_path)\n        except Exception as e:\n            if temp_output.exists():\n                temp_output.unlink()\n            raise e\n        return str(output_path), file_sha256.hexdigest()\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"\n        清理本地文件系统的临时分片文件\n        :param upload_id: 上传会话ID\n        :param save_path: 文件保存路径\n        \"\"\"\n        chunk_dir = (self.root_path / save_path).parent / 'chunks' / upload_id\n        if chunk_dir.exists():\n            try:\n                shutil.rmtree(chunk_dir)\n            except Exception as e:\n                logger.info(f\"清理本地分片目录失败: {e}\")\n        # 清理父级 chunks 目录（如果为空）\n        chunks_parent = chunk_dir.parent\n        if chunks_parent.exists() and not any(chunks_parent.iterdir()):\n            try:\n                chunks_parent.rmdir()\n            except Exception as e:\n                logger.info(f\"清理 chunks 父目录失败: {e}\")\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在于本地文件系统\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        file_path = self.root_path / save_path\n        return file_path.exists()\n\n\nclass S3FileStorage(FileStorageInterface):\n    def __init__(self):\n        self.access_key_id = settings.s3_access_key_id\n        self.secret_access_key = settings.s3_secret_access_key\n        self.bucket_name = settings.s3_bucket_name\n        self.s3_hostname = settings.s3_hostname\n        self.region_name = settings.s3_region_name\n        self.signature_version = settings.s3_signature_version\n        self.endpoint_url = settings.s3_endpoint_url or f\"https://{self.s3_hostname}\"\n        self.aws_session_token = settings.aws_session_token\n        self.proxy = settings.s3_proxy\n        self.session = aioboto3.Session(\n            aws_access_key_id=self.access_key_id,\n            aws_secret_access_key=self.secret_access_key,\n        )\n        if not settings.s3_endpoint_url:\n            self.endpoint_url = f\"https://{self.s3_hostname}\"\n        else:\n            # 如果提供了 s3_endpoint_url，则优先使用它\n            self.endpoint_url = settings.s3_endpoint_url\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        async with self.session.client(\n                \"s3\",\n                endpoint_url=self.endpoint_url,\n                aws_session_token=self.aws_session_token,\n                region_name=self.region_name,\n                config=Config(signature_version=self.signature_version),\n        ) as s3:\n            # 使用 upload_fileobj 流式上传，避免将整个文件加载到内存\n            await s3.upload_fileobj(\n                file.file,\n                self.bucket_name,\n                save_path,\n                ExtraArgs={\"ContentType\": file.content_type or \"application/octet-stream\"},\n            )\n\n    async def delete_file(self, file_code: FileCodes):\n        async with self.session.client(\n                \"s3\",\n                endpoint_url=self.endpoint_url,\n                region_name=self.region_name,\n                config=Config(signature_version=self.signature_version),\n        ) as s3:\n            await s3.delete_object(\n                Bucket=self.bucket_name, Key=await file_code.get_file_path()\n            )\n\n    async def get_file_response(self, file_code: FileCodes):\n        try:\n            filename = file_code.prefix + file_code.suffix\n            content_length = None  # 初始化为 None，表示未知大小\n            \n            async with self.session.client(\n                    \"s3\",\n                    endpoint_url=self.endpoint_url,\n                    region_name=self.region_name,\n                    config=Config(signature_version=self.signature_version),\n            ) as s3:\n                # 尝试获取文件大小（HEAD请求）\n                try:\n                    head_response = await s3.head_object(\n                        Bucket=self.bucket_name,\n                        Key=await file_code.get_file_path()\n                    )\n                    # 从HEAD响应中获取Content-Length\n                    if 'ContentLength' in head_response:\n                        content_length = head_response['ContentLength']\n                    elif 'Content-Length' in head_response['ResponseMetadata']['HTTPHeaders']:\n                        content_length = int(head_response['ResponseMetadata']['HTTPHeaders']['Content-Length'])\n                except Exception:\n                    # 如果HEAD请求失败，则不提供 Content-Length\n                    pass\n                \n                link = await s3.generate_presigned_url(\n                    \"get_object\",\n                    Params={\n                        \"Bucket\": self.bucket_name,\n                        \"Key\": await file_code.get_file_path(),\n                    },\n                    ExpiresIn=3600,\n                )\n            \n            # 创建ClientSession并传递给生成器复用\n            session = aiohttp.ClientSession()\n            \n            async def stream_generator():\n                try:\n                    async with session.get(link) as resp:\n                        if resp.status != 200:\n                            raise HTTPException(\n                                status_code=resp.status,\n                                detail=f\"从S3获取文件失败: {resp.status}\"\n                            )\n                        # 设置块大小（例如64KB）\n                        chunk_size = 65536\n                        while True:\n                            chunk = await resp.content.read(chunk_size)\n                            if not chunk:\n                                break\n                            yield chunk\n                finally:\n                    await session.close()\n            \n            from fastapi.responses import StreamingResponse\n            encoded_filename = quote(filename, safe='')\n            headers = {\n                \"Content-Disposition\": f\"attachment; filename*=UTF-8''{encoded_filename}\"\n            }\n            if content_length is not None:\n                headers[\"Content-Length\"] = str(content_length)\n            return StreamingResponse(\n                stream_generator(),\n                media_type=\"application/octet-stream\",\n                headers=headers\n            )\n        except HTTPException:\n            raise\n        except Exception:\n            raise HTTPException(status_code=503, detail=\"服务代理下载异常，请稍后再试\")\n\n    async def get_file_url(self, file_code: FileCodes):\n        if file_code.prefix == \"文本分享\":\n            return file_code.text\n        if self.proxy:\n            return await get_file_url(file_code.code)\n        else:\n            async with self.session.client(\n                    \"s3\",\n                    endpoint_url=self.endpoint_url,\n                    region_name=self.region_name,\n                    config=Config(signature_version=self.signature_version),\n            ) as s3:\n                result = await s3.generate_presigned_url(\n                    \"get_object\",\n                    Params={\n                        \"Bucket\": self.bucket_name,\n                        \"Key\": await file_code.get_file_path(),\n                    },\n                    ExpiresIn=3600,\n                )\n                return result\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"\n        保存分片到 S3（使用独立对象存储每个分片）\n        注意：这里不使用 S3 原生的 multipart upload，而是将每个分片作为独立对象存储\n        \"\"\"\n        chunk_key = str(Path(save_path).parent / \"chunks\" / upload_id / f\"{chunk_index}.part\")\n        async with self.session.client(\n            's3',\n            endpoint_url=self.endpoint_url,\n            aws_session_token=self.aws_session_token,\n            region_name=self.region_name,\n            config=Config(signature_version=self.signature_version),\n        ) as s3:\n            # 将分片作为独立对象上传\n            await s3.put_object(\n                Bucket=self.bucket_name,\n                Key=chunk_key,\n                Body=chunk_data,\n                Metadata={\n                    'chunk-hash': chunk_hash,\n                    'chunk-index': str(chunk_index)\n                }\n            )\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"\n        合并 S3 上的分片文件\n        使用 S3 的 multipart upload API 实现流式合并，避免内存问题\n        \"\"\"\n        file_sha256 = hashlib.sha256()\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n\n        async with self.session.client(\n            's3',\n            endpoint_url=self.endpoint_url,\n            aws_session_token=self.aws_session_token,\n            region_name=self.region_name,\n            config=Config(signature_version=self.signature_version),\n        ) as s3:\n            # 创建 multipart upload\n            mpu = await s3.create_multipart_upload(\n                Bucket=self.bucket_name,\n                Key=save_path,\n                ContentType='application/octet-stream'\n            )\n            mpu_id = mpu['UploadId']\n            parts = []\n\n            try:\n                # 按顺序读取、验证并上传每个分片\n                for i in range(chunk_info.total_chunks):\n                    chunk_key = f\"{chunk_dir}/{i}.part\"\n                    chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()\n                    if not chunk_record:\n                        raise ValueError(f\"分片{i}记录不存在\")\n\n                    try:\n                        response = await s3.get_object(\n                            Bucket=self.bucket_name,\n                            Key=chunk_key\n                        )\n                        chunk_data = await response['Body'].read()\n                    except Exception as e:\n                        raise ValueError(f\"分片{i}文件不存在: {e}\")\n\n                    current_hash = hashlib.sha256(chunk_data).hexdigest()\n                    if current_hash != chunk_record.chunk_hash:\n                        raise ValueError(f\"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}\")\n\n                    file_sha256.update(chunk_data)\n\n                    # 上传分片到 multipart upload\n                    part_response = await s3.upload_part(\n                        Bucket=self.bucket_name,\n                        Key=save_path,\n                        UploadId=mpu_id,\n                        PartNumber=i + 1,  # S3 part numbers start at 1\n                        Body=chunk_data\n                    )\n                    parts.append({\n                        'PartNumber': i + 1,\n                        'ETag': part_response['ETag']\n                    })\n\n                    # 释放内存\n                    del chunk_data\n\n                # 完成 multipart upload\n                await s3.complete_multipart_upload(\n                    Bucket=self.bucket_name,\n                    Key=save_path,\n                    UploadId=mpu_id,\n                    MultipartUpload={'Parts': parts}\n                )\n            except Exception as e:\n                # 出错时取消 multipart upload\n                await s3.abort_multipart_upload(\n                    Bucket=self.bucket_name,\n                    Key=save_path,\n                    UploadId=mpu_id\n                )\n                raise e\n\n        return save_path, file_sha256.hexdigest()\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"\n        清理 S3 上的临时分片文件\n        :param upload_id: 上传会话ID\n        :param save_path: 文件保存路径\n        \"\"\"\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n        async with self.session.client(\n            's3',\n            endpoint_url=self.endpoint_url,\n            aws_session_token=self.aws_session_token,\n            region_name=self.region_name,\n            config=Config(signature_version=self.signature_version),\n        ) as s3:\n            try:\n                # 列出并删除所有分片对象\n                paginator = s3.get_paginator('list_objects_v2')\n                async for page in paginator.paginate(Bucket=self.bucket_name, Prefix=chunk_dir):\n                    objects = page.get('Contents', [])\n                    if objects:\n                        delete_objects = [{'Key': obj['Key']} for obj in objects]\n                        await s3.delete_objects(\n                            Bucket=self.bucket_name,\n                            Delete={'Objects': delete_objects}\n                        )\n            except Exception as e:\n                logger.info(f\"清理 S3 分片数据时出错: {e}\")\n\n    async def generate_presigned_upload_url(self, save_path: str, expires_in: int = 900) -> Optional[str]:\n        \"\"\"\n        生成S3预签名上传URL\n        :param save_path: 文件保存路径\n        :param expires_in: URL过期时间（秒），默认15分钟\n        :return: 预签名PUT URL\n        \"\"\"\n        async with self.session.client(\n            \"s3\",\n            endpoint_url=self.endpoint_url,\n            aws_session_token=self.aws_session_token,\n            region_name=self.region_name,\n            config=Config(signature_version=self.signature_version),\n        ) as s3:\n            return await s3.generate_presigned_url(\n                \"put_object\",\n                Params={\n                    \"Bucket\": self.bucket_name,\n                    \"Key\": save_path,\n                },\n                ExpiresIn=expires_in,\n            )\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在于S3\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        async with self.session.client(\n            \"s3\",\n            endpoint_url=self.endpoint_url,\n            aws_session_token=self.aws_session_token,\n            region_name=self.region_name,\n            config=Config(signature_version=self.signature_version),\n        ) as s3:\n            try:\n                await s3.head_object(Bucket=self.bucket_name, Key=save_path)\n                return True\n            except Exception:\n                return False\n\n\nclass OneDriveFileStorage(FileStorageInterface):\n    def __init__(self):\n        try:\n            import msal\n            from office365.graph_client import GraphClient\n            from office365.runtime.client_request_exception import (\n                ClientRequestException,\n            )\n        except ImportError:\n            raise ImportError(\"请先安装`msal`和`Office365-REST-Python-Client`\")\n        self.msal = msal\n        self.domain = settings.onedrive_domain\n        self.client_id = settings.onedrive_client_id\n        self.username = settings.onedrive_username\n        self.password = settings.onedrive_password\n        self.proxy = settings.onedrive_proxy\n        self._ClientRequestException = ClientRequestException\n\n        try:\n            client = GraphClient(self.acquire_token_pwd)\n            self.root_path = (\n                client.me.drive.root.get_by_path(settings.onedrive_root_path)\n                .get()\n                .execute_query()\n            )\n        except ClientRequestException as e:\n            if e.code == \"itemNotFound\":\n                client.me.drive.root.create_folder(settings.onedrive_root_path)\n                self.root_path = (\n                    client.me.drive.root.get_by_path(\n                        settings.onedrive_root_path)\n                    .get()\n                    .execute_query()\n                )\n            else:\n                raise e\n        except Exception as e:\n            raise Exception(\"OneDrive验证失败，请检查配置是否正确\\n\" + str(e))\n\n    def acquire_token_pwd(self):\n        authority_url = f\"https://login.microsoftonline.com/{self.domain}\"\n        app = self.msal.PublicClientApplication(\n            authority=authority_url, client_id=self.client_id\n        )\n        result = app.acquire_token_by_username_password(\n            username=self.username,\n            password=self.password,\n            scopes=[\"https://graph.microsoft.com/.default\"],\n        )\n        return result\n\n    def _get_path_str(self, path):\n        if isinstance(path, str):\n            path = path.replace(\"\\\\\", \"/\").replace(\"//\", \"/\").split(\"/\")\n        elif isinstance(path, Path):\n            path = str(path).replace(\"\\\\\", \"/\").replace(\"//\", \"/\").split(\"/\")\n        else:\n            raise TypeError(\"path must be str or Path\")\n        path[-1] = path[-1].split(\".\")[0]\n        return \"/\".join(path)\n\n    def _save(self, file, save_path):\n        content = file.file.read()\n        name = save_path(file.filename)\n        path = self._get_path_str(save_path)\n        self.root_path.get_by_path(path).upload(name, content).execute_query()\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        await asyncio.to_thread(self._save, file, save_path)\n\n    def _delete(self, save_path):\n        path = self._get_path_str(save_path)\n        try:\n            self.root_path.get_by_path(path).delete_object().execute_query()\n        except self._ClientRequestException as e:\n            if e.code == \"itemNotFound\":\n                pass\n            else:\n                raise e\n\n    async def delete_file(self, file_code: FileCodes):\n        await asyncio.to_thread(self._delete, await file_code.get_file_path())\n\n    def _convert_link_to_download_link(self, link):\n        p1 = re.search(r\"https://(.+)\\.sharepoint\\.com\", link).group(1)\n        p2 = re.search(r\"personal/(.+)/\", link).group(1)\n        p3 = re.search(rf\"{p2}/(.+)\", link).group(1)\n        return f\"https://{p1}.sharepoint.com/personal/{p2}/_layouts/52/download.aspx?share={p3}\"\n\n    def _get_file_url(self, save_path, name):\n        path = self._get_path_str(save_path)\n        remote_file = self.root_path.get_by_path(path + \"/\" + name)\n        expiration_datetime = datetime.datetime.now(\n            tz=datetime.timezone.utc\n        ) + datetime.timedelta(hours=1)\n        expiration_datetime = expiration_datetime.strftime(\n            \"%Y-%m-%dT%H:%M:%SZ\")\n        permission = remote_file.create_link(\n            \"view\", \"anonymous\", expiration_datetime=expiration_datetime\n        ).execute_query()\n        return self._convert_link_to_download_link(permission.link.webUrl)\n\n    async def get_file_response(self, file_code: FileCodes):\n        try:\n            filename = file_code.prefix + file_code.suffix\n            link = await asyncio.to_thread(\n                self._get_file_url, await file_code.get_file_path(), filename\n            )\n            \n            content_length = None  # 初始化为 None，表示未知大小\n            \n            # 创建ClientSession并复用\n            session = aiohttp.ClientSession()\n            \n            # 尝试发送HEAD请求获取Content-Length\n            try:\n                async with session.head(link) as resp:\n                    if resp.status == 200 and 'Content-Length' in resp.headers:\n                        content_length = int(resp.headers['Content-Length'])\n            except Exception:\n                # 如果HEAD请求失败，则不提供 Content-Length\n                pass\n            \n            async def stream_generator():\n                try:\n                    async with session.get(link) as resp:\n                        if resp.status != 200:\n                            raise HTTPException(\n                                status_code=resp.status,\n                                detail=f\"从OneDrive获取文件失败: {resp.status}\"\n                            )\n                        chunk_size = 65536\n                        while True:\n                            chunk = await resp.content.read(chunk_size)\n                            if not chunk:\n                                break\n                            yield chunk\n                finally:\n                    await session.close()\n            \n            encoded_filename = quote(filename, safe='')\n            headers = {\n                \"Content-Disposition\": f\"attachment; filename*=UTF-8''{encoded_filename}\"\n            }\n            if content_length is not None:\n                headers[\"Content-Length\"] = str(content_length)\n            return StreamingResponse(\n                stream_generator(),\n                media_type=\"application/octet-stream\",\n                headers=headers\n            )\n        except HTTPException:\n            raise\n        except Exception:\n            raise HTTPException(status_code=503, detail=\"服务代理下载异常，请稍后再试\")\n\n    async def get_file_url(self, file_code: FileCodes):\n        if self.proxy:\n            return await get_file_url(file_code.code)\n        else:\n            return await asyncio.to_thread(\n                self._get_file_url,\n                await file_code.get_file_path(),\n                f\"{file_code.prefix}{file_code.suffix}\",\n            )\n\n    def _save_chunk(self, chunk_path: str, chunk_data: bytes):\n        \"\"\"同步保存分片到 OneDrive\"\"\"\n        path_parts = chunk_path.replace(\"\\\\\", \"/\").split(\"/\")\n        filename = path_parts[-1]\n        dir_path = \"/\".join(path_parts[:-1])\n        \n        # 确保目录存在\n        current_folder = self.root_path\n        for part in dir_path.split(\"/\"):\n            if part:\n                try:\n                    current_folder = current_folder.get_by_path(part).get().execute_query()\n                except self._ClientRequestException as e:\n                    if e.code == \"itemNotFound\":\n                        current_folder = current_folder.create_folder(part).execute_query()\n                    else:\n                        raise e\n        \n        # 上传分片\n        current_folder.upload(filename, chunk_data).execute_query()\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"保存分片到 OneDrive\"\"\"\n        chunk_path = str(Path(save_path).parent / \"chunks\" / upload_id / f\"{chunk_index}.part\")\n        await asyncio.to_thread(self._save_chunk, chunk_path, chunk_data)\n\n    def _read_chunk(self, chunk_path: str) -> bytes:\n        \"\"\"同步读取分片\"\"\"\n        path = self._get_path_str(chunk_path)\n        file_obj = self.root_path.get_by_path(path).get().execute_query()\n        return file_obj.get_content().execute_query().value\n\n    def _upload_merged(self, save_path: str, data: bytes):\n        \"\"\"同步上传合并后的文件\"\"\"\n        path_parts = save_path.replace(\"\\\\\", \"/\").split(\"/\")\n        filename = path_parts[-1]\n        dir_path = \"/\".join(path_parts[:-1])\n        \n        # 确保目录存在\n        current_folder = self.root_path\n        for part in dir_path.split(\"/\"):\n            if part:\n                try:\n                    current_folder = current_folder.get_by_path(part).get().execute_query()\n                except self._ClientRequestException as e:\n                    if e.code == \"itemNotFound\":\n                        current_folder = current_folder.create_folder(part).execute_query()\n                    else:\n                        raise e\n        \n        current_folder.upload(filename, data).execute_query()\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"合并 OneDrive 上的分片文件，使用临时文件避免内存问题\"\"\"\n        file_sha256 = hashlib.sha256()\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n\n        # 使用临时文件存储合并数据\n        with tempfile.NamedTemporaryFile(delete=False) as temp_file:\n            temp_path = temp_file.name\n\n        try:\n            async with aiofiles.open(temp_path, 'wb') as out_file:\n                for i in range(chunk_info.total_chunks):\n                    chunk_path = f\"{chunk_dir}/{i}.part\"\n                    chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()\n                    if not chunk_record:\n                        raise ValueError(f\"分片{i}记录不存在\")\n\n                    try:\n                        chunk_data = await asyncio.to_thread(self._read_chunk, chunk_path)\n                    except Exception as e:\n                        raise ValueError(f\"分片{i}文件不存在: {e}\")\n\n                    current_hash = hashlib.sha256(chunk_data).hexdigest()\n                    if current_hash != chunk_record.chunk_hash:\n                        raise ValueError(f\"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}\")\n\n                    file_sha256.update(chunk_data)\n                    await out_file.write(chunk_data)\n                    del chunk_data  # 释放内存\n\n            # 读取临时文件并上传\n            async with aiofiles.open(temp_path, 'rb') as f:\n                merged_content = await f.read()\n            await asyncio.to_thread(self._upload_merged, save_path, merged_content)\n        finally:\n            # 清理临时文件\n            if os.path.exists(temp_path):\n                os.unlink(temp_path)\n\n        return save_path, file_sha256.hexdigest()\n\n    def _delete_chunk_dir(self, chunk_dir: str):\n        \"\"\"同步删除分片目录\"\"\"\n        try:\n            path = self._get_path_str(chunk_dir)\n            self.root_path.get_by_path(path).delete_object().execute_query()\n        except self._ClientRequestException as e:\n            if e.code != \"itemNotFound\":\n                raise e\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"清理 OneDrive 上的临时分片文件\"\"\"\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n        try:\n            await asyncio.to_thread(self._delete_chunk_dir, chunk_dir)\n        except Exception as e:\n            logger.info(f\"清理 OneDrive 分片时出错: {e}\")\n\n    def _file_exists(self, save_path: str) -> bool:\n        \"\"\"同步检查文件是否存在\"\"\"\n        try:\n            path = self._get_path_str(save_path)\n            self.root_path.get_by_path(path).get().execute_query()\n            return True\n        except self._ClientRequestException as e:\n            if e.code == \"itemNotFound\":\n                return False\n            raise e\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在于OneDrive\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        return await asyncio.to_thread(self._file_exists, save_path)\n\n\nclass OpenDALFileStorage(FileStorageInterface):\n    def __init__(self):\n        try:\n            import opendal\n        except ImportError:\n            raise ImportError('请先安装 `opendal`, 例如: \"pip install opendal\"')\n        self.service = settings.opendal_scheme\n        service_settings = {}\n        for key, value in settings.items():\n            if key.startswith(\"opendal_\" + self.service):\n                setting_name = key.split(\"_\", 2)[2]\n                service_settings[setting_name] = value\n        self.operator = opendal.AsyncOperator(\n            settings.opendal_scheme, **service_settings\n        )\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        # 使用 asyncio.to_thread 避免阻塞事件循环\n        content = await asyncio.to_thread(file.file.read)\n        await self.operator.write(save_path, content)\n\n    async def delete_file(self, file_code: FileCodes):\n        await self.operator.delete(await file_code.get_file_path())\n\n    async def get_file_url(self, file_code: FileCodes):\n        return await get_file_url(file_code.code)\n\n    async def get_file_response(self, file_code: FileCodes):\n        try:\n            filename = file_code.prefix + file_code.suffix\n            content_length = None  # 初始化为 None，表示未知大小\n            \n            # 尝试获取文件大小\n            try:\n                stat_result = await self.operator.stat(await file_code.get_file_path())\n                if hasattr(stat_result, 'content_length') and stat_result.content_length:\n                    content_length = stat_result.content_length\n                elif hasattr(stat_result, 'size') and stat_result.size:\n                    content_length = stat_result.size\n            except Exception:\n                # 如果获取大小失败，则不提供 Content-Length\n                pass\n            \n            # 尝试使用流式读取器\n            try:\n                # OpenDAL 可能提供 reader 方法返回一个异步读取器\n                reader = await self.operator.reader(await file_code.get_file_path())\n            except AttributeError:\n                # 如果 reader 方法不存在，回退到全量读取（兼容旧版本）\n                content = await self.operator.read(await file_code.get_file_path())\n                encoded_filename = quote(filename, safe='')\n                headers = {\n                    \"Content-Disposition\": f\"attachment; filename*=UTF-8''{encoded_filename}\"\n                }\n                if content_length is not None:\n                    headers[\"Content-Length\"] = str(content_length)\n                return Response(\n                    content, headers=headers, media_type=\"application/octet-stream\"\n                )\n            \n            async def stream_generator():\n                chunk_size = 65536\n                while True:\n                    chunk = await reader.read(chunk_size)\n                    if not chunk:\n                        break\n                    yield chunk\n            \n            encoded_filename = quote(filename, safe='')\n            headers = {\n                \"Content-Disposition\": f\"attachment; filename*=UTF-8''{encoded_filename}\"\n            }\n            if content_length is not None:\n                headers[\"Content-Length\"] = str(content_length)\n            return StreamingResponse(\n                stream_generator(),\n                media_type=\"application/octet-stream\",\n                headers=headers\n            )\n        except Exception as e:\n            logger.info(e)\n            raise HTTPException(status_code=404, detail=\"文件已过期删除\")\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"保存分片到 OpenDAL 存储\"\"\"\n        chunk_path = str(Path(save_path).parent / \"chunks\" / upload_id / f\"{chunk_index}.part\")\n        await self.operator.write(chunk_path, chunk_data)\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"合并 OpenDAL 存储上的分片文件，使用临时文件避免内存问题\"\"\"\n        file_sha256 = hashlib.sha256()\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n\n        # 使用临时文件存储合并数据\n        with tempfile.NamedTemporaryFile(delete=False) as temp_file:\n            temp_path = temp_file.name\n\n        try:\n            async with aiofiles.open(temp_path, 'wb') as out_file:\n                for i in range(chunk_info.total_chunks):\n                    chunk_path = f\"{chunk_dir}/{i}.part\"\n                    chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()\n                    if not chunk_record:\n                        raise ValueError(f\"分片{i}记录不存在\")\n\n                    try:\n                        chunk_data = await self.operator.read(chunk_path)\n                    except Exception as e:\n                        raise ValueError(f\"分片{i}文件不存在: {e}\")\n\n                    current_hash = hashlib.sha256(chunk_data).hexdigest()\n                    if current_hash != chunk_record.chunk_hash:\n                        raise ValueError(f\"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}\")\n\n                    file_sha256.update(chunk_data)\n                    await out_file.write(chunk_data)\n                    del chunk_data  # 释放内存\n\n            # 读取临时文件并写入存储\n            async with aiofiles.open(temp_path, 'rb') as f:\n                merged_content = await f.read()\n            await self.operator.write(save_path, merged_content)\n        finally:\n            # 清理临时文件\n            if os.path.exists(temp_path):\n                os.unlink(temp_path)\n        \n        return save_path, file_sha256.hexdigest()\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"清理 OpenDAL 存储上的临时分片文件\"\"\"\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n        try:\n            # OpenDAL 支持递归删除\n            await self.operator.remove_all(chunk_dir)\n        except Exception as e:\n            logger.info(f\"清理 OpenDAL 分片时出错: {e}\")\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在于OpenDAL存储\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        try:\n            await self.operator.stat(save_path)\n            return True\n        except Exception:\n            return False\n\n\nclass WebDAVFileStorage(FileStorageInterface):\n    _instance: Optional[\"WebDAVFileStorage\"] = None\n\n    def __init__(self):\n        if not hasattr(self, \"_initialized\"):\n            self.base_url = settings.webdav_url.rstrip(\"/\") + \"/\"\n            self.auth = aiohttp.BasicAuth(\n                login=settings.webdav_username, password=settings.webdav_password\n            )\n            self._initialized = True\n\n    def _build_url(self, path: str) -> str:\n        encoded_path = quote(str(path.replace(\"\\\\\", \"/\").lstrip(\"/\")).lstrip(\"/\"))\n        return f\"{self.base_url}{encoded_path}\"\n\n    async def _mkdir_p(self, directory_path: str):\n        \"\"\"递归创建目录（类似mkdir -p）\"\"\"\n        path_obj = Path(unquote(directory_path))\n        current_path = \"\"\n\n        async with aiohttp.ClientSession(auth=self.auth) as session:\n            # 逐级检查目录是否存在\n            for part in path_obj.parts:\n                current_path = str(Path(current_path) / part)\n                url = self._build_url(current_path)\n\n                # 检查目录是否存在\n                async with session.head(url) as resp:\n                    if resp.status == 404:\n                        # 创建目录\n                        async with session.request(\"MKCOL\", url) as mkcol_resp:\n                            if mkcol_resp.status not in (200, 201, 409):\n                                content = await mkcol_resp.text()\n                                raise HTTPException(\n                                    status_code=mkcol_resp.status,\n                                    detail=f\"目录创建失败: {content[:200]}\",\n                                )\n\n    async def _is_dir_empty(self, dir_path: str) -> bool:\n        \"\"\"检查目录是否为空\"\"\"\n        url = self._build_url(dir_path)\n\n        async with aiohttp.ClientSession(auth=self.auth) as session:\n            async with session.request(\"PROPFIND\", url, headers={\"Depth\": \"1\"}) as resp:\n                if resp.status != 207:  # 207 是 Multi-Status 响应\n                    return False\n                content = await resp.text()\n                # 如果只有一个 response（当前目录），说明目录为空\n                return content.count(\"<D:response>\") <= 1\n\n    async def _delete_empty_dirs(self, file_path: str, session: aiohttp.ClientSession):\n        \"\"\"递归删除空目录\"\"\"\n        path_obj = Path(file_path)\n        current_path = path_obj.parent\n\n        while str(current_path) != \".\":\n            if not await self._is_dir_empty(str(current_path)):\n                break\n\n            url = self._build_url(str(current_path))\n            async with session.delete(url) as resp:\n                if resp.status not in (200, 204, 404):\n                    break\n\n            current_path = current_path.parent\n\n    async def save_file(self, file: UploadFile, save_path: str):\n        \"\"\"保存文件（自动创建目录，流式上传）\"\"\"\n        path_obj = Path(save_path)\n        directory_path = str(path_obj.parent)\n        # 提取原始文件名并进行清理\n        filename = await sanitize_filename(path_obj.name)\n        # 构建安全的保存路径\n        safe_save_path = str(Path(directory_path) / filename)\n\n        try:\n            # 先创建目录结构\n            await self._mkdir_p(directory_path)\n            # 上传文件（流式）\n            url = self._build_url(safe_save_path)\n\n            async def file_sender():\n                \"\"\"流式读取文件内容\"\"\"\n                chunk_size = 256 * 1024  # 256KB chunks\n                while True:\n                    chunk = await asyncio.to_thread(file.file.read, chunk_size)\n                    if not chunk:\n                        break\n                    yield chunk\n\n            async with aiohttp.ClientSession(auth=self.auth) as session:\n                async with session.put(\n                        url,\n                        data=file_sender(),\n                        headers={\"Content-Type\": file.content_type or \"application/octet-stream\"}\n                ) as resp:\n                    if resp.status not in (200, 201, 204):\n                        content = await resp.text()\n                        raise HTTPException(\n                            status_code=resp.status,\n                            detail=f\"文件上传失败: {content[:200]}\",\n                        )\n        except aiohttp.ClientError as e:\n            raise HTTPException(\n                status_code=503, detail=f\"WebDAV连接异常: {str(e)}\")\n\n    async def delete_file(self, file_code: FileCodes):\n        \"\"\"删除WebDAV文件及空目录\"\"\"\n        file_path = await file_code.get_file_path()\n        url = self._build_url(file_path)\n        try:\n            async with aiohttp.ClientSession(auth=self.auth) as session:\n                # 删除文件\n                async with session.delete(url) as resp:\n                    if resp.status not in (200, 204, 404):\n                        content = await resp.text()\n                        raise HTTPException(\n                            status_code=resp.status,\n                            detail=f\"WebDAV删除失败: {content[:200]}\",\n                        )\n\n                # 使用同一个 session 删除空目录\n                await self._delete_empty_dirs(file_path, session)\n\n        except aiohttp.ClientError as e:\n            raise HTTPException(\n                status_code=503, detail=f\"WebDAV连接异常: {str(e)}\")\n\n    async def get_file_url(self, file_code: FileCodes):\n        return await get_file_url(file_code.code)\n\n    async def get_file_response(self, file_code: FileCodes):\n        \"\"\"获取文件响应（代理模式）\"\"\"\n        try:\n            filename = file_code.prefix + file_code.suffix\n            url = self._build_url(await file_code.get_file_path())\n            content_length = None  # 初始化为 None，表示未知大小\n            \n            # 创建ClientSession并复用（包含认证头）\n            session = aiohttp.ClientSession(headers={\n                \"Authorization\": f\"Basic {base64.b64encode(f'{settings.webdav_username}:{settings.webdav_password}'.encode()).decode()}\"\n            })\n            \n            # 尝试发送HEAD请求获取Content-Length\n            try:\n                async with session.head(url) as resp:\n                    if resp.status == 200 and 'Content-Length' in resp.headers:\n                        content_length = int(resp.headers['Content-Length'])\n            except Exception:\n                # 如果HEAD请求失败，则不提供 Content-Length\n                pass\n            \n            async def stream_generator():\n                try:\n                    async with session.get(url) as resp:\n                        if resp.status != 200:\n                            raise HTTPException(\n                                status_code=resp.status,\n                                detail=f\"文件获取失败{resp.status}: {await resp.text()}\",\n                            )\n                        chunk_size = 65536\n                        while True:\n                            chunk = await resp.content.read(chunk_size)\n                            if not chunk:\n                                break\n                            yield chunk\n                finally:\n                    await session.close()\n            \n            encoded_filename = quote(filename, safe='')\n            headers = {\n                \"Content-Disposition\": f\"attachment; filename*=UTF-8''{encoded_filename}\"\n            }\n            if content_length is not None:\n                headers[\"Content-Length\"] = str(content_length)\n            return StreamingResponse(\n                stream_generator(),\n                media_type=\"application/octet-stream\",\n                headers=headers\n            )\n        except aiohttp.ClientError as e:\n            raise HTTPException(\n                status_code=503, detail=f\"WebDAV连接异常: {str(e)}\")\n\n    async def save_chunk(self, upload_id: str, chunk_index: int, chunk_data: bytes, chunk_hash: str, save_path: str):\n        \"\"\"保存分片到 WebDAV\"\"\"\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n        chunk_path = f\"{chunk_dir}/{chunk_index}.part\"\n        \n        # 先创建目录结构\n        await self._mkdir_p(chunk_dir)\n        \n        chunk_url = self._build_url(chunk_path)\n        async with aiohttp.ClientSession(auth=self.auth) as session:\n            async with session.put(chunk_url, data=chunk_data) as resp:\n                if resp.status not in (200, 201, 204):\n                    content = await resp.text()\n                    raise HTTPException(\n                        status_code=resp.status,\n                        detail=f\"分片上传失败: {content[:200]}\"\n                    )\n\n    async def merge_chunks(self, upload_id: str, chunk_info: UploadChunk, save_path: str) -> tuple[str, str]:\n        \"\"\"\n        合并 WebDAV 上的分片文件\n        使用临时文件避免内存问题\n        \"\"\"\n        file_sha256 = hashlib.sha256()\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n\n        # 使用临时文件存储合并数据，避免内存问题\n        with tempfile.NamedTemporaryFile(delete=False) as temp_file:\n            temp_path = temp_file.name\n\n        try:\n            async with aiohttp.ClientSession(auth=self.auth) as session:\n                # 按顺序读取并验证每个分片，写入临时文件\n                async with aiofiles.open(temp_path, 'wb') as out_file:\n                    for i in range(chunk_info.total_chunks):\n                        chunk_path = f\"{chunk_dir}/{i}.part\"\n                        chunk_url = self._build_url(chunk_path)\n\n                        # 获取分片记录\n                        chunk_record = await UploadChunk.filter(upload_id=upload_id, chunk_index=i).first()\n                        if not chunk_record:\n                            raise ValueError(f\"分片{i}记录不存在\")\n\n                        # 下载分片数据\n                        async with session.get(chunk_url) as resp:\n                            if resp.status != 200:\n                                raise ValueError(f\"分片{i}文件不存在或无法访问\")\n                            chunk_data = await resp.read()\n\n                        # 验证哈希\n                        current_hash = hashlib.sha256(chunk_data).hexdigest()\n                        if current_hash != chunk_record.chunk_hash:\n                            raise ValueError(f\"分片{i}哈希不匹配: 期望 {chunk_record.chunk_hash}, 实际 {current_hash}\")\n\n                        file_sha256.update(chunk_data)\n                        await out_file.write(chunk_data)\n                        del chunk_data  # 释放内存\n\n                # 确保目标目录存在\n                output_dir = str(Path(save_path).parent)\n                await self._mkdir_p(output_dir)\n\n                # 流式上传合并后的文件\n                output_url = self._build_url(save_path)\n\n                async def file_sender():\n                    async with aiofiles.open(temp_path, 'rb') as f:\n                        while True:\n                            chunk = await f.read(256 * 1024)\n                            if not chunk:\n                                break\n                            yield chunk\n\n                async with session.put(output_url, data=file_sender()) as resp:\n                    if resp.status not in (200, 201, 204):\n                        content = await resp.text()\n                        raise HTTPException(\n                            status_code=resp.status,\n                            detail=f\"合并文件上传失败: {content[:200]}\"\n                        )\n        finally:\n            # 清理临时文件\n            if os.path.exists(temp_path):\n                os.unlink(temp_path)\n\n        return save_path, file_sha256.hexdigest()\n\n    async def clean_chunks(self, upload_id: str, save_path: str):\n        \"\"\"\n        清理 WebDAV 上的临时分片文件\n        :param upload_id: 上传会话ID\n        :param save_path: 文件保存路径\n        \"\"\"\n        chunk_dir = str(Path(save_path).parent / \"chunks\" / upload_id)\n        chunk_dir_url = self._build_url(chunk_dir)\n        async with aiohttp.ClientSession(auth=self.auth) as session:\n            try:\n                # 检查分片目录是否存在\n                async with session.request(\"PROPFIND\", chunk_dir_url, headers={\"Depth\": \"1\"}) as resp:\n                    if resp.status == 207:  # 207 表示 Multi-Status\n                        # 获取目录下的所有分片文件\n                        xml_data = await resp.text()\n                        file_paths = re.findall(\n                            r'<D:href>(.*?)</D:href>', xml_data)\n                        for file_path in file_paths:\n                            if file_path.endswith(\".part\"):\n                                # 删除分片文件\n                                file_url = self._build_url(file_path)\n                                async with session.delete(file_url) as delete_resp:\n                                    if delete_resp.status not in (200, 204, 404):\n                                        logger.info(f\"删除分片文件失败: {file_path}\")\n\n                        # 删除分片目录\n                        async with session.delete(chunk_dir_url) as delete_resp:\n                            if delete_resp.status not in (200, 204, 404):\n                                logger.info(f\"删除分片目录失败: {chunk_dir_url}\")\n                    else:\n                        logger.info(f\"分片目录不存在: {chunk_dir_url}\")\n            except Exception as e:\n                logger.info(f\"清理 WebDAV 分片时出错: {e}\")\n\n    async def file_exists(self, save_path: str) -> bool:\n        \"\"\"\n        检查文件是否存在于WebDAV\n        :param save_path: 文件路径\n        :return: 文件是否存在\n        \"\"\"\n        url = self._build_url(save_path)\n        async with aiohttp.ClientSession(auth=self.auth) as session:\n            async with session.head(url) as resp:\n                return resp.status == 200\n\n\nstorages = {\n    \"local\": SystemFileStorage,\n    \"s3\": S3FileStorage,\n    \"onedrive\": OneDriveFileStorage,\n    \"opendal\": OpenDALFileStorage,\n    \"webdav\": WebDAVFileStorage,\n}\n"
  },
  {
    "path": "core/tasks.py",
    "content": "# @Time    : 2023/8/15 22:00\n# @Author  : Lan\n# @File    : tasks.py\n# @Software: PyCharm\nimport asyncio\nimport datetime\nimport logging\nimport os\n\nfrom tortoise.expressions import Q\n\nfrom apps.base.models import FileCodes, UploadChunk\nfrom apps.base.utils import ip_limit, get_chunk_file_path_name\nfrom core.config import refresh_settings\nfrom core.settings import settings, data_root\nfrom core.storage import FileStorageInterface, storages\nfrom core.utils import get_now\n\n\nasync def delete_expire_files():\n    while True:\n        try:\n            await refresh_settings()\n            file_storage: FileStorageInterface = storages[settings.file_storage]()\n            # 遍历 share目录下的所有文件夹，删除空的文件夹，并判断父目录是否为空，如果为空也删除\n            if settings.file_storage == \"local\":\n                for root, dirs, files in os.walk(f\"{data_root}/share/data\"):\n                    if not dirs and not files:\n                        os.rmdir(root)\n            await ip_limit[\"error\"].remove_expired_ip()\n            await ip_limit[\"upload\"].remove_expired_ip()\n            expire_data = await FileCodes.filter(\n                Q(expired_at__lt=await get_now()) | Q(expired_count=0)\n            ).all()\n            for exp in expire_data:\n                try:\n                    await file_storage.delete_file(exp)\n                except Exception as e:\n                    logging.error(f\"删除过期文件失败 code={exp.code}: {e}\")\n                try:\n                    await exp.delete()\n                except Exception as e:\n                    logging.error(f\"删除记录失败 code={exp.code}: {e}\")\n        except Exception as e:\n            logging.error(e)\n        finally:\n            await asyncio.sleep(600)\n\n\nasync def clean_incomplete_uploads():\n    while True:\n        try:\n            await refresh_settings()\n            file_storage: FileStorageInterface = storages[settings.file_storage]()\n            expire_hours = getattr(settings, \"chunk_expire_hours\", 24)\n            now = await get_now()\n            expire_time = now - datetime.timedelta(hours=expire_hours)\n            expired_sessions = await UploadChunk.filter(\n                chunk_index=-1, created_at__lt=expire_time\n            ).all()\n\n            for session in expired_sessions:\n                try:\n                    save_path = session.save_path\n                    if not save_path:\n                        _, _, _, _, save_path = await get_chunk_file_path_name(\n                            session.file_name, session.upload_id\n                        )\n                    await file_storage.clean_chunks(session.upload_id, save_path)\n                except Exception as e:\n                    logging.error(\n                        f\"清理分片文件失败 upload_id={session.upload_id}: {e}\"\n                    )\n\n                try:\n                    await UploadChunk.filter(upload_id=session.upload_id).delete()\n                    logging.info(f\"已清理过期上传会话 upload_id={session.upload_id}\")\n                except Exception as e:\n                    logging.error(\n                        f\"删除分片记录失败 upload_id={session.upload_id}: {e}\"\n                    )\n\n        except Exception as e:\n            logging.error(f\"清理未完成上传任务异常: {e}\")\n        finally:\n            await asyncio.sleep(3600)\n"
  },
  {
    "path": "core/utils.py",
    "content": "# @Time    : 2023/8/13 19:54\n# @Author  : Lan\n# @File    : utils.py\n# @Software: PyCharm\nimport datetime\nimport hashlib\nimport os\nimport random\nimport re\nimport string\nimport time\n\nfrom core.settings import settings\n\n\nasync def get_random_num():\n    \"\"\"\n    获取随机数\n    :return:\n    \"\"\"\n    return random.randint(10000, 99999)\n\n\nr_s = string.ascii_uppercase + string.digits\n\n\nasync def get_random_string():\n    \"\"\"\n    获取随机字符串\n    :return:\n    \"\"\"\n    return \"\".join(random.choice(r_s) for _ in range(5))\n\n\nasync def get_now():\n    \"\"\"\n    获取当前时间\n    :return:\n    \"\"\"\n    return datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=8)))\n\n\nasync def get_select_token(code: str):\n    \"\"\"\n    获取下载token\n    :param code:\n    :return:\n    \"\"\"\n    token = settings.admin_token\n    return hashlib.sha256(\n        f\"{code}{int(time.time() / 1000)}000{token}\".encode()\n    ).hexdigest()\n\n\nasync def get_file_url(code: str):\n    \"\"\"\n    对于需要通过服务器中转下载的服务，获取文件下载地址\n    :param code:\n    :return:\n    \"\"\"\n    return f\"/share/download?key={await get_select_token(code)}&code={code}\"\n\n\nasync def max_save_times_desc(max_save_seconds: int):\n    \"\"\"\n    获取最大保存时间的描述\n    :param max_save_seconds:\n    :return:\n    \"\"\"\n\n    def gen_desc_zh(value: int, desc: str):\n        if value > 0:\n            return f\"{value}{desc}\"\n        else:\n            return \"\"\n\n    def gen_desc_en(value: int, desc: str):\n        if value > 0:\n            ret = f\"{value} {desc}\"\n            if value > 1:\n                ret += \"s\"\n            ret += \" \"\n            return ret\n        else:\n            return \"\"\n\n    max_timedelta = datetime.timedelta(seconds=max_save_seconds)\n    desc_zh, desc_en = \"最长保存时间：\", \"Max save time: \"\n    desc_zh += gen_desc_zh(max_timedelta.days, \"天\")\n    desc_en += gen_desc_en(max_timedelta.days, \"day\")\n    desc_zh += gen_desc_zh(max_timedelta.seconds // 3600, \"小时\")\n    desc_en += gen_desc_en(max_timedelta.seconds // 3600, \"hour\")\n    desc_zh += gen_desc_zh(max_timedelta.seconds % 3600 // 60, \"分钟\")\n    desc_en += gen_desc_en(max_timedelta.seconds % 3600 // 60, \"minute\")\n    desc_zh += gen_desc_zh(max_timedelta.seconds % 60, \"秒\")\n    desc_en += gen_desc_en(max_timedelta.seconds % 60, \"second\")\n    return desc_zh, desc_en\n\n\ndef hash_password(password: str) -> str:\n    \"\"\"\n    使用 SHA256 + salt 哈希密码\n    返回格式: sha256$<salt>$<hash>\n    \"\"\"\n    salt = os.urandom(16).hex()\n    password_hash = hashlib.sha256(f\"{salt}{password}\".encode()).hexdigest()\n    return f\"sha256${salt}${password_hash}\"\n\n\ndef verify_password(password: str, hashed: str) -> bool:\n    \"\"\"\n    验证密码是否匹配\n    支持新格式 (sha256$salt$hash) 和旧格式 (明文)\n    \"\"\"\n    if not hashed:\n        return False\n\n    # 新格式: sha256$salt$hash\n    if hashed.startswith(\"sha256$\"):\n        parts = hashed.split(\"$\")\n        if len(parts) != 3:\n            return False\n        _, salt, stored_hash = parts\n        password_hash = hashlib.sha256(f\"{salt}{password}\".encode()).hexdigest()\n        return password_hash == stored_hash\n\n    # 旧格式: 明文比较 (兼容迁移前的数据)\n    return password == hashed\n\n\ndef is_password_hashed(password: str) -> bool:\n    \"\"\"\n    检查密码是否已经是哈希格式\n    \"\"\"\n    return password.startswith(\"sha256$\") and len(password.split(\"$\")) == 3\n\n\nasync def sanitize_filename(filename: str) -> str:\n    \"\"\"\n    安全处理文件名：\n    1. 剥离路径只保留文件名\n    2. 替换非法字符\n    3. 处理空文件名情况\n    \"\"\"\n    filename = os.path.basename(filename)\n    illegal_chars = r'[\\\\/*?:\"<>|\\x00-\\x1F]'  # 包含控制字符\n    # 替换非法字符为下划线\n    cleaned = re.sub(illegal_chars, \"_\", filename)\n    # 处理空格（可选替换为_）\n    cleaned = cleaned.replace(\" \", \"_\")\n    # 处理连续下划线\n    cleaned = re.sub(r\"_+\", \"_\", cleaned)\n    # 处理首尾特殊字符\n    cleaned = cleaned.strip(\"._\")\n    # 处理空文件名情况\n    if not cleaned:\n        cleaned = \"unnamed_file\"\n    # 长度限制（按需调整）\n    return cleaned[:255]\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3\"\nservices:\n  file-code-box:\n    image: lanol/filecodebox:latest\n    volumes:\n      - fcb-data:/app/data:rw\n    restart: unless-stopped\n    ports:\n      - \"12345:12345\"\nvolumes:\n  fcb-data:\n    external: false"
  },
  {
    "path": "docs/.vitepress/cache/deps/_metadata.json",
    "content": "{\n  \"hash\": \"8f855eaf\",\n  \"configHash\": \"1b3ca22f\",\n  \"lockfileHash\": \"bd28b2c2\",\n  \"browserHash\": \"29e84937\",\n  \"optimized\": {\n    \"vue\": {\n      \"src\": \"../../../node_modules/.pnpm/vue@3.5.13/node_modules/vue/dist/vue.runtime.esm-bundler.js\",\n      \"file\": \"vue.js\",\n      \"fileHash\": \"3215885f\",\n      \"needsInterop\": false\n    },\n    \"vitepress > @vue/devtools-api\": {\n      \"src\": \"../../../node_modules/.pnpm/@vue+devtools-api@7.7.1/node_modules/@vue/devtools-api/dist/index.js\",\n      \"file\": \"vitepress___@vue_devtools-api.js\",\n      \"fileHash\": \"5a5f95ef\",\n      \"needsInterop\": false\n    },\n    \"vitepress > @vueuse/core\": {\n      \"src\": \"../../../node_modules/.pnpm/@vueuse+core@12.5.0/node_modules/@vueuse/core/index.mjs\",\n      \"file\": \"vitepress___@vueuse_core.js\",\n      \"fileHash\": \"0fbf66f0\",\n      \"needsInterop\": false\n    },\n    \"vitepress > @vueuse/integrations/useFocusTrap\": {\n      \"src\": \"../../../node_modules/.pnpm/@vueuse+integrations@12.5.0_focus-trap@7.6.4/node_modules/@vueuse/integrations/useFocusTrap.mjs\",\n      \"file\": \"vitepress___@vueuse_integrations_useFocusTrap.js\",\n      \"fileHash\": \"91b03896\",\n      \"needsInterop\": false\n    },\n    \"vitepress > mark.js/src/vanilla.js\": {\n      \"src\": \"../../../node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js\",\n      \"file\": \"vitepress___mark__js_src_vanilla__js.js\",\n      \"fileHash\": \"99e4f81c\",\n      \"needsInterop\": false\n    },\n    \"vitepress > minisearch\": {\n      \"src\": \"../../../node_modules/.pnpm/minisearch@7.1.1/node_modules/minisearch/dist/es/index.js\",\n      \"file\": \"vitepress___minisearch.js\",\n      \"fileHash\": \"cc176c9c\",\n      \"needsInterop\": false\n    }\n  },\n  \"chunks\": {\n    \"chunk-KT7LHMJ2\": {\n      \"file\": \"chunk-KT7LHMJ2.js\"\n    },\n    \"chunk-CQOUZRMK\": {\n      \"file\": \"chunk-CQOUZRMK.js\"\n    }\n  }\n}"
  },
  {
    "path": "docs/.vitepress/cache/deps/chunk-CQOUZRMK.js",
    "content": "// node_modules/.pnpm/@vue+shared@3.5.13/node_modules/@vue/shared/dist/shared.esm-bundler.js\nfunction makeMap(str) {\n  const map2 = /* @__PURE__ */ Object.create(null);\n  for (const key of str.split(\",\")) map2[key] = 1;\n  return (val) => val in map2;\n}\nvar EMPTY_OBJ = true ? Object.freeze({}) : {};\nvar EMPTY_ARR = true ? Object.freeze([]) : [];\nvar NOOP = () => {\n};\nvar NO = () => false;\nvar isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // uppercase letter\n(key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97);\nvar isModelListener = (key) => key.startsWith(\"onUpdate:\");\nvar extend = Object.assign;\nvar remove = (arr, el) => {\n  const i = arr.indexOf(el);\n  if (i > -1) {\n    arr.splice(i, 1);\n  }\n};\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nvar hasOwn = (val, key) => hasOwnProperty.call(val, key);\nvar isArray = Array.isArray;\nvar isMap = (val) => toTypeString(val) === \"[object Map]\";\nvar isSet = (val) => toTypeString(val) === \"[object Set]\";\nvar isDate = (val) => toTypeString(val) === \"[object Date]\";\nvar isRegExp = (val) => toTypeString(val) === \"[object RegExp]\";\nvar isFunction = (val) => typeof val === \"function\";\nvar isString = (val) => typeof val === \"string\";\nvar isSymbol = (val) => typeof val === \"symbol\";\nvar isObject = (val) => val !== null && typeof val === \"object\";\nvar isPromise = (val) => {\n  return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch);\n};\nvar objectToString = Object.prototype.toString;\nvar toTypeString = (value) => objectToString.call(value);\nvar toRawType = (value) => {\n  return toTypeString(value).slice(8, -1);\n};\nvar isPlainObject = (val) => toTypeString(val) === \"[object Object]\";\nvar isIntegerKey = (key) => isString(key) && key !== \"NaN\" && key[0] !== \"-\" && \"\" + parseInt(key, 10) === key;\nvar isReservedProp = makeMap(\n  // the leading comma is intentional so empty string \"\" is also included\n  \",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted\"\n);\nvar isBuiltInDirective = makeMap(\n  \"bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo\"\n);\nvar cacheStringFunction = (fn) => {\n  const cache = /* @__PURE__ */ Object.create(null);\n  return (str) => {\n    const hit = cache[str];\n    return hit || (cache[str] = fn(str));\n  };\n};\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cacheStringFunction(\n  (str) => {\n    return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : \"\");\n  }\n);\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cacheStringFunction(\n  (str) => str.replace(hyphenateRE, \"-$1\").toLowerCase()\n);\nvar capitalize = cacheStringFunction((str) => {\n  return str.charAt(0).toUpperCase() + str.slice(1);\n});\nvar toHandlerKey = cacheStringFunction(\n  (str) => {\n    const s = str ? `on${capitalize(str)}` : ``;\n    return s;\n  }\n);\nvar hasChanged = (value, oldValue) => !Object.is(value, oldValue);\nvar invokeArrayFns = (fns, ...arg) => {\n  for (let i = 0; i < fns.length; i++) {\n    fns[i](...arg);\n  }\n};\nvar def = (obj, key, value, writable = false) => {\n  Object.defineProperty(obj, key, {\n    configurable: true,\n    enumerable: false,\n    writable,\n    value\n  });\n};\nvar looseToNumber = (val) => {\n  const n = parseFloat(val);\n  return isNaN(n) ? val : n;\n};\nvar toNumber = (val) => {\n  const n = isString(val) ? Number(val) : NaN;\n  return isNaN(n) ? val : n;\n};\nvar _globalThis;\nvar getGlobalThis = () => {\n  return _globalThis || (_globalThis = typeof globalThis !== \"undefined\" ? globalThis : typeof self !== \"undefined\" ? self : typeof window !== \"undefined\" ? window : typeof global !== \"undefined\" ? global : {});\n};\nvar GLOBALS_ALLOWED = \"Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error,Symbol\";\nvar isGloballyAllowed = makeMap(GLOBALS_ALLOWED);\nfunction normalizeStyle(value) {\n  if (isArray(value)) {\n    const res = {};\n    for (let i = 0; i < value.length; i++) {\n      const item = value[i];\n      const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item);\n      if (normalized) {\n        for (const key in normalized) {\n          res[key] = normalized[key];\n        }\n      }\n    }\n    return res;\n  } else if (isString(value) || isObject(value)) {\n    return value;\n  }\n}\nvar listDelimiterRE = /;(?![^(]*\\))/g;\nvar propertyDelimiterRE = /:([^]+)/;\nvar styleCommentRE = /\\/\\*[^]*?\\*\\//g;\nfunction parseStringStyle(cssText) {\n  const ret = {};\n  cssText.replace(styleCommentRE, \"\").split(listDelimiterRE).forEach((item) => {\n    if (item) {\n      const tmp = item.split(propertyDelimiterRE);\n      tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());\n    }\n  });\n  return ret;\n}\nfunction stringifyStyle(styles) {\n  if (!styles) return \"\";\n  if (isString(styles)) return styles;\n  let ret = \"\";\n  for (const key in styles) {\n    const value = styles[key];\n    if (isString(value) || typeof value === \"number\") {\n      const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key);\n      ret += `${normalizedKey}:${value};`;\n    }\n  }\n  return ret;\n}\nfunction normalizeClass(value) {\n  let res = \"\";\n  if (isString(value)) {\n    res = value;\n  } else if (isArray(value)) {\n    for (let i = 0; i < value.length; i++) {\n      const normalized = normalizeClass(value[i]);\n      if (normalized) {\n        res += normalized + \" \";\n      }\n    }\n  } else if (isObject(value)) {\n    for (const name in value) {\n      if (value[name]) {\n        res += name + \" \";\n      }\n    }\n  }\n  return res.trim();\n}\nfunction normalizeProps(props) {\n  if (!props) return null;\n  let { class: klass, style } = props;\n  if (klass && !isString(klass)) {\n    props.class = normalizeClass(klass);\n  }\n  if (style) {\n    props.style = normalizeStyle(style);\n  }\n  return props;\n}\nvar HTML_TAGS = \"html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot\";\nvar SVG_TAGS = \"svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view\";\nvar MATH_TAGS = \"annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics\";\nvar VOID_TAGS = \"area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr\";\nvar isHTMLTag = makeMap(HTML_TAGS);\nvar isSVGTag = makeMap(SVG_TAGS);\nvar isMathMLTag = makeMap(MATH_TAGS);\nvar isVoidTag = makeMap(VOID_TAGS);\nvar specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`;\nvar isSpecialBooleanAttr = makeMap(specialBooleanAttrs);\nvar isBooleanAttr = makeMap(\n  specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`\n);\nfunction includeBooleanAttr(value) {\n  return !!value || value === \"\";\n}\nvar isKnownHtmlAttr = makeMap(\n  `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`\n);\nvar isKnownSvgAttr = makeMap(\n  `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan`\n);\nvar isKnownMathMLAttr = makeMap(\n  `accent,accentunder,actiontype,align,alignmentscope,altimg,altimg-height,altimg-valign,altimg-width,alttext,bevelled,close,columnsalign,columnlines,columnspan,denomalign,depth,dir,display,displaystyle,encoding,equalcolumns,equalrows,fence,fontstyle,fontweight,form,frame,framespacing,groupalign,height,href,id,indentalign,indentalignfirst,indentalignlast,indentshift,indentshiftfirst,indentshiftlast,indextype,justify,largetop,largeop,lquote,lspace,mathbackground,mathcolor,mathsize,mathvariant,maxsize,minlabelspacing,mode,other,overflow,position,rowalign,rowlines,rowspan,rquote,rspace,scriptlevel,scriptminsize,scriptsizemultiplier,selection,separator,separators,shift,side,src,stackalign,stretchy,subscriptshift,superscriptshift,symmetric,voffset,width,widths,xlink:href,xlink:show,xlink:type,xmlns`\n);\nfunction isRenderableAttrValue(value) {\n  if (value == null) {\n    return false;\n  }\n  const type = typeof value;\n  return type === \"string\" || type === \"number\" || type === \"boolean\";\n}\nvar cssVarNameEscapeSymbolsRE = /[ !\"#$%&'()*+,./:;<=>?@[\\\\\\]^`{|}~]/g;\nfunction getEscapedCssVarName(key, doubleEscape) {\n  return key.replace(\n    cssVarNameEscapeSymbolsRE,\n    (s) => doubleEscape ? s === '\"' ? '\\\\\\\\\\\\\"' : `\\\\\\\\${s}` : `\\\\${s}`\n  );\n}\nfunction looseCompareArrays(a, b) {\n  if (a.length !== b.length) return false;\n  let equal = true;\n  for (let i = 0; equal && i < a.length; i++) {\n    equal = looseEqual(a[i], b[i]);\n  }\n  return equal;\n}\nfunction looseEqual(a, b) {\n  if (a === b) return true;\n  let aValidType = isDate(a);\n  let bValidType = isDate(b);\n  if (aValidType || bValidType) {\n    return aValidType && bValidType ? a.getTime() === b.getTime() : false;\n  }\n  aValidType = isSymbol(a);\n  bValidType = isSymbol(b);\n  if (aValidType || bValidType) {\n    return a === b;\n  }\n  aValidType = isArray(a);\n  bValidType = isArray(b);\n  if (aValidType || bValidType) {\n    return aValidType && bValidType ? looseCompareArrays(a, b) : false;\n  }\n  aValidType = isObject(a);\n  bValidType = isObject(b);\n  if (aValidType || bValidType) {\n    if (!aValidType || !bValidType) {\n      return false;\n    }\n    const aKeysCount = Object.keys(a).length;\n    const bKeysCount = Object.keys(b).length;\n    if (aKeysCount !== bKeysCount) {\n      return false;\n    }\n    for (const key in a) {\n      const aHasKey = a.hasOwnProperty(key);\n      const bHasKey = b.hasOwnProperty(key);\n      if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) {\n        return false;\n      }\n    }\n  }\n  return String(a) === String(b);\n}\nfunction looseIndexOf(arr, val) {\n  return arr.findIndex((item) => looseEqual(item, val));\n}\nvar isRef = (val) => {\n  return !!(val && val[\"__v_isRef\"] === true);\n};\nvar toDisplayString = (val) => {\n  return isString(val) ? val : val == null ? \"\" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? isRef(val) ? toDisplayString(val.value) : JSON.stringify(val, replacer, 2) : String(val);\n};\nvar replacer = (_key, val) => {\n  if (isRef(val)) {\n    return replacer(_key, val.value);\n  } else if (isMap(val)) {\n    return {\n      [`Map(${val.size})`]: [...val.entries()].reduce(\n        (entries, [key, val2], i) => {\n          entries[stringifySymbol(key, i) + \" =>\"] = val2;\n          return entries;\n        },\n        {}\n      )\n    };\n  } else if (isSet(val)) {\n    return {\n      [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v))\n    };\n  } else if (isSymbol(val)) {\n    return stringifySymbol(val);\n  } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) {\n    return String(val);\n  }\n  return val;\n};\nvar stringifySymbol = (v, i = \"\") => {\n  var _a;\n  return (\n    // Symbol.description in es2019+ so we need to cast here to pass\n    // the lib: es2016 check\n    isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v\n  );\n};\n\n// node_modules/.pnpm/@vue+reactivity@3.5.13/node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js\nfunction warn(msg, ...args) {\n  console.warn(`[Vue warn] ${msg}`, ...args);\n}\nvar activeEffectScope;\nvar EffectScope = class {\n  constructor(detached = false) {\n    this.detached = detached;\n    this._active = true;\n    this.effects = [];\n    this.cleanups = [];\n    this._isPaused = false;\n    this.parent = activeEffectScope;\n    if (!detached && activeEffectScope) {\n      this.index = (activeEffectScope.scopes || (activeEffectScope.scopes = [])).push(\n        this\n      ) - 1;\n    }\n  }\n  get active() {\n    return this._active;\n  }\n  pause() {\n    if (this._active) {\n      this._isPaused = true;\n      let i, l;\n      if (this.scopes) {\n        for (i = 0, l = this.scopes.length; i < l; i++) {\n          this.scopes[i].pause();\n        }\n      }\n      for (i = 0, l = this.effects.length; i < l; i++) {\n        this.effects[i].pause();\n      }\n    }\n  }\n  /**\n   * Resumes the effect scope, including all child scopes and effects.\n   */\n  resume() {\n    if (this._active) {\n      if (this._isPaused) {\n        this._isPaused = false;\n        let i, l;\n        if (this.scopes) {\n          for (i = 0, l = this.scopes.length; i < l; i++) {\n            this.scopes[i].resume();\n          }\n        }\n        for (i = 0, l = this.effects.length; i < l; i++) {\n          this.effects[i].resume();\n        }\n      }\n    }\n  }\n  run(fn) {\n    if (this._active) {\n      const currentEffectScope = activeEffectScope;\n      try {\n        activeEffectScope = this;\n        return fn();\n      } finally {\n        activeEffectScope = currentEffectScope;\n      }\n    } else if (true) {\n      warn(`cannot run an inactive effect scope.`);\n    }\n  }\n  /**\n   * This should only be called on non-detached scopes\n   * @internal\n   */\n  on() {\n    activeEffectScope = this;\n  }\n  /**\n   * This should only be called on non-detached scopes\n   * @internal\n   */\n  off() {\n    activeEffectScope = this.parent;\n  }\n  stop(fromParent) {\n    if (this._active) {\n      this._active = false;\n      let i, l;\n      for (i = 0, l = this.effects.length; i < l; i++) {\n        this.effects[i].stop();\n      }\n      this.effects.length = 0;\n      for (i = 0, l = this.cleanups.length; i < l; i++) {\n        this.cleanups[i]();\n      }\n      this.cleanups.length = 0;\n      if (this.scopes) {\n        for (i = 0, l = this.scopes.length; i < l; i++) {\n          this.scopes[i].stop(true);\n        }\n        this.scopes.length = 0;\n      }\n      if (!this.detached && this.parent && !fromParent) {\n        const last = this.parent.scopes.pop();\n        if (last && last !== this) {\n          this.parent.scopes[this.index] = last;\n          last.index = this.index;\n        }\n      }\n      this.parent = void 0;\n    }\n  }\n};\nfunction effectScope(detached) {\n  return new EffectScope(detached);\n}\nfunction getCurrentScope() {\n  return activeEffectScope;\n}\nfunction onScopeDispose(fn, failSilently = false) {\n  if (activeEffectScope) {\n    activeEffectScope.cleanups.push(fn);\n  } else if (!failSilently) {\n    warn(\n      `onScopeDispose() is called when there is no active effect scope to be associated with.`\n    );\n  }\n}\nvar activeSub;\nvar pausedQueueEffects = /* @__PURE__ */ new WeakSet();\nvar ReactiveEffect = class {\n  constructor(fn) {\n    this.fn = fn;\n    this.deps = void 0;\n    this.depsTail = void 0;\n    this.flags = 1 | 4;\n    this.next = void 0;\n    this.cleanup = void 0;\n    this.scheduler = void 0;\n    if (activeEffectScope && activeEffectScope.active) {\n      activeEffectScope.effects.push(this);\n    }\n  }\n  pause() {\n    this.flags |= 64;\n  }\n  resume() {\n    if (this.flags & 64) {\n      this.flags &= ~64;\n      if (pausedQueueEffects.has(this)) {\n        pausedQueueEffects.delete(this);\n        this.trigger();\n      }\n    }\n  }\n  /**\n   * @internal\n   */\n  notify() {\n    if (this.flags & 2 && !(this.flags & 32)) {\n      return;\n    }\n    if (!(this.flags & 8)) {\n      batch(this);\n    }\n  }\n  run() {\n    if (!(this.flags & 1)) {\n      return this.fn();\n    }\n    this.flags |= 2;\n    cleanupEffect(this);\n    prepareDeps(this);\n    const prevEffect = activeSub;\n    const prevShouldTrack = shouldTrack;\n    activeSub = this;\n    shouldTrack = true;\n    try {\n      return this.fn();\n    } finally {\n      if (activeSub !== this) {\n        warn(\n          \"Active effect was not restored correctly - this is likely a Vue internal bug.\"\n        );\n      }\n      cleanupDeps(this);\n      activeSub = prevEffect;\n      shouldTrack = prevShouldTrack;\n      this.flags &= ~2;\n    }\n  }\n  stop() {\n    if (this.flags & 1) {\n      for (let link = this.deps; link; link = link.nextDep) {\n        removeSub(link);\n      }\n      this.deps = this.depsTail = void 0;\n      cleanupEffect(this);\n      this.onStop && this.onStop();\n      this.flags &= ~1;\n    }\n  }\n  trigger() {\n    if (this.flags & 64) {\n      pausedQueueEffects.add(this);\n    } else if (this.scheduler) {\n      this.scheduler();\n    } else {\n      this.runIfDirty();\n    }\n  }\n  /**\n   * @internal\n   */\n  runIfDirty() {\n    if (isDirty(this)) {\n      this.run();\n    }\n  }\n  get dirty() {\n    return isDirty(this);\n  }\n};\nvar batchDepth = 0;\nvar batchedSub;\nvar batchedComputed;\nfunction batch(sub, isComputed = false) {\n  sub.flags |= 8;\n  if (isComputed) {\n    sub.next = batchedComputed;\n    batchedComputed = sub;\n    return;\n  }\n  sub.next = batchedSub;\n  batchedSub = sub;\n}\nfunction startBatch() {\n  batchDepth++;\n}\nfunction endBatch() {\n  if (--batchDepth > 0) {\n    return;\n  }\n  if (batchedComputed) {\n    let e = batchedComputed;\n    batchedComputed = void 0;\n    while (e) {\n      const next = e.next;\n      e.next = void 0;\n      e.flags &= ~8;\n      e = next;\n    }\n  }\n  let error;\n  while (batchedSub) {\n    let e = batchedSub;\n    batchedSub = void 0;\n    while (e) {\n      const next = e.next;\n      e.next = void 0;\n      e.flags &= ~8;\n      if (e.flags & 1) {\n        try {\n          ;\n          e.trigger();\n        } catch (err) {\n          if (!error) error = err;\n        }\n      }\n      e = next;\n    }\n  }\n  if (error) throw error;\n}\nfunction prepareDeps(sub) {\n  for (let link = sub.deps; link; link = link.nextDep) {\n    link.version = -1;\n    link.prevActiveLink = link.dep.activeLink;\n    link.dep.activeLink = link;\n  }\n}\nfunction cleanupDeps(sub) {\n  let head;\n  let tail = sub.depsTail;\n  let link = tail;\n  while (link) {\n    const prev = link.prevDep;\n    if (link.version === -1) {\n      if (link === tail) tail = prev;\n      removeSub(link);\n      removeDep(link);\n    } else {\n      head = link;\n    }\n    link.dep.activeLink = link.prevActiveLink;\n    link.prevActiveLink = void 0;\n    link = prev;\n  }\n  sub.deps = head;\n  sub.depsTail = tail;\n}\nfunction isDirty(sub) {\n  for (let link = sub.deps; link; link = link.nextDep) {\n    if (link.dep.version !== link.version || link.dep.computed && (refreshComputed(link.dep.computed) || link.dep.version !== link.version)) {\n      return true;\n    }\n  }\n  if (sub._dirty) {\n    return true;\n  }\n  return false;\n}\nfunction refreshComputed(computed3) {\n  if (computed3.flags & 4 && !(computed3.flags & 16)) {\n    return;\n  }\n  computed3.flags &= ~16;\n  if (computed3.globalVersion === globalVersion) {\n    return;\n  }\n  computed3.globalVersion = globalVersion;\n  const dep = computed3.dep;\n  computed3.flags |= 2;\n  if (dep.version > 0 && !computed3.isSSR && computed3.deps && !isDirty(computed3)) {\n    computed3.flags &= ~2;\n    return;\n  }\n  const prevSub = activeSub;\n  const prevShouldTrack = shouldTrack;\n  activeSub = computed3;\n  shouldTrack = true;\n  try {\n    prepareDeps(computed3);\n    const value = computed3.fn(computed3._value);\n    if (dep.version === 0 || hasChanged(value, computed3._value)) {\n      computed3._value = value;\n      dep.version++;\n    }\n  } catch (err) {\n    dep.version++;\n    throw err;\n  } finally {\n    activeSub = prevSub;\n    shouldTrack = prevShouldTrack;\n    cleanupDeps(computed3);\n    computed3.flags &= ~2;\n  }\n}\nfunction removeSub(link, soft = false) {\n  const { dep, prevSub, nextSub } = link;\n  if (prevSub) {\n    prevSub.nextSub = nextSub;\n    link.prevSub = void 0;\n  }\n  if (nextSub) {\n    nextSub.prevSub = prevSub;\n    link.nextSub = void 0;\n  }\n  if (dep.subsHead === link) {\n    dep.subsHead = nextSub;\n  }\n  if (dep.subs === link) {\n    dep.subs = prevSub;\n    if (!prevSub && dep.computed) {\n      dep.computed.flags &= ~4;\n      for (let l = dep.computed.deps; l; l = l.nextDep) {\n        removeSub(l, true);\n      }\n    }\n  }\n  if (!soft && !--dep.sc && dep.map) {\n    dep.map.delete(dep.key);\n  }\n}\nfunction removeDep(link) {\n  const { prevDep, nextDep } = link;\n  if (prevDep) {\n    prevDep.nextDep = nextDep;\n    link.prevDep = void 0;\n  }\n  if (nextDep) {\n    nextDep.prevDep = prevDep;\n    link.nextDep = void 0;\n  }\n}\nfunction effect(fn, options) {\n  if (fn.effect instanceof ReactiveEffect) {\n    fn = fn.effect.fn;\n  }\n  const e = new ReactiveEffect(fn);\n  if (options) {\n    extend(e, options);\n  }\n  try {\n    e.run();\n  } catch (err) {\n    e.stop();\n    throw err;\n  }\n  const runner = e.run.bind(e);\n  runner.effect = e;\n  return runner;\n}\nfunction stop(runner) {\n  runner.effect.stop();\n}\nvar shouldTrack = true;\nvar trackStack = [];\nfunction pauseTracking() {\n  trackStack.push(shouldTrack);\n  shouldTrack = false;\n}\nfunction resetTracking() {\n  const last = trackStack.pop();\n  shouldTrack = last === void 0 ? true : last;\n}\nfunction cleanupEffect(e) {\n  const { cleanup } = e;\n  e.cleanup = void 0;\n  if (cleanup) {\n    const prevSub = activeSub;\n    activeSub = void 0;\n    try {\n      cleanup();\n    } finally {\n      activeSub = prevSub;\n    }\n  }\n}\nvar globalVersion = 0;\nvar Link = class {\n  constructor(sub, dep) {\n    this.sub = sub;\n    this.dep = dep;\n    this.version = dep.version;\n    this.nextDep = this.prevDep = this.nextSub = this.prevSub = this.prevActiveLink = void 0;\n  }\n};\nvar Dep = class {\n  constructor(computed3) {\n    this.computed = computed3;\n    this.version = 0;\n    this.activeLink = void 0;\n    this.subs = void 0;\n    this.map = void 0;\n    this.key = void 0;\n    this.sc = 0;\n    if (true) {\n      this.subsHead = void 0;\n    }\n  }\n  track(debugInfo) {\n    if (!activeSub || !shouldTrack || activeSub === this.computed) {\n      return;\n    }\n    let link = this.activeLink;\n    if (link === void 0 || link.sub !== activeSub) {\n      link = this.activeLink = new Link(activeSub, this);\n      if (!activeSub.deps) {\n        activeSub.deps = activeSub.depsTail = link;\n      } else {\n        link.prevDep = activeSub.depsTail;\n        activeSub.depsTail.nextDep = link;\n        activeSub.depsTail = link;\n      }\n      addSub(link);\n    } else if (link.version === -1) {\n      link.version = this.version;\n      if (link.nextDep) {\n        const next = link.nextDep;\n        next.prevDep = link.prevDep;\n        if (link.prevDep) {\n          link.prevDep.nextDep = next;\n        }\n        link.prevDep = activeSub.depsTail;\n        link.nextDep = void 0;\n        activeSub.depsTail.nextDep = link;\n        activeSub.depsTail = link;\n        if (activeSub.deps === link) {\n          activeSub.deps = next;\n        }\n      }\n    }\n    if (activeSub.onTrack) {\n      activeSub.onTrack(\n        extend(\n          {\n            effect: activeSub\n          },\n          debugInfo\n        )\n      );\n    }\n    return link;\n  }\n  trigger(debugInfo) {\n    this.version++;\n    globalVersion++;\n    this.notify(debugInfo);\n  }\n  notify(debugInfo) {\n    startBatch();\n    try {\n      if (true) {\n        for (let head = this.subsHead; head; head = head.nextSub) {\n          if (head.sub.onTrigger && !(head.sub.flags & 8)) {\n            head.sub.onTrigger(\n              extend(\n                {\n                  effect: head.sub\n                },\n                debugInfo\n              )\n            );\n          }\n        }\n      }\n      for (let link = this.subs; link; link = link.prevSub) {\n        if (link.sub.notify()) {\n          ;\n          link.sub.dep.notify();\n        }\n      }\n    } finally {\n      endBatch();\n    }\n  }\n};\nfunction addSub(link) {\n  link.dep.sc++;\n  if (link.sub.flags & 4) {\n    const computed3 = link.dep.computed;\n    if (computed3 && !link.dep.subs) {\n      computed3.flags |= 4 | 16;\n      for (let l = computed3.deps; l; l = l.nextDep) {\n        addSub(l);\n      }\n    }\n    const currentTail = link.dep.subs;\n    if (currentTail !== link) {\n      link.prevSub = currentTail;\n      if (currentTail) currentTail.nextSub = link;\n    }\n    if (link.dep.subsHead === void 0) {\n      link.dep.subsHead = link;\n    }\n    link.dep.subs = link;\n  }\n}\nvar targetMap = /* @__PURE__ */ new WeakMap();\nvar ITERATE_KEY = Symbol(\n  true ? \"Object iterate\" : \"\"\n);\nvar MAP_KEY_ITERATE_KEY = Symbol(\n  true ? \"Map keys iterate\" : \"\"\n);\nvar ARRAY_ITERATE_KEY = Symbol(\n  true ? \"Array iterate\" : \"\"\n);\nfunction track(target, type, key) {\n  if (shouldTrack && activeSub) {\n    let depsMap = targetMap.get(target);\n    if (!depsMap) {\n      targetMap.set(target, depsMap = /* @__PURE__ */ new Map());\n    }\n    let dep = depsMap.get(key);\n    if (!dep) {\n      depsMap.set(key, dep = new Dep());\n      dep.map = depsMap;\n      dep.key = key;\n    }\n    if (true) {\n      dep.track({\n        target,\n        type,\n        key\n      });\n    } else {\n      dep.track();\n    }\n  }\n}\nfunction trigger(target, type, key, newValue, oldValue, oldTarget) {\n  const depsMap = targetMap.get(target);\n  if (!depsMap) {\n    globalVersion++;\n    return;\n  }\n  const run = (dep) => {\n    if (dep) {\n      if (true) {\n        dep.trigger({\n          target,\n          type,\n          key,\n          newValue,\n          oldValue,\n          oldTarget\n        });\n      } else {\n        dep.trigger();\n      }\n    }\n  };\n  startBatch();\n  if (type === \"clear\") {\n    depsMap.forEach(run);\n  } else {\n    const targetIsArray = isArray(target);\n    const isArrayIndex = targetIsArray && isIntegerKey(key);\n    if (targetIsArray && key === \"length\") {\n      const newLength = Number(newValue);\n      depsMap.forEach((dep, key2) => {\n        if (key2 === \"length\" || key2 === ARRAY_ITERATE_KEY || !isSymbol(key2) && key2 >= newLength) {\n          run(dep);\n        }\n      });\n    } else {\n      if (key !== void 0 || depsMap.has(void 0)) {\n        run(depsMap.get(key));\n      }\n      if (isArrayIndex) {\n        run(depsMap.get(ARRAY_ITERATE_KEY));\n      }\n      switch (type) {\n        case \"add\":\n          if (!targetIsArray) {\n            run(depsMap.get(ITERATE_KEY));\n            if (isMap(target)) {\n              run(depsMap.get(MAP_KEY_ITERATE_KEY));\n            }\n          } else if (isArrayIndex) {\n            run(depsMap.get(\"length\"));\n          }\n          break;\n        case \"delete\":\n          if (!targetIsArray) {\n            run(depsMap.get(ITERATE_KEY));\n            if (isMap(target)) {\n              run(depsMap.get(MAP_KEY_ITERATE_KEY));\n            }\n          }\n          break;\n        case \"set\":\n          if (isMap(target)) {\n            run(depsMap.get(ITERATE_KEY));\n          }\n          break;\n      }\n    }\n  }\n  endBatch();\n}\nfunction getDepFromReactive(object, key) {\n  const depMap = targetMap.get(object);\n  return depMap && depMap.get(key);\n}\nfunction reactiveReadArray(array) {\n  const raw = toRaw(array);\n  if (raw === array) return raw;\n  track(raw, \"iterate\", ARRAY_ITERATE_KEY);\n  return isShallow(array) ? raw : raw.map(toReactive);\n}\nfunction shallowReadArray(arr) {\n  track(arr = toRaw(arr), \"iterate\", ARRAY_ITERATE_KEY);\n  return arr;\n}\nvar arrayInstrumentations = {\n  __proto__: null,\n  [Symbol.iterator]() {\n    return iterator(this, Symbol.iterator, toReactive);\n  },\n  concat(...args) {\n    return reactiveReadArray(this).concat(\n      ...args.map((x) => isArray(x) ? reactiveReadArray(x) : x)\n    );\n  },\n  entries() {\n    return iterator(this, \"entries\", (value) => {\n      value[1] = toReactive(value[1]);\n      return value;\n    });\n  },\n  every(fn, thisArg) {\n    return apply(this, \"every\", fn, thisArg, void 0, arguments);\n  },\n  filter(fn, thisArg) {\n    return apply(this, \"filter\", fn, thisArg, (v) => v.map(toReactive), arguments);\n  },\n  find(fn, thisArg) {\n    return apply(this, \"find\", fn, thisArg, toReactive, arguments);\n  },\n  findIndex(fn, thisArg) {\n    return apply(this, \"findIndex\", fn, thisArg, void 0, arguments);\n  },\n  findLast(fn, thisArg) {\n    return apply(this, \"findLast\", fn, thisArg, toReactive, arguments);\n  },\n  findLastIndex(fn, thisArg) {\n    return apply(this, \"findLastIndex\", fn, thisArg, void 0, arguments);\n  },\n  // flat, flatMap could benefit from ARRAY_ITERATE but are not straight-forward to implement\n  forEach(fn, thisArg) {\n    return apply(this, \"forEach\", fn, thisArg, void 0, arguments);\n  },\n  includes(...args) {\n    return searchProxy(this, \"includes\", args);\n  },\n  indexOf(...args) {\n    return searchProxy(this, \"indexOf\", args);\n  },\n  join(separator) {\n    return reactiveReadArray(this).join(separator);\n  },\n  // keys() iterator only reads `length`, no optimisation required\n  lastIndexOf(...args) {\n    return searchProxy(this, \"lastIndexOf\", args);\n  },\n  map(fn, thisArg) {\n    return apply(this, \"map\", fn, thisArg, void 0, arguments);\n  },\n  pop() {\n    return noTracking(this, \"pop\");\n  },\n  push(...args) {\n    return noTracking(this, \"push\", args);\n  },\n  reduce(fn, ...args) {\n    return reduce(this, \"reduce\", fn, args);\n  },\n  reduceRight(fn, ...args) {\n    return reduce(this, \"reduceRight\", fn, args);\n  },\n  shift() {\n    return noTracking(this, \"shift\");\n  },\n  // slice could use ARRAY_ITERATE but also seems to beg for range tracking\n  some(fn, thisArg) {\n    return apply(this, \"some\", fn, thisArg, void 0, arguments);\n  },\n  splice(...args) {\n    return noTracking(this, \"splice\", args);\n  },\n  toReversed() {\n    return reactiveReadArray(this).toReversed();\n  },\n  toSorted(comparer) {\n    return reactiveReadArray(this).toSorted(comparer);\n  },\n  toSpliced(...args) {\n    return reactiveReadArray(this).toSpliced(...args);\n  },\n  unshift(...args) {\n    return noTracking(this, \"unshift\", args);\n  },\n  values() {\n    return iterator(this, \"values\", toReactive);\n  }\n};\nfunction iterator(self2, method, wrapValue) {\n  const arr = shallowReadArray(self2);\n  const iter = arr[method]();\n  if (arr !== self2 && !isShallow(self2)) {\n    iter._next = iter.next;\n    iter.next = () => {\n      const result = iter._next();\n      if (result.value) {\n        result.value = wrapValue(result.value);\n      }\n      return result;\n    };\n  }\n  return iter;\n}\nvar arrayProto = Array.prototype;\nfunction apply(self2, method, fn, thisArg, wrappedRetFn, args) {\n  const arr = shallowReadArray(self2);\n  const needsWrap = arr !== self2 && !isShallow(self2);\n  const methodFn = arr[method];\n  if (methodFn !== arrayProto[method]) {\n    const result2 = methodFn.apply(self2, args);\n    return needsWrap ? toReactive(result2) : result2;\n  }\n  let wrappedFn = fn;\n  if (arr !== self2) {\n    if (needsWrap) {\n      wrappedFn = function(item, index) {\n        return fn.call(this, toReactive(item), index, self2);\n      };\n    } else if (fn.length > 2) {\n      wrappedFn = function(item, index) {\n        return fn.call(this, item, index, self2);\n      };\n    }\n  }\n  const result = methodFn.call(arr, wrappedFn, thisArg);\n  return needsWrap && wrappedRetFn ? wrappedRetFn(result) : result;\n}\nfunction reduce(self2, method, fn, args) {\n  const arr = shallowReadArray(self2);\n  let wrappedFn = fn;\n  if (arr !== self2) {\n    if (!isShallow(self2)) {\n      wrappedFn = function(acc, item, index) {\n        return fn.call(this, acc, toReactive(item), index, self2);\n      };\n    } else if (fn.length > 3) {\n      wrappedFn = function(acc, item, index) {\n        return fn.call(this, acc, item, index, self2);\n      };\n    }\n  }\n  return arr[method](wrappedFn, ...args);\n}\nfunction searchProxy(self2, method, args) {\n  const arr = toRaw(self2);\n  track(arr, \"iterate\", ARRAY_ITERATE_KEY);\n  const res = arr[method](...args);\n  if ((res === -1 || res === false) && isProxy(args[0])) {\n    args[0] = toRaw(args[0]);\n    return arr[method](...args);\n  }\n  return res;\n}\nfunction noTracking(self2, method, args = []) {\n  pauseTracking();\n  startBatch();\n  const res = toRaw(self2)[method].apply(self2, args);\n  endBatch();\n  resetTracking();\n  return res;\n}\nvar isNonTrackableKeys = makeMap(`__proto__,__v_isRef,__isVue`);\nvar builtInSymbols = new Set(\n  Object.getOwnPropertyNames(Symbol).filter((key) => key !== \"arguments\" && key !== \"caller\").map((key) => Symbol[key]).filter(isSymbol)\n);\nfunction hasOwnProperty2(key) {\n  if (!isSymbol(key)) key = String(key);\n  const obj = toRaw(this);\n  track(obj, \"has\", key);\n  return obj.hasOwnProperty(key);\n}\nvar BaseReactiveHandler = class {\n  constructor(_isReadonly = false, _isShallow = false) {\n    this._isReadonly = _isReadonly;\n    this._isShallow = _isShallow;\n  }\n  get(target, key, receiver) {\n    if (key === \"__v_skip\") return target[\"__v_skip\"];\n    const isReadonly2 = this._isReadonly, isShallow2 = this._isShallow;\n    if (key === \"__v_isReactive\") {\n      return !isReadonly2;\n    } else if (key === \"__v_isReadonly\") {\n      return isReadonly2;\n    } else if (key === \"__v_isShallow\") {\n      return isShallow2;\n    } else if (key === \"__v_raw\") {\n      if (receiver === (isReadonly2 ? isShallow2 ? shallowReadonlyMap : readonlyMap : isShallow2 ? shallowReactiveMap : reactiveMap).get(target) || // receiver is not the reactive proxy, but has the same prototype\n      // this means the receiver is a user proxy of the reactive proxy\n      Object.getPrototypeOf(target) === Object.getPrototypeOf(receiver)) {\n        return target;\n      }\n      return;\n    }\n    const targetIsArray = isArray(target);\n    if (!isReadonly2) {\n      let fn;\n      if (targetIsArray && (fn = arrayInstrumentations[key])) {\n        return fn;\n      }\n      if (key === \"hasOwnProperty\") {\n        return hasOwnProperty2;\n      }\n    }\n    const res = Reflect.get(\n      target,\n      key,\n      // if this is a proxy wrapping a ref, return methods using the raw ref\n      // as receiver so that we don't have to call `toRaw` on the ref in all\n      // its class methods\n      isRef2(target) ? target : receiver\n    );\n    if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {\n      return res;\n    }\n    if (!isReadonly2) {\n      track(target, \"get\", key);\n    }\n    if (isShallow2) {\n      return res;\n    }\n    if (isRef2(res)) {\n      return targetIsArray && isIntegerKey(key) ? res : res.value;\n    }\n    if (isObject(res)) {\n      return isReadonly2 ? readonly(res) : reactive(res);\n    }\n    return res;\n  }\n};\nvar MutableReactiveHandler = class extends BaseReactiveHandler {\n  constructor(isShallow2 = false) {\n    super(false, isShallow2);\n  }\n  set(target, key, value, receiver) {\n    let oldValue = target[key];\n    if (!this._isShallow) {\n      const isOldValueReadonly = isReadonly(oldValue);\n      if (!isShallow(value) && !isReadonly(value)) {\n        oldValue = toRaw(oldValue);\n        value = toRaw(value);\n      }\n      if (!isArray(target) && isRef2(oldValue) && !isRef2(value)) {\n        if (isOldValueReadonly) {\n          return false;\n        } else {\n          oldValue.value = value;\n          return true;\n        }\n      }\n    }\n    const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key);\n    const result = Reflect.set(\n      target,\n      key,\n      value,\n      isRef2(target) ? target : receiver\n    );\n    if (target === toRaw(receiver)) {\n      if (!hadKey) {\n        trigger(target, \"add\", key, value);\n      } else if (hasChanged(value, oldValue)) {\n        trigger(target, \"set\", key, value, oldValue);\n      }\n    }\n    return result;\n  }\n  deleteProperty(target, key) {\n    const hadKey = hasOwn(target, key);\n    const oldValue = target[key];\n    const result = Reflect.deleteProperty(target, key);\n    if (result && hadKey) {\n      trigger(target, \"delete\", key, void 0, oldValue);\n    }\n    return result;\n  }\n  has(target, key) {\n    const result = Reflect.has(target, key);\n    if (!isSymbol(key) || !builtInSymbols.has(key)) {\n      track(target, \"has\", key);\n    }\n    return result;\n  }\n  ownKeys(target) {\n    track(\n      target,\n      \"iterate\",\n      isArray(target) ? \"length\" : ITERATE_KEY\n    );\n    return Reflect.ownKeys(target);\n  }\n};\nvar ReadonlyReactiveHandler = class extends BaseReactiveHandler {\n  constructor(isShallow2 = false) {\n    super(true, isShallow2);\n  }\n  set(target, key) {\n    if (true) {\n      warn(\n        `Set operation on key \"${String(key)}\" failed: target is readonly.`,\n        target\n      );\n    }\n    return true;\n  }\n  deleteProperty(target, key) {\n    if (true) {\n      warn(\n        `Delete operation on key \"${String(key)}\" failed: target is readonly.`,\n        target\n      );\n    }\n    return true;\n  }\n};\nvar mutableHandlers = new MutableReactiveHandler();\nvar readonlyHandlers = new ReadonlyReactiveHandler();\nvar shallowReactiveHandlers = new MutableReactiveHandler(true);\nvar shallowReadonlyHandlers = new ReadonlyReactiveHandler(true);\nvar toShallow = (value) => value;\nvar getProto = (v) => Reflect.getPrototypeOf(v);\nfunction createIterableMethod(method, isReadonly2, isShallow2) {\n  return function(...args) {\n    const target = this[\"__v_raw\"];\n    const rawTarget = toRaw(target);\n    const targetIsMap = isMap(rawTarget);\n    const isPair = method === \"entries\" || method === Symbol.iterator && targetIsMap;\n    const isKeyOnly = method === \"keys\" && targetIsMap;\n    const innerIterator = target[method](...args);\n    const wrap = isShallow2 ? toShallow : isReadonly2 ? toReadonly : toReactive;\n    !isReadonly2 && track(\n      rawTarget,\n      \"iterate\",\n      isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY\n    );\n    return {\n      // iterator protocol\n      next() {\n        const { value, done } = innerIterator.next();\n        return done ? { value, done } : {\n          value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value),\n          done\n        };\n      },\n      // iterable protocol\n      [Symbol.iterator]() {\n        return this;\n      }\n    };\n  };\n}\nfunction createReadonlyMethod(type) {\n  return function(...args) {\n    if (true) {\n      const key = args[0] ? `on key \"${args[0]}\" ` : ``;\n      warn(\n        `${capitalize(type)} operation ${key}failed: target is readonly.`,\n        toRaw(this)\n      );\n    }\n    return type === \"delete\" ? false : type === \"clear\" ? void 0 : this;\n  };\n}\nfunction createInstrumentations(readonly2, shallow) {\n  const instrumentations = {\n    get(key) {\n      const target = this[\"__v_raw\"];\n      const rawTarget = toRaw(target);\n      const rawKey = toRaw(key);\n      if (!readonly2) {\n        if (hasChanged(key, rawKey)) {\n          track(rawTarget, \"get\", key);\n        }\n        track(rawTarget, \"get\", rawKey);\n      }\n      const { has } = getProto(rawTarget);\n      const wrap = shallow ? toShallow : readonly2 ? toReadonly : toReactive;\n      if (has.call(rawTarget, key)) {\n        return wrap(target.get(key));\n      } else if (has.call(rawTarget, rawKey)) {\n        return wrap(target.get(rawKey));\n      } else if (target !== rawTarget) {\n        target.get(key);\n      }\n    },\n    get size() {\n      const target = this[\"__v_raw\"];\n      !readonly2 && track(toRaw(target), \"iterate\", ITERATE_KEY);\n      return Reflect.get(target, \"size\", target);\n    },\n    has(key) {\n      const target = this[\"__v_raw\"];\n      const rawTarget = toRaw(target);\n      const rawKey = toRaw(key);\n      if (!readonly2) {\n        if (hasChanged(key, rawKey)) {\n          track(rawTarget, \"has\", key);\n        }\n        track(rawTarget, \"has\", rawKey);\n      }\n      return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey);\n    },\n    forEach(callback, thisArg) {\n      const observed = this;\n      const target = observed[\"__v_raw\"];\n      const rawTarget = toRaw(target);\n      const wrap = shallow ? toShallow : readonly2 ? toReadonly : toReactive;\n      !readonly2 && track(rawTarget, \"iterate\", ITERATE_KEY);\n      return target.forEach((value, key) => {\n        return callback.call(thisArg, wrap(value), wrap(key), observed);\n      });\n    }\n  };\n  extend(\n    instrumentations,\n    readonly2 ? {\n      add: createReadonlyMethod(\"add\"),\n      set: createReadonlyMethod(\"set\"),\n      delete: createReadonlyMethod(\"delete\"),\n      clear: createReadonlyMethod(\"clear\")\n    } : {\n      add(value) {\n        if (!shallow && !isShallow(value) && !isReadonly(value)) {\n          value = toRaw(value);\n        }\n        const target = toRaw(this);\n        const proto = getProto(target);\n        const hadKey = proto.has.call(target, value);\n        if (!hadKey) {\n          target.add(value);\n          trigger(target, \"add\", value, value);\n        }\n        return this;\n      },\n      set(key, value) {\n        if (!shallow && !isShallow(value) && !isReadonly(value)) {\n          value = toRaw(value);\n        }\n        const target = toRaw(this);\n        const { has, get } = getProto(target);\n        let hadKey = has.call(target, key);\n        if (!hadKey) {\n          key = toRaw(key);\n          hadKey = has.call(target, key);\n        } else if (true) {\n          checkIdentityKeys(target, has, key);\n        }\n        const oldValue = get.call(target, key);\n        target.set(key, value);\n        if (!hadKey) {\n          trigger(target, \"add\", key, value);\n        } else if (hasChanged(value, oldValue)) {\n          trigger(target, \"set\", key, value, oldValue);\n        }\n        return this;\n      },\n      delete(key) {\n        const target = toRaw(this);\n        const { has, get } = getProto(target);\n        let hadKey = has.call(target, key);\n        if (!hadKey) {\n          key = toRaw(key);\n          hadKey = has.call(target, key);\n        } else if (true) {\n          checkIdentityKeys(target, has, key);\n        }\n        const oldValue = get ? get.call(target, key) : void 0;\n        const result = target.delete(key);\n        if (hadKey) {\n          trigger(target, \"delete\", key, void 0, oldValue);\n        }\n        return result;\n      },\n      clear() {\n        const target = toRaw(this);\n        const hadItems = target.size !== 0;\n        const oldTarget = true ? isMap(target) ? new Map(target) : new Set(target) : void 0;\n        const result = target.clear();\n        if (hadItems) {\n          trigger(\n            target,\n            \"clear\",\n            void 0,\n            void 0,\n            oldTarget\n          );\n        }\n        return result;\n      }\n    }\n  );\n  const iteratorMethods = [\n    \"keys\",\n    \"values\",\n    \"entries\",\n    Symbol.iterator\n  ];\n  iteratorMethods.forEach((method) => {\n    instrumentations[method] = createIterableMethod(method, readonly2, shallow);\n  });\n  return instrumentations;\n}\nfunction createInstrumentationGetter(isReadonly2, shallow) {\n  const instrumentations = createInstrumentations(isReadonly2, shallow);\n  return (target, key, receiver) => {\n    if (key === \"__v_isReactive\") {\n      return !isReadonly2;\n    } else if (key === \"__v_isReadonly\") {\n      return isReadonly2;\n    } else if (key === \"__v_raw\") {\n      return target;\n    }\n    return Reflect.get(\n      hasOwn(instrumentations, key) && key in target ? instrumentations : target,\n      key,\n      receiver\n    );\n  };\n}\nvar mutableCollectionHandlers = {\n  get: createInstrumentationGetter(false, false)\n};\nvar shallowCollectionHandlers = {\n  get: createInstrumentationGetter(false, true)\n};\nvar readonlyCollectionHandlers = {\n  get: createInstrumentationGetter(true, false)\n};\nvar shallowReadonlyCollectionHandlers = {\n  get: createInstrumentationGetter(true, true)\n};\nfunction checkIdentityKeys(target, has, key) {\n  const rawKey = toRaw(key);\n  if (rawKey !== key && has.call(target, rawKey)) {\n    const type = toRawType(target);\n    warn(\n      `Reactive ${type} contains both the raw and reactive versions of the same object${type === `Map` ? ` as keys` : ``}, which can lead to inconsistencies. Avoid differentiating between the raw and reactive versions of an object and only use the reactive version if possible.`\n    );\n  }\n}\nvar reactiveMap = /* @__PURE__ */ new WeakMap();\nvar shallowReactiveMap = /* @__PURE__ */ new WeakMap();\nvar readonlyMap = /* @__PURE__ */ new WeakMap();\nvar shallowReadonlyMap = /* @__PURE__ */ new WeakMap();\nfunction targetTypeMap(rawType) {\n  switch (rawType) {\n    case \"Object\":\n    case \"Array\":\n      return 1;\n    case \"Map\":\n    case \"Set\":\n    case \"WeakMap\":\n    case \"WeakSet\":\n      return 2;\n    default:\n      return 0;\n  }\n}\nfunction getTargetType(value) {\n  return value[\"__v_skip\"] || !Object.isExtensible(value) ? 0 : targetTypeMap(toRawType(value));\n}\nfunction reactive(target) {\n  if (isReadonly(target)) {\n    return target;\n  }\n  return createReactiveObject(\n    target,\n    false,\n    mutableHandlers,\n    mutableCollectionHandlers,\n    reactiveMap\n  );\n}\nfunction shallowReactive(target) {\n  return createReactiveObject(\n    target,\n    false,\n    shallowReactiveHandlers,\n    shallowCollectionHandlers,\n    shallowReactiveMap\n  );\n}\nfunction readonly(target) {\n  return createReactiveObject(\n    target,\n    true,\n    readonlyHandlers,\n    readonlyCollectionHandlers,\n    readonlyMap\n  );\n}\nfunction shallowReadonly(target) {\n  return createReactiveObject(\n    target,\n    true,\n    shallowReadonlyHandlers,\n    shallowReadonlyCollectionHandlers,\n    shallowReadonlyMap\n  );\n}\nfunction createReactiveObject(target, isReadonly2, baseHandlers, collectionHandlers, proxyMap) {\n  if (!isObject(target)) {\n    if (true) {\n      warn(\n        `value cannot be made ${isReadonly2 ? \"readonly\" : \"reactive\"}: ${String(\n          target\n        )}`\n      );\n    }\n    return target;\n  }\n  if (target[\"__v_raw\"] && !(isReadonly2 && target[\"__v_isReactive\"])) {\n    return target;\n  }\n  const existingProxy = proxyMap.get(target);\n  if (existingProxy) {\n    return existingProxy;\n  }\n  const targetType = getTargetType(target);\n  if (targetType === 0) {\n    return target;\n  }\n  const proxy = new Proxy(\n    target,\n    targetType === 2 ? collectionHandlers : baseHandlers\n  );\n  proxyMap.set(target, proxy);\n  return proxy;\n}\nfunction isReactive(value) {\n  if (isReadonly(value)) {\n    return isReactive(value[\"__v_raw\"]);\n  }\n  return !!(value && value[\"__v_isReactive\"]);\n}\nfunction isReadonly(value) {\n  return !!(value && value[\"__v_isReadonly\"]);\n}\nfunction isShallow(value) {\n  return !!(value && value[\"__v_isShallow\"]);\n}\nfunction isProxy(value) {\n  return value ? !!value[\"__v_raw\"] : false;\n}\nfunction toRaw(observed) {\n  const raw = observed && observed[\"__v_raw\"];\n  return raw ? toRaw(raw) : observed;\n}\nfunction markRaw(value) {\n  if (!hasOwn(value, \"__v_skip\") && Object.isExtensible(value)) {\n    def(value, \"__v_skip\", true);\n  }\n  return value;\n}\nvar toReactive = (value) => isObject(value) ? reactive(value) : value;\nvar toReadonly = (value) => isObject(value) ? readonly(value) : value;\nfunction isRef2(r) {\n  return r ? r[\"__v_isRef\"] === true : false;\n}\nfunction ref(value) {\n  return createRef(value, false);\n}\nfunction shallowRef(value) {\n  return createRef(value, true);\n}\nfunction createRef(rawValue, shallow) {\n  if (isRef2(rawValue)) {\n    return rawValue;\n  }\n  return new RefImpl(rawValue, shallow);\n}\nvar RefImpl = class {\n  constructor(value, isShallow2) {\n    this.dep = new Dep();\n    this[\"__v_isRef\"] = true;\n    this[\"__v_isShallow\"] = false;\n    this._rawValue = isShallow2 ? value : toRaw(value);\n    this._value = isShallow2 ? value : toReactive(value);\n    this[\"__v_isShallow\"] = isShallow2;\n  }\n  get value() {\n    if (true) {\n      this.dep.track({\n        target: this,\n        type: \"get\",\n        key: \"value\"\n      });\n    } else {\n      this.dep.track();\n    }\n    return this._value;\n  }\n  set value(newValue) {\n    const oldValue = this._rawValue;\n    const useDirectValue = this[\"__v_isShallow\"] || isShallow(newValue) || isReadonly(newValue);\n    newValue = useDirectValue ? newValue : toRaw(newValue);\n    if (hasChanged(newValue, oldValue)) {\n      this._rawValue = newValue;\n      this._value = useDirectValue ? newValue : toReactive(newValue);\n      if (true) {\n        this.dep.trigger({\n          target: this,\n          type: \"set\",\n          key: \"value\",\n          newValue,\n          oldValue\n        });\n      } else {\n        this.dep.trigger();\n      }\n    }\n  }\n};\nfunction triggerRef(ref2) {\n  if (ref2.dep) {\n    if (true) {\n      ref2.dep.trigger({\n        target: ref2,\n        type: \"set\",\n        key: \"value\",\n        newValue: ref2._value\n      });\n    } else {\n      ref2.dep.trigger();\n    }\n  }\n}\nfunction unref(ref2) {\n  return isRef2(ref2) ? ref2.value : ref2;\n}\nfunction toValue(source) {\n  return isFunction(source) ? source() : unref(source);\n}\nvar shallowUnwrapHandlers = {\n  get: (target, key, receiver) => key === \"__v_raw\" ? target : unref(Reflect.get(target, key, receiver)),\n  set: (target, key, value, receiver) => {\n    const oldValue = target[key];\n    if (isRef2(oldValue) && !isRef2(value)) {\n      oldValue.value = value;\n      return true;\n    } else {\n      return Reflect.set(target, key, value, receiver);\n    }\n  }\n};\nfunction proxyRefs(objectWithRefs) {\n  return isReactive(objectWithRefs) ? objectWithRefs : new Proxy(objectWithRefs, shallowUnwrapHandlers);\n}\nvar CustomRefImpl = class {\n  constructor(factory) {\n    this[\"__v_isRef\"] = true;\n    this._value = void 0;\n    const dep = this.dep = new Dep();\n    const { get, set } = factory(dep.track.bind(dep), dep.trigger.bind(dep));\n    this._get = get;\n    this._set = set;\n  }\n  get value() {\n    return this._value = this._get();\n  }\n  set value(newVal) {\n    this._set(newVal);\n  }\n};\nfunction customRef(factory) {\n  return new CustomRefImpl(factory);\n}\nfunction toRefs(object) {\n  if (!isProxy(object)) {\n    warn(`toRefs() expects a reactive object but received a plain one.`);\n  }\n  const ret = isArray(object) ? new Array(object.length) : {};\n  for (const key in object) {\n    ret[key] = propertyToRef(object, key);\n  }\n  return ret;\n}\nvar ObjectRefImpl = class {\n  constructor(_object, _key, _defaultValue) {\n    this._object = _object;\n    this._key = _key;\n    this._defaultValue = _defaultValue;\n    this[\"__v_isRef\"] = true;\n    this._value = void 0;\n  }\n  get value() {\n    const val = this._object[this._key];\n    return this._value = val === void 0 ? this._defaultValue : val;\n  }\n  set value(newVal) {\n    this._object[this._key] = newVal;\n  }\n  get dep() {\n    return getDepFromReactive(toRaw(this._object), this._key);\n  }\n};\nvar GetterRefImpl = class {\n  constructor(_getter) {\n    this._getter = _getter;\n    this[\"__v_isRef\"] = true;\n    this[\"__v_isReadonly\"] = true;\n    this._value = void 0;\n  }\n  get value() {\n    return this._value = this._getter();\n  }\n};\nfunction toRef(source, key, defaultValue) {\n  if (isRef2(source)) {\n    return source;\n  } else if (isFunction(source)) {\n    return new GetterRefImpl(source);\n  } else if (isObject(source) && arguments.length > 1) {\n    return propertyToRef(source, key, defaultValue);\n  } else {\n    return ref(source);\n  }\n}\nfunction propertyToRef(source, key, defaultValue) {\n  const val = source[key];\n  return isRef2(val) ? val : new ObjectRefImpl(source, key, defaultValue);\n}\nvar ComputedRefImpl = class {\n  constructor(fn, setter, isSSR) {\n    this.fn = fn;\n    this.setter = setter;\n    this._value = void 0;\n    this.dep = new Dep(this);\n    this.__v_isRef = true;\n    this.deps = void 0;\n    this.depsTail = void 0;\n    this.flags = 16;\n    this.globalVersion = globalVersion - 1;\n    this.next = void 0;\n    this.effect = this;\n    this[\"__v_isReadonly\"] = !setter;\n    this.isSSR = isSSR;\n  }\n  /**\n   * @internal\n   */\n  notify() {\n    this.flags |= 16;\n    if (!(this.flags & 8) && // avoid infinite self recursion\n    activeSub !== this) {\n      batch(this, true);\n      return true;\n    } else if (true) ;\n  }\n  get value() {\n    const link = true ? this.dep.track({\n      target: this,\n      type: \"get\",\n      key: \"value\"\n    }) : this.dep.track();\n    refreshComputed(this);\n    if (link) {\n      link.version = this.dep.version;\n    }\n    return this._value;\n  }\n  set value(newValue) {\n    if (this.setter) {\n      this.setter(newValue);\n    } else if (true) {\n      warn(\"Write operation failed: computed value is readonly\");\n    }\n  }\n};\nfunction computed(getterOrOptions, debugOptions, isSSR = false) {\n  let getter;\n  let setter;\n  if (isFunction(getterOrOptions)) {\n    getter = getterOrOptions;\n  } else {\n    getter = getterOrOptions.get;\n    setter = getterOrOptions.set;\n  }\n  const cRef = new ComputedRefImpl(getter, setter, isSSR);\n  if (debugOptions && !isSSR) {\n    cRef.onTrack = debugOptions.onTrack;\n    cRef.onTrigger = debugOptions.onTrigger;\n  }\n  return cRef;\n}\nvar TrackOpTypes = {\n  \"GET\": \"get\",\n  \"HAS\": \"has\",\n  \"ITERATE\": \"iterate\"\n};\nvar TriggerOpTypes = {\n  \"SET\": \"set\",\n  \"ADD\": \"add\",\n  \"DELETE\": \"delete\",\n  \"CLEAR\": \"clear\"\n};\nvar INITIAL_WATCHER_VALUE = {};\nvar cleanupMap = /* @__PURE__ */ new WeakMap();\nvar activeWatcher = void 0;\nfunction getCurrentWatcher() {\n  return activeWatcher;\n}\nfunction onWatcherCleanup(cleanupFn, failSilently = false, owner = activeWatcher) {\n  if (owner) {\n    let cleanups = cleanupMap.get(owner);\n    if (!cleanups) cleanupMap.set(owner, cleanups = []);\n    cleanups.push(cleanupFn);\n  } else if (!failSilently) {\n    warn(\n      `onWatcherCleanup() was called when there was no active watcher to associate with.`\n    );\n  }\n}\nfunction watch(source, cb, options = EMPTY_OBJ) {\n  const { immediate, deep, once, scheduler, augmentJob, call } = options;\n  const warnInvalidSource = (s) => {\n    (options.onWarn || warn)(\n      `Invalid watch source: `,\n      s,\n      `A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.`\n    );\n  };\n  const reactiveGetter = (source2) => {\n    if (deep) return source2;\n    if (isShallow(source2) || deep === false || deep === 0)\n      return traverse(source2, 1);\n    return traverse(source2);\n  };\n  let effect2;\n  let getter;\n  let cleanup;\n  let boundCleanup;\n  let forceTrigger = false;\n  let isMultiSource = false;\n  if (isRef2(source)) {\n    getter = () => source.value;\n    forceTrigger = isShallow(source);\n  } else if (isReactive(source)) {\n    getter = () => reactiveGetter(source);\n    forceTrigger = true;\n  } else if (isArray(source)) {\n    isMultiSource = true;\n    forceTrigger = source.some((s) => isReactive(s) || isShallow(s));\n    getter = () => source.map((s) => {\n      if (isRef2(s)) {\n        return s.value;\n      } else if (isReactive(s)) {\n        return reactiveGetter(s);\n      } else if (isFunction(s)) {\n        return call ? call(s, 2) : s();\n      } else {\n        warnInvalidSource(s);\n      }\n    });\n  } else if (isFunction(source)) {\n    if (cb) {\n      getter = call ? () => call(source, 2) : source;\n    } else {\n      getter = () => {\n        if (cleanup) {\n          pauseTracking();\n          try {\n            cleanup();\n          } finally {\n            resetTracking();\n          }\n        }\n        const currentEffect = activeWatcher;\n        activeWatcher = effect2;\n        try {\n          return call ? call(source, 3, [boundCleanup]) : source(boundCleanup);\n        } finally {\n          activeWatcher = currentEffect;\n        }\n      };\n    }\n  } else {\n    getter = NOOP;\n    warnInvalidSource(source);\n  }\n  if (cb && deep) {\n    const baseGetter = getter;\n    const depth = deep === true ? Infinity : deep;\n    getter = () => traverse(baseGetter(), depth);\n  }\n  const scope = getCurrentScope();\n  const watchHandle = () => {\n    effect2.stop();\n    if (scope && scope.active) {\n      remove(scope.effects, effect2);\n    }\n  };\n  if (once && cb) {\n    const _cb = cb;\n    cb = (...args) => {\n      _cb(...args);\n      watchHandle();\n    };\n  }\n  let oldValue = isMultiSource ? new Array(source.length).fill(INITIAL_WATCHER_VALUE) : INITIAL_WATCHER_VALUE;\n  const job = (immediateFirstRun) => {\n    if (!(effect2.flags & 1) || !effect2.dirty && !immediateFirstRun) {\n      return;\n    }\n    if (cb) {\n      const newValue = effect2.run();\n      if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => hasChanged(v, oldValue[i])) : hasChanged(newValue, oldValue))) {\n        if (cleanup) {\n          cleanup();\n        }\n        const currentWatcher = activeWatcher;\n        activeWatcher = effect2;\n        try {\n          const args = [\n            newValue,\n            // pass undefined as the old value when it's changed for the first time\n            oldValue === INITIAL_WATCHER_VALUE ? void 0 : isMultiSource && oldValue[0] === INITIAL_WATCHER_VALUE ? [] : oldValue,\n            boundCleanup\n          ];\n          call ? call(cb, 3, args) : (\n            // @ts-expect-error\n            cb(...args)\n          );\n          oldValue = newValue;\n        } finally {\n          activeWatcher = currentWatcher;\n        }\n      }\n    } else {\n      effect2.run();\n    }\n  };\n  if (augmentJob) {\n    augmentJob(job);\n  }\n  effect2 = new ReactiveEffect(getter);\n  effect2.scheduler = scheduler ? () => scheduler(job, false) : job;\n  boundCleanup = (fn) => onWatcherCleanup(fn, false, effect2);\n  cleanup = effect2.onStop = () => {\n    const cleanups = cleanupMap.get(effect2);\n    if (cleanups) {\n      if (call) {\n        call(cleanups, 4);\n      } else {\n        for (const cleanup2 of cleanups) cleanup2();\n      }\n      cleanupMap.delete(effect2);\n    }\n  };\n  if (true) {\n    effect2.onTrack = options.onTrack;\n    effect2.onTrigger = options.onTrigger;\n  }\n  if (cb) {\n    if (immediate) {\n      job(true);\n    } else {\n      oldValue = effect2.run();\n    }\n  } else if (scheduler) {\n    scheduler(job.bind(null, true), true);\n  } else {\n    effect2.run();\n  }\n  watchHandle.pause = effect2.pause.bind(effect2);\n  watchHandle.resume = effect2.resume.bind(effect2);\n  watchHandle.stop = watchHandle;\n  return watchHandle;\n}\nfunction traverse(value, depth = Infinity, seen) {\n  if (depth <= 0 || !isObject(value) || value[\"__v_skip\"]) {\n    return value;\n  }\n  seen = seen || /* @__PURE__ */ new Set();\n  if (seen.has(value)) {\n    return value;\n  }\n  seen.add(value);\n  depth--;\n  if (isRef2(value)) {\n    traverse(value.value, depth, seen);\n  } else if (isArray(value)) {\n    for (let i = 0; i < value.length; i++) {\n      traverse(value[i], depth, seen);\n    }\n  } else if (isSet(value) || isMap(value)) {\n    value.forEach((v) => {\n      traverse(v, depth, seen);\n    });\n  } else if (isPlainObject(value)) {\n    for (const key in value) {\n      traverse(value[key], depth, seen);\n    }\n    for (const key of Object.getOwnPropertySymbols(value)) {\n      if (Object.prototype.propertyIsEnumerable.call(value, key)) {\n        traverse(value[key], depth, seen);\n      }\n    }\n  }\n  return value;\n}\n\n// node_modules/.pnpm/@vue+runtime-core@3.5.13/node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js\nvar stack = [];\nfunction pushWarningContext(vnode) {\n  stack.push(vnode);\n}\nfunction popWarningContext() {\n  stack.pop();\n}\nvar isWarning = false;\nfunction warn$1(msg, ...args) {\n  if (isWarning) return;\n  isWarning = true;\n  pauseTracking();\n  const instance = stack.length ? stack[stack.length - 1].component : null;\n  const appWarnHandler = instance && instance.appContext.config.warnHandler;\n  const trace = getComponentTrace();\n  if (appWarnHandler) {\n    callWithErrorHandling(\n      appWarnHandler,\n      instance,\n      11,\n      [\n        // eslint-disable-next-line no-restricted-syntax\n        msg + args.map((a) => {\n          var _a, _b;\n          return (_b = (_a = a.toString) == null ? void 0 : _a.call(a)) != null ? _b : JSON.stringify(a);\n        }).join(\"\"),\n        instance && instance.proxy,\n        trace.map(\n          ({ vnode }) => `at <${formatComponentName(instance, vnode.type)}>`\n        ).join(\"\\n\"),\n        trace\n      ]\n    );\n  } else {\n    const warnArgs = [`[Vue warn]: ${msg}`, ...args];\n    if (trace.length && // avoid spamming console during tests\n    true) {\n      warnArgs.push(`\n`, ...formatTrace(trace));\n    }\n    console.warn(...warnArgs);\n  }\n  resetTracking();\n  isWarning = false;\n}\nfunction getComponentTrace() {\n  let currentVNode = stack[stack.length - 1];\n  if (!currentVNode) {\n    return [];\n  }\n  const normalizedStack = [];\n  while (currentVNode) {\n    const last = normalizedStack[0];\n    if (last && last.vnode === currentVNode) {\n      last.recurseCount++;\n    } else {\n      normalizedStack.push({\n        vnode: currentVNode,\n        recurseCount: 0\n      });\n    }\n    const parentInstance = currentVNode.component && currentVNode.component.parent;\n    currentVNode = parentInstance && parentInstance.vnode;\n  }\n  return normalizedStack;\n}\nfunction formatTrace(trace) {\n  const logs = [];\n  trace.forEach((entry, i) => {\n    logs.push(...i === 0 ? [] : [`\n`], ...formatTraceEntry(entry));\n  });\n  return logs;\n}\nfunction formatTraceEntry({ vnode, recurseCount }) {\n  const postfix = recurseCount > 0 ? `... (${recurseCount} recursive calls)` : ``;\n  const isRoot = vnode.component ? vnode.component.parent == null : false;\n  const open = ` at <${formatComponentName(\n    vnode.component,\n    vnode.type,\n    isRoot\n  )}`;\n  const close = `>` + postfix;\n  return vnode.props ? [open, ...formatProps(vnode.props), close] : [open + close];\n}\nfunction formatProps(props) {\n  const res = [];\n  const keys = Object.keys(props);\n  keys.slice(0, 3).forEach((key) => {\n    res.push(...formatProp(key, props[key]));\n  });\n  if (keys.length > 3) {\n    res.push(` ...`);\n  }\n  return res;\n}\nfunction formatProp(key, value, raw) {\n  if (isString(value)) {\n    value = JSON.stringify(value);\n    return raw ? value : [`${key}=${value}`];\n  } else if (typeof value === \"number\" || typeof value === \"boolean\" || value == null) {\n    return raw ? value : [`${key}=${value}`];\n  } else if (isRef2(value)) {\n    value = formatProp(key, toRaw(value.value), true);\n    return raw ? value : [`${key}=Ref<`, value, `>`];\n  } else if (isFunction(value)) {\n    return [`${key}=fn${value.name ? `<${value.name}>` : ``}`];\n  } else {\n    value = toRaw(value);\n    return raw ? value : [`${key}=`, value];\n  }\n}\nfunction assertNumber(val, type) {\n  if (false) return;\n  if (val === void 0) {\n    return;\n  } else if (typeof val !== \"number\") {\n    warn$1(`${type} is not a valid number - got ${JSON.stringify(val)}.`);\n  } else if (isNaN(val)) {\n    warn$1(`${type} is NaN - the duration expression might be incorrect.`);\n  }\n}\nvar ErrorCodes = {\n  \"SETUP_FUNCTION\": 0,\n  \"0\": \"SETUP_FUNCTION\",\n  \"RENDER_FUNCTION\": 1,\n  \"1\": \"RENDER_FUNCTION\",\n  \"NATIVE_EVENT_HANDLER\": 5,\n  \"5\": \"NATIVE_EVENT_HANDLER\",\n  \"COMPONENT_EVENT_HANDLER\": 6,\n  \"6\": \"COMPONENT_EVENT_HANDLER\",\n  \"VNODE_HOOK\": 7,\n  \"7\": \"VNODE_HOOK\",\n  \"DIRECTIVE_HOOK\": 8,\n  \"8\": \"DIRECTIVE_HOOK\",\n  \"TRANSITION_HOOK\": 9,\n  \"9\": \"TRANSITION_HOOK\",\n  \"APP_ERROR_HANDLER\": 10,\n  \"10\": \"APP_ERROR_HANDLER\",\n  \"APP_WARN_HANDLER\": 11,\n  \"11\": \"APP_WARN_HANDLER\",\n  \"FUNCTION_REF\": 12,\n  \"12\": \"FUNCTION_REF\",\n  \"ASYNC_COMPONENT_LOADER\": 13,\n  \"13\": \"ASYNC_COMPONENT_LOADER\",\n  \"SCHEDULER\": 14,\n  \"14\": \"SCHEDULER\",\n  \"COMPONENT_UPDATE\": 15,\n  \"15\": \"COMPONENT_UPDATE\",\n  \"APP_UNMOUNT_CLEANUP\": 16,\n  \"16\": \"APP_UNMOUNT_CLEANUP\"\n};\nvar ErrorTypeStrings$1 = {\n  [\"sp\"]: \"serverPrefetch hook\",\n  [\"bc\"]: \"beforeCreate hook\",\n  [\"c\"]: \"created hook\",\n  [\"bm\"]: \"beforeMount hook\",\n  [\"m\"]: \"mounted hook\",\n  [\"bu\"]: \"beforeUpdate hook\",\n  [\"u\"]: \"updated\",\n  [\"bum\"]: \"beforeUnmount hook\",\n  [\"um\"]: \"unmounted hook\",\n  [\"a\"]: \"activated hook\",\n  [\"da\"]: \"deactivated hook\",\n  [\"ec\"]: \"errorCaptured hook\",\n  [\"rtc\"]: \"renderTracked hook\",\n  [\"rtg\"]: \"renderTriggered hook\",\n  [0]: \"setup function\",\n  [1]: \"render function\",\n  [2]: \"watcher getter\",\n  [3]: \"watcher callback\",\n  [4]: \"watcher cleanup function\",\n  [5]: \"native event handler\",\n  [6]: \"component event handler\",\n  [7]: \"vnode hook\",\n  [8]: \"directive hook\",\n  [9]: \"transition hook\",\n  [10]: \"app errorHandler\",\n  [11]: \"app warnHandler\",\n  [12]: \"ref function\",\n  [13]: \"async component loader\",\n  [14]: \"scheduler flush\",\n  [15]: \"component update\",\n  [16]: \"app unmount cleanup function\"\n};\nfunction callWithErrorHandling(fn, instance, type, args) {\n  try {\n    return args ? fn(...args) : fn();\n  } catch (err) {\n    handleError(err, instance, type);\n  }\n}\nfunction callWithAsyncErrorHandling(fn, instance, type, args) {\n  if (isFunction(fn)) {\n    const res = callWithErrorHandling(fn, instance, type, args);\n    if (res && isPromise(res)) {\n      res.catch((err) => {\n        handleError(err, instance, type);\n      });\n    }\n    return res;\n  }\n  if (isArray(fn)) {\n    const values = [];\n    for (let i = 0; i < fn.length; i++) {\n      values.push(callWithAsyncErrorHandling(fn[i], instance, type, args));\n    }\n    return values;\n  } else if (true) {\n    warn$1(\n      `Invalid value type passed to callWithAsyncErrorHandling(): ${typeof fn}`\n    );\n  }\n}\nfunction handleError(err, instance, type, throwInDev = true) {\n  const contextVNode = instance ? instance.vnode : null;\n  const { errorHandler, throwUnhandledErrorInProduction } = instance && instance.appContext.config || EMPTY_OBJ;\n  if (instance) {\n    let cur = instance.parent;\n    const exposedInstance = instance.proxy;\n    const errorInfo = true ? ErrorTypeStrings$1[type] : `https://vuejs.org/error-reference/#runtime-${type}`;\n    while (cur) {\n      const errorCapturedHooks = cur.ec;\n      if (errorCapturedHooks) {\n        for (let i = 0; i < errorCapturedHooks.length; i++) {\n          if (errorCapturedHooks[i](err, exposedInstance, errorInfo) === false) {\n            return;\n          }\n        }\n      }\n      cur = cur.parent;\n    }\n    if (errorHandler) {\n      pauseTracking();\n      callWithErrorHandling(errorHandler, null, 10, [\n        err,\n        exposedInstance,\n        errorInfo\n      ]);\n      resetTracking();\n      return;\n    }\n  }\n  logError(err, type, contextVNode, throwInDev, throwUnhandledErrorInProduction);\n}\nfunction logError(err, type, contextVNode, throwInDev = true, throwInProd = false) {\n  if (true) {\n    const info = ErrorTypeStrings$1[type];\n    if (contextVNode) {\n      pushWarningContext(contextVNode);\n    }\n    warn$1(`Unhandled error${info ? ` during execution of ${info}` : ``}`);\n    if (contextVNode) {\n      popWarningContext();\n    }\n    if (throwInDev) {\n      throw err;\n    } else {\n      console.error(err);\n    }\n  } else if (throwInProd) {\n    throw err;\n  } else {\n    console.error(err);\n  }\n}\nvar queue = [];\nvar flushIndex = -1;\nvar pendingPostFlushCbs = [];\nvar activePostFlushCbs = null;\nvar postFlushIndex = 0;\nvar resolvedPromise = Promise.resolve();\nvar currentFlushPromise = null;\nvar RECURSION_LIMIT = 100;\nfunction nextTick(fn) {\n  const p2 = currentFlushPromise || resolvedPromise;\n  return fn ? p2.then(this ? fn.bind(this) : fn) : p2;\n}\nfunction findInsertionIndex(id) {\n  let start = flushIndex + 1;\n  let end = queue.length;\n  while (start < end) {\n    const middle = start + end >>> 1;\n    const middleJob = queue[middle];\n    const middleJobId = getId(middleJob);\n    if (middleJobId < id || middleJobId === id && middleJob.flags & 2) {\n      start = middle + 1;\n    } else {\n      end = middle;\n    }\n  }\n  return start;\n}\nfunction queueJob(job) {\n  if (!(job.flags & 1)) {\n    const jobId = getId(job);\n    const lastJob = queue[queue.length - 1];\n    if (!lastJob || // fast path when the job id is larger than the tail\n    !(job.flags & 2) && jobId >= getId(lastJob)) {\n      queue.push(job);\n    } else {\n      queue.splice(findInsertionIndex(jobId), 0, job);\n    }\n    job.flags |= 1;\n    queueFlush();\n  }\n}\nfunction queueFlush() {\n  if (!currentFlushPromise) {\n    currentFlushPromise = resolvedPromise.then(flushJobs);\n  }\n}\nfunction queuePostFlushCb(cb) {\n  if (!isArray(cb)) {\n    if (activePostFlushCbs && cb.id === -1) {\n      activePostFlushCbs.splice(postFlushIndex + 1, 0, cb);\n    } else if (!(cb.flags & 1)) {\n      pendingPostFlushCbs.push(cb);\n      cb.flags |= 1;\n    }\n  } else {\n    pendingPostFlushCbs.push(...cb);\n  }\n  queueFlush();\n}\nfunction flushPreFlushCbs(instance, seen, i = flushIndex + 1) {\n  if (true) {\n    seen = seen || /* @__PURE__ */ new Map();\n  }\n  for (; i < queue.length; i++) {\n    const cb = queue[i];\n    if (cb && cb.flags & 2) {\n      if (instance && cb.id !== instance.uid) {\n        continue;\n      }\n      if (checkRecursiveUpdates(seen, cb)) {\n        continue;\n      }\n      queue.splice(i, 1);\n      i--;\n      if (cb.flags & 4) {\n        cb.flags &= ~1;\n      }\n      cb();\n      if (!(cb.flags & 4)) {\n        cb.flags &= ~1;\n      }\n    }\n  }\n}\nfunction flushPostFlushCbs(seen) {\n  if (pendingPostFlushCbs.length) {\n    const deduped = [...new Set(pendingPostFlushCbs)].sort(\n      (a, b) => getId(a) - getId(b)\n    );\n    pendingPostFlushCbs.length = 0;\n    if (activePostFlushCbs) {\n      activePostFlushCbs.push(...deduped);\n      return;\n    }\n    activePostFlushCbs = deduped;\n    if (true) {\n      seen = seen || /* @__PURE__ */ new Map();\n    }\n    for (postFlushIndex = 0; postFlushIndex < activePostFlushCbs.length; postFlushIndex++) {\n      const cb = activePostFlushCbs[postFlushIndex];\n      if (checkRecursiveUpdates(seen, cb)) {\n        continue;\n      }\n      if (cb.flags & 4) {\n        cb.flags &= ~1;\n      }\n      if (!(cb.flags & 8)) cb();\n      cb.flags &= ~1;\n    }\n    activePostFlushCbs = null;\n    postFlushIndex = 0;\n  }\n}\nvar getId = (job) => job.id == null ? job.flags & 2 ? -1 : Infinity : job.id;\nfunction flushJobs(seen) {\n  if (true) {\n    seen = seen || /* @__PURE__ */ new Map();\n  }\n  const check = true ? (job) => checkRecursiveUpdates(seen, job) : NOOP;\n  try {\n    for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {\n      const job = queue[flushIndex];\n      if (job && !(job.flags & 8)) {\n        if (check(job)) {\n          continue;\n        }\n        if (job.flags & 4) {\n          job.flags &= ~1;\n        }\n        callWithErrorHandling(\n          job,\n          job.i,\n          job.i ? 15 : 14\n        );\n        if (!(job.flags & 4)) {\n          job.flags &= ~1;\n        }\n      }\n    }\n  } finally {\n    for (; flushIndex < queue.length; flushIndex++) {\n      const job = queue[flushIndex];\n      if (job) {\n        job.flags &= ~1;\n      }\n    }\n    flushIndex = -1;\n    queue.length = 0;\n    flushPostFlushCbs(seen);\n    currentFlushPromise = null;\n    if (queue.length || pendingPostFlushCbs.length) {\n      flushJobs(seen);\n    }\n  }\n}\nfunction checkRecursiveUpdates(seen, fn) {\n  const count = seen.get(fn) || 0;\n  if (count > RECURSION_LIMIT) {\n    const instance = fn.i;\n    const componentName = instance && getComponentName(instance.type);\n    handleError(\n      `Maximum recursive updates exceeded${componentName ? ` in component <${componentName}>` : ``}. This means you have a reactive effect that is mutating its own dependencies and thus recursively triggering itself. Possible sources include component template, render function, updated hook or watcher source function.`,\n      null,\n      10\n    );\n    return true;\n  }\n  seen.set(fn, count + 1);\n  return false;\n}\nvar isHmrUpdating = false;\nvar hmrDirtyComponents = /* @__PURE__ */ new Map();\nif (true) {\n  getGlobalThis().__VUE_HMR_RUNTIME__ = {\n    createRecord: tryWrap(createRecord),\n    rerender: tryWrap(rerender),\n    reload: tryWrap(reload)\n  };\n}\nvar map = /* @__PURE__ */ new Map();\nfunction registerHMR(instance) {\n  const id = instance.type.__hmrId;\n  let record = map.get(id);\n  if (!record) {\n    createRecord(id, instance.type);\n    record = map.get(id);\n  }\n  record.instances.add(instance);\n}\nfunction unregisterHMR(instance) {\n  map.get(instance.type.__hmrId).instances.delete(instance);\n}\nfunction createRecord(id, initialDef) {\n  if (map.has(id)) {\n    return false;\n  }\n  map.set(id, {\n    initialDef: normalizeClassComponent(initialDef),\n    instances: /* @__PURE__ */ new Set()\n  });\n  return true;\n}\nfunction normalizeClassComponent(component) {\n  return isClassComponent(component) ? component.__vccOpts : component;\n}\nfunction rerender(id, newRender) {\n  const record = map.get(id);\n  if (!record) {\n    return;\n  }\n  record.initialDef.render = newRender;\n  [...record.instances].forEach((instance) => {\n    if (newRender) {\n      instance.render = newRender;\n      normalizeClassComponent(instance.type).render = newRender;\n    }\n    instance.renderCache = [];\n    isHmrUpdating = true;\n    instance.update();\n    isHmrUpdating = false;\n  });\n}\nfunction reload(id, newComp) {\n  const record = map.get(id);\n  if (!record) return;\n  newComp = normalizeClassComponent(newComp);\n  updateComponentDef(record.initialDef, newComp);\n  const instances = [...record.instances];\n  for (let i = 0; i < instances.length; i++) {\n    const instance = instances[i];\n    const oldComp = normalizeClassComponent(instance.type);\n    let dirtyInstances = hmrDirtyComponents.get(oldComp);\n    if (!dirtyInstances) {\n      if (oldComp !== record.initialDef) {\n        updateComponentDef(oldComp, newComp);\n      }\n      hmrDirtyComponents.set(oldComp, dirtyInstances = /* @__PURE__ */ new Set());\n    }\n    dirtyInstances.add(instance);\n    instance.appContext.propsCache.delete(instance.type);\n    instance.appContext.emitsCache.delete(instance.type);\n    instance.appContext.optionsCache.delete(instance.type);\n    if (instance.ceReload) {\n      dirtyInstances.add(instance);\n      instance.ceReload(newComp.styles);\n      dirtyInstances.delete(instance);\n    } else if (instance.parent) {\n      queueJob(() => {\n        isHmrUpdating = true;\n        instance.parent.update();\n        isHmrUpdating = false;\n        dirtyInstances.delete(instance);\n      });\n    } else if (instance.appContext.reload) {\n      instance.appContext.reload();\n    } else if (typeof window !== \"undefined\") {\n      window.location.reload();\n    } else {\n      console.warn(\n        \"[HMR] Root or manually mounted instance modified. Full reload required.\"\n      );\n    }\n    if (instance.root.ce && instance !== instance.root) {\n      instance.root.ce._removeChildStyle(oldComp);\n    }\n  }\n  queuePostFlushCb(() => {\n    hmrDirtyComponents.clear();\n  });\n}\nfunction updateComponentDef(oldComp, newComp) {\n  extend(oldComp, newComp);\n  for (const key in oldComp) {\n    if (key !== \"__file\" && !(key in newComp)) {\n      delete oldComp[key];\n    }\n  }\n}\nfunction tryWrap(fn) {\n  return (id, arg) => {\n    try {\n      return fn(id, arg);\n    } catch (e) {\n      console.error(e);\n      console.warn(\n        `[HMR] Something went wrong during Vue component hot-reload. Full reload required.`\n      );\n    }\n  };\n}\nvar devtools$1;\nvar buffer = [];\nvar devtoolsNotInstalled = false;\nfunction emit$1(event, ...args) {\n  if (devtools$1) {\n    devtools$1.emit(event, ...args);\n  } else if (!devtoolsNotInstalled) {\n    buffer.push({ event, args });\n  }\n}\nfunction setDevtoolsHook$1(hook, target) {\n  var _a, _b;\n  devtools$1 = hook;\n  if (devtools$1) {\n    devtools$1.enabled = true;\n    buffer.forEach(({ event, args }) => devtools$1.emit(event, ...args));\n    buffer = [];\n  } else if (\n    // handle late devtools injection - only do this if we are in an actual\n    // browser environment to avoid the timer handle stalling test runner exit\n    // (#4815)\n    typeof window !== \"undefined\" && // some envs mock window but not fully\n    window.HTMLElement && // also exclude jsdom\n    // eslint-disable-next-line no-restricted-syntax\n    !((_b = (_a = window.navigator) == null ? void 0 : _a.userAgent) == null ? void 0 : _b.includes(\"jsdom\"))\n  ) {\n    const replay = target.__VUE_DEVTOOLS_HOOK_REPLAY__ = target.__VUE_DEVTOOLS_HOOK_REPLAY__ || [];\n    replay.push((newHook) => {\n      setDevtoolsHook$1(newHook, target);\n    });\n    setTimeout(() => {\n      if (!devtools$1) {\n        target.__VUE_DEVTOOLS_HOOK_REPLAY__ = null;\n        devtoolsNotInstalled = true;\n        buffer = [];\n      }\n    }, 3e3);\n  } else {\n    devtoolsNotInstalled = true;\n    buffer = [];\n  }\n}\nfunction devtoolsInitApp(app, version2) {\n  emit$1(\"app:init\", app, version2, {\n    Fragment,\n    Text,\n    Comment,\n    Static\n  });\n}\nfunction devtoolsUnmountApp(app) {\n  emit$1(\"app:unmount\", app);\n}\nvar devtoolsComponentAdded = createDevtoolsComponentHook(\n  \"component:added\"\n  /* COMPONENT_ADDED */\n);\nvar devtoolsComponentUpdated = createDevtoolsComponentHook(\n  \"component:updated\"\n  /* COMPONENT_UPDATED */\n);\nvar _devtoolsComponentRemoved = createDevtoolsComponentHook(\n  \"component:removed\"\n  /* COMPONENT_REMOVED */\n);\nvar devtoolsComponentRemoved = (component) => {\n  if (devtools$1 && typeof devtools$1.cleanupBuffer === \"function\" && // remove the component if it wasn't buffered\n  !devtools$1.cleanupBuffer(component)) {\n    _devtoolsComponentRemoved(component);\n  }\n};\nfunction createDevtoolsComponentHook(hook) {\n  return (component) => {\n    emit$1(\n      hook,\n      component.appContext.app,\n      component.uid,\n      component.parent ? component.parent.uid : void 0,\n      component\n    );\n  };\n}\nvar devtoolsPerfStart = createDevtoolsPerformanceHook(\n  \"perf:start\"\n  /* PERFORMANCE_START */\n);\nvar devtoolsPerfEnd = createDevtoolsPerformanceHook(\n  \"perf:end\"\n  /* PERFORMANCE_END */\n);\nfunction createDevtoolsPerformanceHook(hook) {\n  return (component, type, time) => {\n    emit$1(hook, component.appContext.app, component.uid, component, type, time);\n  };\n}\nfunction devtoolsComponentEmit(component, event, params) {\n  emit$1(\n    \"component:emit\",\n    component.appContext.app,\n    component,\n    event,\n    params\n  );\n}\nvar currentRenderingInstance = null;\nvar currentScopeId = null;\nfunction setCurrentRenderingInstance(instance) {\n  const prev = currentRenderingInstance;\n  currentRenderingInstance = instance;\n  currentScopeId = instance && instance.type.__scopeId || null;\n  return prev;\n}\nfunction pushScopeId(id) {\n  currentScopeId = id;\n}\nfunction popScopeId() {\n  currentScopeId = null;\n}\nvar withScopeId = (_id) => withCtx;\nfunction withCtx(fn, ctx = currentRenderingInstance, isNonScopedSlot) {\n  if (!ctx) return fn;\n  if (fn._n) {\n    return fn;\n  }\n  const renderFnWithContext = (...args) => {\n    if (renderFnWithContext._d) {\n      setBlockTracking(-1);\n    }\n    const prevInstance = setCurrentRenderingInstance(ctx);\n    let res;\n    try {\n      res = fn(...args);\n    } finally {\n      setCurrentRenderingInstance(prevInstance);\n      if (renderFnWithContext._d) {\n        setBlockTracking(1);\n      }\n    }\n    if (true) {\n      devtoolsComponentUpdated(ctx);\n    }\n    return res;\n  };\n  renderFnWithContext._n = true;\n  renderFnWithContext._c = true;\n  renderFnWithContext._d = true;\n  return renderFnWithContext;\n}\nfunction validateDirectiveName(name) {\n  if (isBuiltInDirective(name)) {\n    warn$1(\"Do not use built-in directive ids as custom directive id: \" + name);\n  }\n}\nfunction withDirectives(vnode, directives) {\n  if (currentRenderingInstance === null) {\n    warn$1(`withDirectives can only be used inside render functions.`);\n    return vnode;\n  }\n  const instance = getComponentPublicInstance(currentRenderingInstance);\n  const bindings = vnode.dirs || (vnode.dirs = []);\n  for (let i = 0; i < directives.length; i++) {\n    let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i];\n    if (dir) {\n      if (isFunction(dir)) {\n        dir = {\n          mounted: dir,\n          updated: dir\n        };\n      }\n      if (dir.deep) {\n        traverse(value);\n      }\n      bindings.push({\n        dir,\n        instance,\n        value,\n        oldValue: void 0,\n        arg,\n        modifiers\n      });\n    }\n  }\n  return vnode;\n}\nfunction invokeDirectiveHook(vnode, prevVNode, instance, name) {\n  const bindings = vnode.dirs;\n  const oldBindings = prevVNode && prevVNode.dirs;\n  for (let i = 0; i < bindings.length; i++) {\n    const binding = bindings[i];\n    if (oldBindings) {\n      binding.oldValue = oldBindings[i].value;\n    }\n    let hook = binding.dir[name];\n    if (hook) {\n      pauseTracking();\n      callWithAsyncErrorHandling(hook, instance, 8, [\n        vnode.el,\n        binding,\n        vnode,\n        prevVNode\n      ]);\n      resetTracking();\n    }\n  }\n}\nvar TeleportEndKey = Symbol(\"_vte\");\nvar isTeleport = (type) => type.__isTeleport;\nvar isTeleportDisabled = (props) => props && (props.disabled || props.disabled === \"\");\nvar isTeleportDeferred = (props) => props && (props.defer || props.defer === \"\");\nvar isTargetSVG = (target) => typeof SVGElement !== \"undefined\" && target instanceof SVGElement;\nvar isTargetMathML = (target) => typeof MathMLElement === \"function\" && target instanceof MathMLElement;\nvar resolveTarget = (props, select) => {\n  const targetSelector = props && props.to;\n  if (isString(targetSelector)) {\n    if (!select) {\n      warn$1(\n        `Current renderer does not support string target for Teleports. (missing querySelector renderer option)`\n      );\n      return null;\n    } else {\n      const target = select(targetSelector);\n      if (!target && !isTeleportDisabled(props)) {\n        warn$1(\n          `Failed to locate Teleport target with selector \"${targetSelector}\". Note the target element must exist before the component is mounted - i.e. the target cannot be rendered by the component itself, and ideally should be outside of the entire Vue component tree.`\n        );\n      }\n      return target;\n    }\n  } else {\n    if (!targetSelector && !isTeleportDisabled(props)) {\n      warn$1(`Invalid Teleport target: ${targetSelector}`);\n    }\n    return targetSelector;\n  }\n};\nvar TeleportImpl = {\n  name: \"Teleport\",\n  __isTeleport: true,\n  process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, internals) {\n    const {\n      mc: mountChildren,\n      pc: patchChildren,\n      pbc: patchBlockChildren,\n      o: { insert, querySelector, createText, createComment }\n    } = internals;\n    const disabled = isTeleportDisabled(n2.props);\n    let { shapeFlag, children, dynamicChildren } = n2;\n    if (isHmrUpdating) {\n      optimized = false;\n      dynamicChildren = null;\n    }\n    if (n1 == null) {\n      const placeholder = n2.el = true ? createComment(\"teleport start\") : createText(\"\");\n      const mainAnchor = n2.anchor = true ? createComment(\"teleport end\") : createText(\"\");\n      insert(placeholder, container, anchor);\n      insert(mainAnchor, container, anchor);\n      const mount = (container2, anchor2) => {\n        if (shapeFlag & 16) {\n          if (parentComponent && parentComponent.isCE) {\n            parentComponent.ce._teleportTarget = container2;\n          }\n          mountChildren(\n            children,\n            container2,\n            anchor2,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        }\n      };\n      const mountToTarget = () => {\n        const target = n2.target = resolveTarget(n2.props, querySelector);\n        const targetAnchor = prepareAnchor(target, n2, createText, insert);\n        if (target) {\n          if (namespace !== \"svg\" && isTargetSVG(target)) {\n            namespace = \"svg\";\n          } else if (namespace !== \"mathml\" && isTargetMathML(target)) {\n            namespace = \"mathml\";\n          }\n          if (!disabled) {\n            mount(target, targetAnchor);\n            updateCssVars(n2, false);\n          }\n        } else if (!disabled) {\n          warn$1(\n            \"Invalid Teleport target on mount:\",\n            target,\n            `(${typeof target})`\n          );\n        }\n      };\n      if (disabled) {\n        mount(container, mainAnchor);\n        updateCssVars(n2, true);\n      }\n      if (isTeleportDeferred(n2.props)) {\n        queuePostRenderEffect(() => {\n          mountToTarget();\n          n2.el.__isMounted = true;\n        }, parentSuspense);\n      } else {\n        mountToTarget();\n      }\n    } else {\n      if (isTeleportDeferred(n2.props) && !n1.el.__isMounted) {\n        queuePostRenderEffect(() => {\n          TeleportImpl.process(\n            n1,\n            n2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized,\n            internals\n          );\n          delete n1.el.__isMounted;\n        }, parentSuspense);\n        return;\n      }\n      n2.el = n1.el;\n      n2.targetStart = n1.targetStart;\n      const mainAnchor = n2.anchor = n1.anchor;\n      const target = n2.target = n1.target;\n      const targetAnchor = n2.targetAnchor = n1.targetAnchor;\n      const wasDisabled = isTeleportDisabled(n1.props);\n      const currentContainer = wasDisabled ? container : target;\n      const currentAnchor = wasDisabled ? mainAnchor : targetAnchor;\n      if (namespace === \"svg\" || isTargetSVG(target)) {\n        namespace = \"svg\";\n      } else if (namespace === \"mathml\" || isTargetMathML(target)) {\n        namespace = \"mathml\";\n      }\n      if (dynamicChildren) {\n        patchBlockChildren(\n          n1.dynamicChildren,\n          dynamicChildren,\n          currentContainer,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds\n        );\n        traverseStaticChildren(n1, n2, true);\n      } else if (!optimized) {\n        patchChildren(\n          n1,\n          n2,\n          currentContainer,\n          currentAnchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          false\n        );\n      }\n      if (disabled) {\n        if (!wasDisabled) {\n          moveTeleport(\n            n2,\n            container,\n            mainAnchor,\n            internals,\n            1\n          );\n        } else {\n          if (n2.props && n1.props && n2.props.to !== n1.props.to) {\n            n2.props.to = n1.props.to;\n          }\n        }\n      } else {\n        if ((n2.props && n2.props.to) !== (n1.props && n1.props.to)) {\n          const nextTarget = n2.target = resolveTarget(\n            n2.props,\n            querySelector\n          );\n          if (nextTarget) {\n            moveTeleport(\n              n2,\n              nextTarget,\n              null,\n              internals,\n              0\n            );\n          } else if (true) {\n            warn$1(\n              \"Invalid Teleport target on update:\",\n              target,\n              `(${typeof target})`\n            );\n          }\n        } else if (wasDisabled) {\n          moveTeleport(\n            n2,\n            target,\n            targetAnchor,\n            internals,\n            1\n          );\n        }\n      }\n      updateCssVars(n2, disabled);\n    }\n  },\n  remove(vnode, parentComponent, parentSuspense, { um: unmount, o: { remove: hostRemove } }, doRemove) {\n    const {\n      shapeFlag,\n      children,\n      anchor,\n      targetStart,\n      targetAnchor,\n      target,\n      props\n    } = vnode;\n    if (target) {\n      hostRemove(targetStart);\n      hostRemove(targetAnchor);\n    }\n    doRemove && hostRemove(anchor);\n    if (shapeFlag & 16) {\n      const shouldRemove = doRemove || !isTeleportDisabled(props);\n      for (let i = 0; i < children.length; i++) {\n        const child = children[i];\n        unmount(\n          child,\n          parentComponent,\n          parentSuspense,\n          shouldRemove,\n          !!child.dynamicChildren\n        );\n      }\n    }\n  },\n  move: moveTeleport,\n  hydrate: hydrateTeleport\n};\nfunction moveTeleport(vnode, container, parentAnchor, { o: { insert }, m: move }, moveType = 2) {\n  if (moveType === 0) {\n    insert(vnode.targetAnchor, container, parentAnchor);\n  }\n  const { el, anchor, shapeFlag, children, props } = vnode;\n  const isReorder = moveType === 2;\n  if (isReorder) {\n    insert(el, container, parentAnchor);\n  }\n  if (!isReorder || isTeleportDisabled(props)) {\n    if (shapeFlag & 16) {\n      for (let i = 0; i < children.length; i++) {\n        move(\n          children[i],\n          container,\n          parentAnchor,\n          2\n        );\n      }\n    }\n  }\n  if (isReorder) {\n    insert(anchor, container, parentAnchor);\n  }\n}\nfunction hydrateTeleport(node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized, {\n  o: { nextSibling, parentNode, querySelector, insert, createText }\n}, hydrateChildren) {\n  const target = vnode.target = resolveTarget(\n    vnode.props,\n    querySelector\n  );\n  if (target) {\n    const disabled = isTeleportDisabled(vnode.props);\n    const targetNode = target._lpa || target.firstChild;\n    if (vnode.shapeFlag & 16) {\n      if (disabled) {\n        vnode.anchor = hydrateChildren(\n          nextSibling(node),\n          vnode,\n          parentNode(node),\n          parentComponent,\n          parentSuspense,\n          slotScopeIds,\n          optimized\n        );\n        vnode.targetStart = targetNode;\n        vnode.targetAnchor = targetNode && nextSibling(targetNode);\n      } else {\n        vnode.anchor = nextSibling(node);\n        let targetAnchor = targetNode;\n        while (targetAnchor) {\n          if (targetAnchor && targetAnchor.nodeType === 8) {\n            if (targetAnchor.data === \"teleport start anchor\") {\n              vnode.targetStart = targetAnchor;\n            } else if (targetAnchor.data === \"teleport anchor\") {\n              vnode.targetAnchor = targetAnchor;\n              target._lpa = vnode.targetAnchor && nextSibling(vnode.targetAnchor);\n              break;\n            }\n          }\n          targetAnchor = nextSibling(targetAnchor);\n        }\n        if (!vnode.targetAnchor) {\n          prepareAnchor(target, vnode, createText, insert);\n        }\n        hydrateChildren(\n          targetNode && nextSibling(targetNode),\n          vnode,\n          target,\n          parentComponent,\n          parentSuspense,\n          slotScopeIds,\n          optimized\n        );\n      }\n    }\n    updateCssVars(vnode, disabled);\n  }\n  return vnode.anchor && nextSibling(vnode.anchor);\n}\nvar Teleport = TeleportImpl;\nfunction updateCssVars(vnode, isDisabled) {\n  const ctx = vnode.ctx;\n  if (ctx && ctx.ut) {\n    let node, anchor;\n    if (isDisabled) {\n      node = vnode.el;\n      anchor = vnode.anchor;\n    } else {\n      node = vnode.targetStart;\n      anchor = vnode.targetAnchor;\n    }\n    while (node && node !== anchor) {\n      if (node.nodeType === 1) node.setAttribute(\"data-v-owner\", ctx.uid);\n      node = node.nextSibling;\n    }\n    ctx.ut();\n  }\n}\nfunction prepareAnchor(target, vnode, createText, insert) {\n  const targetStart = vnode.targetStart = createText(\"\");\n  const targetAnchor = vnode.targetAnchor = createText(\"\");\n  targetStart[TeleportEndKey] = targetAnchor;\n  if (target) {\n    insert(targetStart, target);\n    insert(targetAnchor, target);\n  }\n  return targetAnchor;\n}\nvar leaveCbKey = Symbol(\"_leaveCb\");\nvar enterCbKey = Symbol(\"_enterCb\");\nfunction useTransitionState() {\n  const state = {\n    isMounted: false,\n    isLeaving: false,\n    isUnmounting: false,\n    leavingVNodes: /* @__PURE__ */ new Map()\n  };\n  onMounted(() => {\n    state.isMounted = true;\n  });\n  onBeforeUnmount(() => {\n    state.isUnmounting = true;\n  });\n  return state;\n}\nvar TransitionHookValidator = [Function, Array];\nvar BaseTransitionPropsValidators = {\n  mode: String,\n  appear: Boolean,\n  persisted: Boolean,\n  // enter\n  onBeforeEnter: TransitionHookValidator,\n  onEnter: TransitionHookValidator,\n  onAfterEnter: TransitionHookValidator,\n  onEnterCancelled: TransitionHookValidator,\n  // leave\n  onBeforeLeave: TransitionHookValidator,\n  onLeave: TransitionHookValidator,\n  onAfterLeave: TransitionHookValidator,\n  onLeaveCancelled: TransitionHookValidator,\n  // appear\n  onBeforeAppear: TransitionHookValidator,\n  onAppear: TransitionHookValidator,\n  onAfterAppear: TransitionHookValidator,\n  onAppearCancelled: TransitionHookValidator\n};\nvar recursiveGetSubtree = (instance) => {\n  const subTree = instance.subTree;\n  return subTree.component ? recursiveGetSubtree(subTree.component) : subTree;\n};\nvar BaseTransitionImpl = {\n  name: `BaseTransition`,\n  props: BaseTransitionPropsValidators,\n  setup(props, { slots }) {\n    const instance = getCurrentInstance();\n    const state = useTransitionState();\n    return () => {\n      const children = slots.default && getTransitionRawChildren(slots.default(), true);\n      if (!children || !children.length) {\n        return;\n      }\n      const child = findNonCommentChild(children);\n      const rawProps = toRaw(props);\n      const { mode } = rawProps;\n      if (mode && mode !== \"in-out\" && mode !== \"out-in\" && mode !== \"default\") {\n        warn$1(`invalid <transition> mode: ${mode}`);\n      }\n      if (state.isLeaving) {\n        return emptyPlaceholder(child);\n      }\n      const innerChild = getInnerChild$1(child);\n      if (!innerChild) {\n        return emptyPlaceholder(child);\n      }\n      let enterHooks = resolveTransitionHooks(\n        innerChild,\n        rawProps,\n        state,\n        instance,\n        // #11061, ensure enterHooks is fresh after clone\n        (hooks) => enterHooks = hooks\n      );\n      if (innerChild.type !== Comment) {\n        setTransitionHooks(innerChild, enterHooks);\n      }\n      let oldInnerChild = instance.subTree && getInnerChild$1(instance.subTree);\n      if (oldInnerChild && oldInnerChild.type !== Comment && !isSameVNodeType(innerChild, oldInnerChild) && recursiveGetSubtree(instance).type !== Comment) {\n        let leavingHooks = resolveTransitionHooks(\n          oldInnerChild,\n          rawProps,\n          state,\n          instance\n        );\n        setTransitionHooks(oldInnerChild, leavingHooks);\n        if (mode === \"out-in\" && innerChild.type !== Comment) {\n          state.isLeaving = true;\n          leavingHooks.afterLeave = () => {\n            state.isLeaving = false;\n            if (!(instance.job.flags & 8)) {\n              instance.update();\n            }\n            delete leavingHooks.afterLeave;\n            oldInnerChild = void 0;\n          };\n          return emptyPlaceholder(child);\n        } else if (mode === \"in-out\" && innerChild.type !== Comment) {\n          leavingHooks.delayLeave = (el, earlyRemove, delayedLeave) => {\n            const leavingVNodesCache = getLeavingNodesForType(\n              state,\n              oldInnerChild\n            );\n            leavingVNodesCache[String(oldInnerChild.key)] = oldInnerChild;\n            el[leaveCbKey] = () => {\n              earlyRemove();\n              el[leaveCbKey] = void 0;\n              delete enterHooks.delayedLeave;\n              oldInnerChild = void 0;\n            };\n            enterHooks.delayedLeave = () => {\n              delayedLeave();\n              delete enterHooks.delayedLeave;\n              oldInnerChild = void 0;\n            };\n          };\n        } else {\n          oldInnerChild = void 0;\n        }\n      } else if (oldInnerChild) {\n        oldInnerChild = void 0;\n      }\n      return child;\n    };\n  }\n};\nfunction findNonCommentChild(children) {\n  let child = children[0];\n  if (children.length > 1) {\n    let hasFound = false;\n    for (const c of children) {\n      if (c.type !== Comment) {\n        if (hasFound) {\n          warn$1(\n            \"<transition> can only be used on a single element or component. Use <transition-group> for lists.\"\n          );\n          break;\n        }\n        child = c;\n        hasFound = true;\n        if (false) break;\n      }\n    }\n  }\n  return child;\n}\nvar BaseTransition = BaseTransitionImpl;\nfunction getLeavingNodesForType(state, vnode) {\n  const { leavingVNodes } = state;\n  let leavingVNodesCache = leavingVNodes.get(vnode.type);\n  if (!leavingVNodesCache) {\n    leavingVNodesCache = /* @__PURE__ */ Object.create(null);\n    leavingVNodes.set(vnode.type, leavingVNodesCache);\n  }\n  return leavingVNodesCache;\n}\nfunction resolveTransitionHooks(vnode, props, state, instance, postClone) {\n  const {\n    appear,\n    mode,\n    persisted = false,\n    onBeforeEnter,\n    onEnter,\n    onAfterEnter,\n    onEnterCancelled,\n    onBeforeLeave,\n    onLeave,\n    onAfterLeave,\n    onLeaveCancelled,\n    onBeforeAppear,\n    onAppear,\n    onAfterAppear,\n    onAppearCancelled\n  } = props;\n  const key = String(vnode.key);\n  const leavingVNodesCache = getLeavingNodesForType(state, vnode);\n  const callHook3 = (hook, args) => {\n    hook && callWithAsyncErrorHandling(\n      hook,\n      instance,\n      9,\n      args\n    );\n  };\n  const callAsyncHook = (hook, args) => {\n    const done = args[1];\n    callHook3(hook, args);\n    if (isArray(hook)) {\n      if (hook.every((hook2) => hook2.length <= 1)) done();\n    } else if (hook.length <= 1) {\n      done();\n    }\n  };\n  const hooks = {\n    mode,\n    persisted,\n    beforeEnter(el) {\n      let hook = onBeforeEnter;\n      if (!state.isMounted) {\n        if (appear) {\n          hook = onBeforeAppear || onBeforeEnter;\n        } else {\n          return;\n        }\n      }\n      if (el[leaveCbKey]) {\n        el[leaveCbKey](\n          true\n          /* cancelled */\n        );\n      }\n      const leavingVNode = leavingVNodesCache[key];\n      if (leavingVNode && isSameVNodeType(vnode, leavingVNode) && leavingVNode.el[leaveCbKey]) {\n        leavingVNode.el[leaveCbKey]();\n      }\n      callHook3(hook, [el]);\n    },\n    enter(el) {\n      let hook = onEnter;\n      let afterHook = onAfterEnter;\n      let cancelHook = onEnterCancelled;\n      if (!state.isMounted) {\n        if (appear) {\n          hook = onAppear || onEnter;\n          afterHook = onAfterAppear || onAfterEnter;\n          cancelHook = onAppearCancelled || onEnterCancelled;\n        } else {\n          return;\n        }\n      }\n      let called = false;\n      const done = el[enterCbKey] = (cancelled) => {\n        if (called) return;\n        called = true;\n        if (cancelled) {\n          callHook3(cancelHook, [el]);\n        } else {\n          callHook3(afterHook, [el]);\n        }\n        if (hooks.delayedLeave) {\n          hooks.delayedLeave();\n        }\n        el[enterCbKey] = void 0;\n      };\n      if (hook) {\n        callAsyncHook(hook, [el, done]);\n      } else {\n        done();\n      }\n    },\n    leave(el, remove2) {\n      const key2 = String(vnode.key);\n      if (el[enterCbKey]) {\n        el[enterCbKey](\n          true\n          /* cancelled */\n        );\n      }\n      if (state.isUnmounting) {\n        return remove2();\n      }\n      callHook3(onBeforeLeave, [el]);\n      let called = false;\n      const done = el[leaveCbKey] = (cancelled) => {\n        if (called) return;\n        called = true;\n        remove2();\n        if (cancelled) {\n          callHook3(onLeaveCancelled, [el]);\n        } else {\n          callHook3(onAfterLeave, [el]);\n        }\n        el[leaveCbKey] = void 0;\n        if (leavingVNodesCache[key2] === vnode) {\n          delete leavingVNodesCache[key2];\n        }\n      };\n      leavingVNodesCache[key2] = vnode;\n      if (onLeave) {\n        callAsyncHook(onLeave, [el, done]);\n      } else {\n        done();\n      }\n    },\n    clone(vnode2) {\n      const hooks2 = resolveTransitionHooks(\n        vnode2,\n        props,\n        state,\n        instance,\n        postClone\n      );\n      if (postClone) postClone(hooks2);\n      return hooks2;\n    }\n  };\n  return hooks;\n}\nfunction emptyPlaceholder(vnode) {\n  if (isKeepAlive(vnode)) {\n    vnode = cloneVNode(vnode);\n    vnode.children = null;\n    return vnode;\n  }\n}\nfunction getInnerChild$1(vnode) {\n  if (!isKeepAlive(vnode)) {\n    if (isTeleport(vnode.type) && vnode.children) {\n      return findNonCommentChild(vnode.children);\n    }\n    return vnode;\n  }\n  if (vnode.component) {\n    return vnode.component.subTree;\n  }\n  const { shapeFlag, children } = vnode;\n  if (children) {\n    if (shapeFlag & 16) {\n      return children[0];\n    }\n    if (shapeFlag & 32 && isFunction(children.default)) {\n      return children.default();\n    }\n  }\n}\nfunction setTransitionHooks(vnode, hooks) {\n  if (vnode.shapeFlag & 6 && vnode.component) {\n    vnode.transition = hooks;\n    setTransitionHooks(vnode.component.subTree, hooks);\n  } else if (vnode.shapeFlag & 128) {\n    vnode.ssContent.transition = hooks.clone(vnode.ssContent);\n    vnode.ssFallback.transition = hooks.clone(vnode.ssFallback);\n  } else {\n    vnode.transition = hooks;\n  }\n}\nfunction getTransitionRawChildren(children, keepComment = false, parentKey) {\n  let ret = [];\n  let keyedFragmentCount = 0;\n  for (let i = 0; i < children.length; i++) {\n    let child = children[i];\n    const key = parentKey == null ? child.key : String(parentKey) + String(child.key != null ? child.key : i);\n    if (child.type === Fragment) {\n      if (child.patchFlag & 128) keyedFragmentCount++;\n      ret = ret.concat(\n        getTransitionRawChildren(child.children, keepComment, key)\n      );\n    } else if (keepComment || child.type !== Comment) {\n      ret.push(key != null ? cloneVNode(child, { key }) : child);\n    }\n  }\n  if (keyedFragmentCount > 1) {\n    for (let i = 0; i < ret.length; i++) {\n      ret[i].patchFlag = -2;\n    }\n  }\n  return ret;\n}\nfunction defineComponent(options, extraOptions) {\n  return isFunction(options) ? (\n    // #8236: extend call and options.name access are considered side-effects\n    // by Rollup, so we have to wrap it in a pure-annotated IIFE.\n    (() => extend({ name: options.name }, extraOptions, { setup: options }))()\n  ) : options;\n}\nfunction useId() {\n  const i = getCurrentInstance();\n  if (i) {\n    return (i.appContext.config.idPrefix || \"v\") + \"-\" + i.ids[0] + i.ids[1]++;\n  } else if (true) {\n    warn$1(\n      `useId() is called when there is no active component instance to be associated with.`\n    );\n  }\n  return \"\";\n}\nfunction markAsyncBoundary(instance) {\n  instance.ids = [instance.ids[0] + instance.ids[2]++ + \"-\", 0, 0];\n}\nvar knownTemplateRefs = /* @__PURE__ */ new WeakSet();\nfunction useTemplateRef(key) {\n  const i = getCurrentInstance();\n  const r = shallowRef(null);\n  if (i) {\n    const refs = i.refs === EMPTY_OBJ ? i.refs = {} : i.refs;\n    let desc;\n    if ((desc = Object.getOwnPropertyDescriptor(refs, key)) && !desc.configurable) {\n      warn$1(`useTemplateRef('${key}') already exists.`);\n    } else {\n      Object.defineProperty(refs, key, {\n        enumerable: true,\n        get: () => r.value,\n        set: (val) => r.value = val\n      });\n    }\n  } else if (true) {\n    warn$1(\n      `useTemplateRef() is called when there is no active component instance to be associated with.`\n    );\n  }\n  const ret = true ? readonly(r) : r;\n  if (true) {\n    knownTemplateRefs.add(ret);\n  }\n  return ret;\n}\nfunction setRef(rawRef, oldRawRef, parentSuspense, vnode, isUnmount = false) {\n  if (isArray(rawRef)) {\n    rawRef.forEach(\n      (r, i) => setRef(\n        r,\n        oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),\n        parentSuspense,\n        vnode,\n        isUnmount\n      )\n    );\n    return;\n  }\n  if (isAsyncWrapper(vnode) && !isUnmount) {\n    if (vnode.shapeFlag & 512 && vnode.type.__asyncResolved && vnode.component.subTree.component) {\n      setRef(rawRef, oldRawRef, parentSuspense, vnode.component.subTree);\n    }\n    return;\n  }\n  const refValue = vnode.shapeFlag & 4 ? getComponentPublicInstance(vnode.component) : vnode.el;\n  const value = isUnmount ? null : refValue;\n  const { i: owner, r: ref2 } = rawRef;\n  if (!owner) {\n    warn$1(\n      `Missing ref owner context. ref cannot be used on hoisted vnodes. A vnode with ref must be created inside the render function.`\n    );\n    return;\n  }\n  const oldRef = oldRawRef && oldRawRef.r;\n  const refs = owner.refs === EMPTY_OBJ ? owner.refs = {} : owner.refs;\n  const setupState = owner.setupState;\n  const rawSetupState = toRaw(setupState);\n  const canSetSetupRef = setupState === EMPTY_OBJ ? () => false : (key) => {\n    if (true) {\n      if (hasOwn(rawSetupState, key) && !isRef2(rawSetupState[key])) {\n        warn$1(\n          `Template ref \"${key}\" used on a non-ref value. It will not work in the production build.`\n        );\n      }\n      if (knownTemplateRefs.has(rawSetupState[key])) {\n        return false;\n      }\n    }\n    return hasOwn(rawSetupState, key);\n  };\n  if (oldRef != null && oldRef !== ref2) {\n    if (isString(oldRef)) {\n      refs[oldRef] = null;\n      if (canSetSetupRef(oldRef)) {\n        setupState[oldRef] = null;\n      }\n    } else if (isRef2(oldRef)) {\n      oldRef.value = null;\n    }\n  }\n  if (isFunction(ref2)) {\n    callWithErrorHandling(ref2, owner, 12, [value, refs]);\n  } else {\n    const _isString = isString(ref2);\n    const _isRef = isRef2(ref2);\n    if (_isString || _isRef) {\n      const doSet = () => {\n        if (rawRef.f) {\n          const existing = _isString ? canSetSetupRef(ref2) ? setupState[ref2] : refs[ref2] : ref2.value;\n          if (isUnmount) {\n            isArray(existing) && remove(existing, refValue);\n          } else {\n            if (!isArray(existing)) {\n              if (_isString) {\n                refs[ref2] = [refValue];\n                if (canSetSetupRef(ref2)) {\n                  setupState[ref2] = refs[ref2];\n                }\n              } else {\n                ref2.value = [refValue];\n                if (rawRef.k) refs[rawRef.k] = ref2.value;\n              }\n            } else if (!existing.includes(refValue)) {\n              existing.push(refValue);\n            }\n          }\n        } else if (_isString) {\n          refs[ref2] = value;\n          if (canSetSetupRef(ref2)) {\n            setupState[ref2] = value;\n          }\n        } else if (_isRef) {\n          ref2.value = value;\n          if (rawRef.k) refs[rawRef.k] = value;\n        } else if (true) {\n          warn$1(\"Invalid template ref type:\", ref2, `(${typeof ref2})`);\n        }\n      };\n      if (value) {\n        doSet.id = -1;\n        queuePostRenderEffect(doSet, parentSuspense);\n      } else {\n        doSet();\n      }\n    } else if (true) {\n      warn$1(\"Invalid template ref type:\", ref2, `(${typeof ref2})`);\n    }\n  }\n}\nvar hasLoggedMismatchError = false;\nvar logMismatchError = () => {\n  if (hasLoggedMismatchError) {\n    return;\n  }\n  console.error(\"Hydration completed but contains mismatches.\");\n  hasLoggedMismatchError = true;\n};\nvar isSVGContainer = (container) => container.namespaceURI.includes(\"svg\") && container.tagName !== \"foreignObject\";\nvar isMathMLContainer = (container) => container.namespaceURI.includes(\"MathML\");\nvar getContainerType = (container) => {\n  if (container.nodeType !== 1) return void 0;\n  if (isSVGContainer(container)) return \"svg\";\n  if (isMathMLContainer(container)) return \"mathml\";\n  return void 0;\n};\nvar isComment = (node) => node.nodeType === 8;\nfunction createHydrationFunctions(rendererInternals) {\n  const {\n    mt: mountComponent,\n    p: patch,\n    o: {\n      patchProp: patchProp2,\n      createText,\n      nextSibling,\n      parentNode,\n      remove: remove2,\n      insert,\n      createComment\n    }\n  } = rendererInternals;\n  const hydrate2 = (vnode, container) => {\n    if (!container.hasChildNodes()) {\n      warn$1(\n        `Attempting to hydrate existing markup but container is empty. Performing full mount instead.`\n      );\n      patch(null, vnode, container);\n      flushPostFlushCbs();\n      container._vnode = vnode;\n      return;\n    }\n    hydrateNode(container.firstChild, vnode, null, null, null);\n    flushPostFlushCbs();\n    container._vnode = vnode;\n  };\n  const hydrateNode = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized = false) => {\n    optimized = optimized || !!vnode.dynamicChildren;\n    const isFragmentStart = isComment(node) && node.data === \"[\";\n    const onMismatch = () => handleMismatch(\n      node,\n      vnode,\n      parentComponent,\n      parentSuspense,\n      slotScopeIds,\n      isFragmentStart\n    );\n    const { type, ref: ref2, shapeFlag, patchFlag } = vnode;\n    let domType = node.nodeType;\n    vnode.el = node;\n    if (true) {\n      def(node, \"__vnode\", vnode, true);\n      def(node, \"__vueParentComponent\", parentComponent, true);\n    }\n    if (patchFlag === -2) {\n      optimized = false;\n      vnode.dynamicChildren = null;\n    }\n    let nextNode = null;\n    switch (type) {\n      case Text:\n        if (domType !== 3) {\n          if (vnode.children === \"\") {\n            insert(vnode.el = createText(\"\"), parentNode(node), node);\n            nextNode = node;\n          } else {\n            nextNode = onMismatch();\n          }\n        } else {\n          if (node.data !== vnode.children) {\n            warn$1(\n              `Hydration text mismatch in`,\n              node.parentNode,\n              `\n  - rendered on server: ${JSON.stringify(\n                node.data\n              )}\n  - expected on client: ${JSON.stringify(vnode.children)}`\n            );\n            logMismatchError();\n            node.data = vnode.children;\n          }\n          nextNode = nextSibling(node);\n        }\n        break;\n      case Comment:\n        if (isTemplateNode(node)) {\n          nextNode = nextSibling(node);\n          replaceNode(\n            vnode.el = node.content.firstChild,\n            node,\n            parentComponent\n          );\n        } else if (domType !== 8 || isFragmentStart) {\n          nextNode = onMismatch();\n        } else {\n          nextNode = nextSibling(node);\n        }\n        break;\n      case Static:\n        if (isFragmentStart) {\n          node = nextSibling(node);\n          domType = node.nodeType;\n        }\n        if (domType === 1 || domType === 3) {\n          nextNode = node;\n          const needToAdoptContent = !vnode.children.length;\n          for (let i = 0; i < vnode.staticCount; i++) {\n            if (needToAdoptContent)\n              vnode.children += nextNode.nodeType === 1 ? nextNode.outerHTML : nextNode.data;\n            if (i === vnode.staticCount - 1) {\n              vnode.anchor = nextNode;\n            }\n            nextNode = nextSibling(nextNode);\n          }\n          return isFragmentStart ? nextSibling(nextNode) : nextNode;\n        } else {\n          onMismatch();\n        }\n        break;\n      case Fragment:\n        if (!isFragmentStart) {\n          nextNode = onMismatch();\n        } else {\n          nextNode = hydrateFragment(\n            node,\n            vnode,\n            parentComponent,\n            parentSuspense,\n            slotScopeIds,\n            optimized\n          );\n        }\n        break;\n      default:\n        if (shapeFlag & 1) {\n          if ((domType !== 1 || vnode.type.toLowerCase() !== node.tagName.toLowerCase()) && !isTemplateNode(node)) {\n            nextNode = onMismatch();\n          } else {\n            nextNode = hydrateElement(\n              node,\n              vnode,\n              parentComponent,\n              parentSuspense,\n              slotScopeIds,\n              optimized\n            );\n          }\n        } else if (shapeFlag & 6) {\n          vnode.slotScopeIds = slotScopeIds;\n          const container = parentNode(node);\n          if (isFragmentStart) {\n            nextNode = locateClosingAnchor(node);\n          } else if (isComment(node) && node.data === \"teleport start\") {\n            nextNode = locateClosingAnchor(node, node.data, \"teleport end\");\n          } else {\n            nextNode = nextSibling(node);\n          }\n          mountComponent(\n            vnode,\n            container,\n            null,\n            parentComponent,\n            parentSuspense,\n            getContainerType(container),\n            optimized\n          );\n          if (isAsyncWrapper(vnode) && !vnode.type.__asyncResolved) {\n            let subTree;\n            if (isFragmentStart) {\n              subTree = createVNode(Fragment);\n              subTree.anchor = nextNode ? nextNode.previousSibling : container.lastChild;\n            } else {\n              subTree = node.nodeType === 3 ? createTextVNode(\"\") : createVNode(\"div\");\n            }\n            subTree.el = node;\n            vnode.component.subTree = subTree;\n          }\n        } else if (shapeFlag & 64) {\n          if (domType !== 8) {\n            nextNode = onMismatch();\n          } else {\n            nextNode = vnode.type.hydrate(\n              node,\n              vnode,\n              parentComponent,\n              parentSuspense,\n              slotScopeIds,\n              optimized,\n              rendererInternals,\n              hydrateChildren\n            );\n          }\n        } else if (shapeFlag & 128) {\n          nextNode = vnode.type.hydrate(\n            node,\n            vnode,\n            parentComponent,\n            parentSuspense,\n            getContainerType(parentNode(node)),\n            slotScopeIds,\n            optimized,\n            rendererInternals,\n            hydrateNode\n          );\n        } else if (true) {\n          warn$1(\"Invalid HostVNode type:\", type, `(${typeof type})`);\n        }\n    }\n    if (ref2 != null) {\n      setRef(ref2, null, parentSuspense, vnode);\n    }\n    return nextNode;\n  };\n  const hydrateElement = (el, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => {\n    optimized = optimized || !!vnode.dynamicChildren;\n    const { type, props, patchFlag, shapeFlag, dirs, transition } = vnode;\n    const forcePatch = type === \"input\" || type === \"option\";\n    if (true) {\n      if (dirs) {\n        invokeDirectiveHook(vnode, null, parentComponent, \"created\");\n      }\n      let needCallTransitionHooks = false;\n      if (isTemplateNode(el)) {\n        needCallTransitionHooks = needTransition(\n          null,\n          // no need check parentSuspense in hydration\n          transition\n        ) && parentComponent && parentComponent.vnode.props && parentComponent.vnode.props.appear;\n        const content = el.content.firstChild;\n        if (needCallTransitionHooks) {\n          transition.beforeEnter(content);\n        }\n        replaceNode(content, el, parentComponent);\n        vnode.el = el = content;\n      }\n      if (shapeFlag & 16 && // skip if element has innerHTML / textContent\n      !(props && (props.innerHTML || props.textContent))) {\n        let next = hydrateChildren(\n          el.firstChild,\n          vnode,\n          el,\n          parentComponent,\n          parentSuspense,\n          slotScopeIds,\n          optimized\n        );\n        let hasWarned2 = false;\n        while (next) {\n          if (!isMismatchAllowed(\n            el,\n            1\n            /* CHILDREN */\n          )) {\n            if (!hasWarned2) {\n              warn$1(\n                `Hydration children mismatch on`,\n                el,\n                `\nServer rendered element contains more child nodes than client vdom.`\n              );\n              hasWarned2 = true;\n            }\n            logMismatchError();\n          }\n          const cur = next;\n          next = next.nextSibling;\n          remove2(cur);\n        }\n      } else if (shapeFlag & 8) {\n        let clientText = vnode.children;\n        if (clientText[0] === \"\\n\" && (el.tagName === \"PRE\" || el.tagName === \"TEXTAREA\")) {\n          clientText = clientText.slice(1);\n        }\n        if (el.textContent !== clientText) {\n          if (!isMismatchAllowed(\n            el,\n            0\n            /* TEXT */\n          )) {\n            warn$1(\n              `Hydration text content mismatch on`,\n              el,\n              `\n  - rendered on server: ${el.textContent}\n  - expected on client: ${vnode.children}`\n            );\n            logMismatchError();\n          }\n          el.textContent = vnode.children;\n        }\n      }\n      if (props) {\n        if (true) {\n          const isCustomElement = el.tagName.includes(\"-\");\n          for (const key in props) {\n            if (// #11189 skip if this node has directives that have created hooks\n            // as it could have mutated the DOM in any possible way\n            !(dirs && dirs.some((d) => d.dir.created)) && propHasMismatch(el, key, props[key], vnode, parentComponent)) {\n              logMismatchError();\n            }\n            if (forcePatch && (key.endsWith(\"value\") || key === \"indeterminate\") || isOn(key) && !isReservedProp(key) || // force hydrate v-bind with .prop modifiers\n            key[0] === \".\" || isCustomElement) {\n              patchProp2(el, key, null, props[key], void 0, parentComponent);\n            }\n          }\n        } else if (props.onClick) {\n          patchProp2(\n            el,\n            \"onClick\",\n            null,\n            props.onClick,\n            void 0,\n            parentComponent\n          );\n        } else if (patchFlag & 4 && isReactive(props.style)) {\n          for (const key in props.style) props.style[key];\n        }\n      }\n      let vnodeHooks;\n      if (vnodeHooks = props && props.onVnodeBeforeMount) {\n        invokeVNodeHook(vnodeHooks, parentComponent, vnode);\n      }\n      if (dirs) {\n        invokeDirectiveHook(vnode, null, parentComponent, \"beforeMount\");\n      }\n      if ((vnodeHooks = props && props.onVnodeMounted) || dirs || needCallTransitionHooks) {\n        queueEffectWithSuspense(() => {\n          vnodeHooks && invokeVNodeHook(vnodeHooks, parentComponent, vnode);\n          needCallTransitionHooks && transition.enter(el);\n          dirs && invokeDirectiveHook(vnode, null, parentComponent, \"mounted\");\n        }, parentSuspense);\n      }\n    }\n    return el.nextSibling;\n  };\n  const hydrateChildren = (node, parentVNode, container, parentComponent, parentSuspense, slotScopeIds, optimized) => {\n    optimized = optimized || !!parentVNode.dynamicChildren;\n    const children = parentVNode.children;\n    const l = children.length;\n    let hasWarned2 = false;\n    for (let i = 0; i < l; i++) {\n      const vnode = optimized ? children[i] : children[i] = normalizeVNode(children[i]);\n      const isText = vnode.type === Text;\n      if (node) {\n        if (isText && !optimized) {\n          if (i + 1 < l && normalizeVNode(children[i + 1]).type === Text) {\n            insert(\n              createText(\n                node.data.slice(vnode.children.length)\n              ),\n              container,\n              nextSibling(node)\n            );\n            node.data = vnode.children;\n          }\n        }\n        node = hydrateNode(\n          node,\n          vnode,\n          parentComponent,\n          parentSuspense,\n          slotScopeIds,\n          optimized\n        );\n      } else if (isText && !vnode.children) {\n        insert(vnode.el = createText(\"\"), container);\n      } else {\n        if (!isMismatchAllowed(\n          container,\n          1\n          /* CHILDREN */\n        )) {\n          if (!hasWarned2) {\n            warn$1(\n              `Hydration children mismatch on`,\n              container,\n              `\nServer rendered element contains fewer child nodes than client vdom.`\n            );\n            hasWarned2 = true;\n          }\n          logMismatchError();\n        }\n        patch(\n          null,\n          vnode,\n          container,\n          null,\n          parentComponent,\n          parentSuspense,\n          getContainerType(container),\n          slotScopeIds\n        );\n      }\n    }\n    return node;\n  };\n  const hydrateFragment = (node, vnode, parentComponent, parentSuspense, slotScopeIds, optimized) => {\n    const { slotScopeIds: fragmentSlotScopeIds } = vnode;\n    if (fragmentSlotScopeIds) {\n      slotScopeIds = slotScopeIds ? slotScopeIds.concat(fragmentSlotScopeIds) : fragmentSlotScopeIds;\n    }\n    const container = parentNode(node);\n    const next = hydrateChildren(\n      nextSibling(node),\n      vnode,\n      container,\n      parentComponent,\n      parentSuspense,\n      slotScopeIds,\n      optimized\n    );\n    if (next && isComment(next) && next.data === \"]\") {\n      return nextSibling(vnode.anchor = next);\n    } else {\n      logMismatchError();\n      insert(vnode.anchor = createComment(`]`), container, next);\n      return next;\n    }\n  };\n  const handleMismatch = (node, vnode, parentComponent, parentSuspense, slotScopeIds, isFragment) => {\n    if (!isMismatchAllowed(\n      node.parentElement,\n      1\n      /* CHILDREN */\n    )) {\n      warn$1(\n        `Hydration node mismatch:\n- rendered on server:`,\n        node,\n        node.nodeType === 3 ? `(text)` : isComment(node) && node.data === \"[\" ? `(start of fragment)` : ``,\n        `\n- expected on client:`,\n        vnode.type\n      );\n      logMismatchError();\n    }\n    vnode.el = null;\n    if (isFragment) {\n      const end = locateClosingAnchor(node);\n      while (true) {\n        const next2 = nextSibling(node);\n        if (next2 && next2 !== end) {\n          remove2(next2);\n        } else {\n          break;\n        }\n      }\n    }\n    const next = nextSibling(node);\n    const container = parentNode(node);\n    remove2(node);\n    patch(\n      null,\n      vnode,\n      container,\n      next,\n      parentComponent,\n      parentSuspense,\n      getContainerType(container),\n      slotScopeIds\n    );\n    if (parentComponent) {\n      parentComponent.vnode.el = vnode.el;\n      updateHOCHostEl(parentComponent, vnode.el);\n    }\n    return next;\n  };\n  const locateClosingAnchor = (node, open = \"[\", close = \"]\") => {\n    let match = 0;\n    while (node) {\n      node = nextSibling(node);\n      if (node && isComment(node)) {\n        if (node.data === open) match++;\n        if (node.data === close) {\n          if (match === 0) {\n            return nextSibling(node);\n          } else {\n            match--;\n          }\n        }\n      }\n    }\n    return node;\n  };\n  const replaceNode = (newNode, oldNode, parentComponent) => {\n    const parentNode2 = oldNode.parentNode;\n    if (parentNode2) {\n      parentNode2.replaceChild(newNode, oldNode);\n    }\n    let parent = parentComponent;\n    while (parent) {\n      if (parent.vnode.el === oldNode) {\n        parent.vnode.el = parent.subTree.el = newNode;\n      }\n      parent = parent.parent;\n    }\n  };\n  const isTemplateNode = (node) => {\n    return node.nodeType === 1 && node.tagName === \"TEMPLATE\";\n  };\n  return [hydrate2, hydrateNode];\n}\nfunction propHasMismatch(el, key, clientValue, vnode, instance) {\n  let mismatchType;\n  let mismatchKey;\n  let actual;\n  let expected;\n  if (key === \"class\") {\n    actual = el.getAttribute(\"class\");\n    expected = normalizeClass(clientValue);\n    if (!isSetEqual(toClassSet(actual || \"\"), toClassSet(expected))) {\n      mismatchType = 2;\n      mismatchKey = `class`;\n    }\n  } else if (key === \"style\") {\n    actual = el.getAttribute(\"style\") || \"\";\n    expected = isString(clientValue) ? clientValue : stringifyStyle(normalizeStyle(clientValue));\n    const actualMap = toStyleMap(actual);\n    const expectedMap = toStyleMap(expected);\n    if (vnode.dirs) {\n      for (const { dir, value } of vnode.dirs) {\n        if (dir.name === \"show\" && !value) {\n          expectedMap.set(\"display\", \"none\");\n        }\n      }\n    }\n    if (instance) {\n      resolveCssVars(instance, vnode, expectedMap);\n    }\n    if (!isMapEqual(actualMap, expectedMap)) {\n      mismatchType = 3;\n      mismatchKey = \"style\";\n    }\n  } else if (el instanceof SVGElement && isKnownSvgAttr(key) || el instanceof HTMLElement && (isBooleanAttr(key) || isKnownHtmlAttr(key))) {\n    if (isBooleanAttr(key)) {\n      actual = el.hasAttribute(key);\n      expected = includeBooleanAttr(clientValue);\n    } else if (clientValue == null) {\n      actual = el.hasAttribute(key);\n      expected = false;\n    } else {\n      if (el.hasAttribute(key)) {\n        actual = el.getAttribute(key);\n      } else if (key === \"value\" && el.tagName === \"TEXTAREA\") {\n        actual = el.value;\n      } else {\n        actual = false;\n      }\n      expected = isRenderableAttrValue(clientValue) ? String(clientValue) : false;\n    }\n    if (actual !== expected) {\n      mismatchType = 4;\n      mismatchKey = key;\n    }\n  }\n  if (mismatchType != null && !isMismatchAllowed(el, mismatchType)) {\n    const format = (v) => v === false ? `(not rendered)` : `${mismatchKey}=\"${v}\"`;\n    const preSegment = `Hydration ${MismatchTypeString[mismatchType]} mismatch on`;\n    const postSegment = `\n  - rendered on server: ${format(actual)}\n  - expected on client: ${format(expected)}\n  Note: this mismatch is check-only. The DOM will not be rectified in production due to performance overhead.\n  You should fix the source of the mismatch.`;\n    {\n      warn$1(preSegment, el, postSegment);\n    }\n    return true;\n  }\n  return false;\n}\nfunction toClassSet(str) {\n  return new Set(str.trim().split(/\\s+/));\n}\nfunction isSetEqual(a, b) {\n  if (a.size !== b.size) {\n    return false;\n  }\n  for (const s of a) {\n    if (!b.has(s)) {\n      return false;\n    }\n  }\n  return true;\n}\nfunction toStyleMap(str) {\n  const styleMap = /* @__PURE__ */ new Map();\n  for (const item of str.split(\";\")) {\n    let [key, value] = item.split(\":\");\n    key = key.trim();\n    value = value && value.trim();\n    if (key && value) {\n      styleMap.set(key, value);\n    }\n  }\n  return styleMap;\n}\nfunction isMapEqual(a, b) {\n  if (a.size !== b.size) {\n    return false;\n  }\n  for (const [key, value] of a) {\n    if (value !== b.get(key)) {\n      return false;\n    }\n  }\n  return true;\n}\nfunction resolveCssVars(instance, vnode, expectedMap) {\n  const root = instance.subTree;\n  if (instance.getCssVars && (vnode === root || root && root.type === Fragment && root.children.includes(vnode))) {\n    const cssVars = instance.getCssVars();\n    for (const key in cssVars) {\n      expectedMap.set(\n        `--${getEscapedCssVarName(key, false)}`,\n        String(cssVars[key])\n      );\n    }\n  }\n  if (vnode === root && instance.parent) {\n    resolveCssVars(instance.parent, instance.vnode, expectedMap);\n  }\n}\nvar allowMismatchAttr = \"data-allow-mismatch\";\nvar MismatchTypeString = {\n  [\n    0\n    /* TEXT */\n  ]: \"text\",\n  [\n    1\n    /* CHILDREN */\n  ]: \"children\",\n  [\n    2\n    /* CLASS */\n  ]: \"class\",\n  [\n    3\n    /* STYLE */\n  ]: \"style\",\n  [\n    4\n    /* ATTRIBUTE */\n  ]: \"attribute\"\n};\nfunction isMismatchAllowed(el, allowedType) {\n  if (allowedType === 0 || allowedType === 1) {\n    while (el && !el.hasAttribute(allowMismatchAttr)) {\n      el = el.parentElement;\n    }\n  }\n  const allowedAttr = el && el.getAttribute(allowMismatchAttr);\n  if (allowedAttr == null) {\n    return false;\n  } else if (allowedAttr === \"\") {\n    return true;\n  } else {\n    const list = allowedAttr.split(\",\");\n    if (allowedType === 0 && list.includes(\"children\")) {\n      return true;\n    }\n    return allowedAttr.split(\",\").includes(MismatchTypeString[allowedType]);\n  }\n}\nvar requestIdleCallback = getGlobalThis().requestIdleCallback || ((cb) => setTimeout(cb, 1));\nvar cancelIdleCallback = getGlobalThis().cancelIdleCallback || ((id) => clearTimeout(id));\nvar hydrateOnIdle = (timeout = 1e4) => (hydrate2) => {\n  const id = requestIdleCallback(hydrate2, { timeout });\n  return () => cancelIdleCallback(id);\n};\nfunction elementIsVisibleInViewport(el) {\n  const { top, left, bottom, right } = el.getBoundingClientRect();\n  const { innerHeight, innerWidth } = window;\n  return (top > 0 && top < innerHeight || bottom > 0 && bottom < innerHeight) && (left > 0 && left < innerWidth || right > 0 && right < innerWidth);\n}\nvar hydrateOnVisible = (opts) => (hydrate2, forEach) => {\n  const ob = new IntersectionObserver((entries) => {\n    for (const e of entries) {\n      if (!e.isIntersecting) continue;\n      ob.disconnect();\n      hydrate2();\n      break;\n    }\n  }, opts);\n  forEach((el) => {\n    if (!(el instanceof Element)) return;\n    if (elementIsVisibleInViewport(el)) {\n      hydrate2();\n      ob.disconnect();\n      return false;\n    }\n    ob.observe(el);\n  });\n  return () => ob.disconnect();\n};\nvar hydrateOnMediaQuery = (query) => (hydrate2) => {\n  if (query) {\n    const mql = matchMedia(query);\n    if (mql.matches) {\n      hydrate2();\n    } else {\n      mql.addEventListener(\"change\", hydrate2, { once: true });\n      return () => mql.removeEventListener(\"change\", hydrate2);\n    }\n  }\n};\nvar hydrateOnInteraction = (interactions = []) => (hydrate2, forEach) => {\n  if (isString(interactions)) interactions = [interactions];\n  let hasHydrated = false;\n  const doHydrate = (e) => {\n    if (!hasHydrated) {\n      hasHydrated = true;\n      teardown();\n      hydrate2();\n      e.target.dispatchEvent(new e.constructor(e.type, e));\n    }\n  };\n  const teardown = () => {\n    forEach((el) => {\n      for (const i of interactions) {\n        el.removeEventListener(i, doHydrate);\n      }\n    });\n  };\n  forEach((el) => {\n    for (const i of interactions) {\n      el.addEventListener(i, doHydrate, { once: true });\n    }\n  });\n  return teardown;\n};\nfunction forEachElement(node, cb) {\n  if (isComment(node) && node.data === \"[\") {\n    let depth = 1;\n    let next = node.nextSibling;\n    while (next) {\n      if (next.nodeType === 1) {\n        const result = cb(next);\n        if (result === false) {\n          break;\n        }\n      } else if (isComment(next)) {\n        if (next.data === \"]\") {\n          if (--depth === 0) break;\n        } else if (next.data === \"[\") {\n          depth++;\n        }\n      }\n      next = next.nextSibling;\n    }\n  } else {\n    cb(node);\n  }\n}\nvar isAsyncWrapper = (i) => !!i.type.__asyncLoader;\nfunction defineAsyncComponent(source) {\n  if (isFunction(source)) {\n    source = { loader: source };\n  }\n  const {\n    loader,\n    loadingComponent,\n    errorComponent,\n    delay = 200,\n    hydrate: hydrateStrategy,\n    timeout,\n    // undefined = never times out\n    suspensible = true,\n    onError: userOnError\n  } = source;\n  let pendingRequest = null;\n  let resolvedComp;\n  let retries = 0;\n  const retry = () => {\n    retries++;\n    pendingRequest = null;\n    return load();\n  };\n  const load = () => {\n    let thisRequest;\n    return pendingRequest || (thisRequest = pendingRequest = loader().catch((err) => {\n      err = err instanceof Error ? err : new Error(String(err));\n      if (userOnError) {\n        return new Promise((resolve2, reject) => {\n          const userRetry = () => resolve2(retry());\n          const userFail = () => reject(err);\n          userOnError(err, userRetry, userFail, retries + 1);\n        });\n      } else {\n        throw err;\n      }\n    }).then((comp) => {\n      if (thisRequest !== pendingRequest && pendingRequest) {\n        return pendingRequest;\n      }\n      if (!comp) {\n        warn$1(\n          `Async component loader resolved to undefined. If you are using retry(), make sure to return its return value.`\n        );\n      }\n      if (comp && (comp.__esModule || comp[Symbol.toStringTag] === \"Module\")) {\n        comp = comp.default;\n      }\n      if (comp && !isObject(comp) && !isFunction(comp)) {\n        throw new Error(`Invalid async component load result: ${comp}`);\n      }\n      resolvedComp = comp;\n      return comp;\n    }));\n  };\n  return defineComponent({\n    name: \"AsyncComponentWrapper\",\n    __asyncLoader: load,\n    __asyncHydrate(el, instance, hydrate2) {\n      const doHydrate = hydrateStrategy ? () => {\n        const teardown = hydrateStrategy(\n          hydrate2,\n          (cb) => forEachElement(el, cb)\n        );\n        if (teardown) {\n          (instance.bum || (instance.bum = [])).push(teardown);\n        }\n      } : hydrate2;\n      if (resolvedComp) {\n        doHydrate();\n      } else {\n        load().then(() => !instance.isUnmounted && doHydrate());\n      }\n    },\n    get __asyncResolved() {\n      return resolvedComp;\n    },\n    setup() {\n      const instance = currentInstance;\n      markAsyncBoundary(instance);\n      if (resolvedComp) {\n        return () => createInnerComp(resolvedComp, instance);\n      }\n      const onError = (err) => {\n        pendingRequest = null;\n        handleError(\n          err,\n          instance,\n          13,\n          !errorComponent\n        );\n      };\n      if (suspensible && instance.suspense || isInSSRComponentSetup) {\n        return load().then((comp) => {\n          return () => createInnerComp(comp, instance);\n        }).catch((err) => {\n          onError(err);\n          return () => errorComponent ? createVNode(errorComponent, {\n            error: err\n          }) : null;\n        });\n      }\n      const loaded = ref(false);\n      const error = ref();\n      const delayed = ref(!!delay);\n      if (delay) {\n        setTimeout(() => {\n          delayed.value = false;\n        }, delay);\n      }\n      if (timeout != null) {\n        setTimeout(() => {\n          if (!loaded.value && !error.value) {\n            const err = new Error(\n              `Async component timed out after ${timeout}ms.`\n            );\n            onError(err);\n            error.value = err;\n          }\n        }, timeout);\n      }\n      load().then(() => {\n        loaded.value = true;\n        if (instance.parent && isKeepAlive(instance.parent.vnode)) {\n          instance.parent.update();\n        }\n      }).catch((err) => {\n        onError(err);\n        error.value = err;\n      });\n      return () => {\n        if (loaded.value && resolvedComp) {\n          return createInnerComp(resolvedComp, instance);\n        } else if (error.value && errorComponent) {\n          return createVNode(errorComponent, {\n            error: error.value\n          });\n        } else if (loadingComponent && !delayed.value) {\n          return createVNode(loadingComponent);\n        }\n      };\n    }\n  });\n}\nfunction createInnerComp(comp, parent) {\n  const { ref: ref2, props, children, ce } = parent.vnode;\n  const vnode = createVNode(comp, props, children);\n  vnode.ref = ref2;\n  vnode.ce = ce;\n  delete parent.vnode.ce;\n  return vnode;\n}\nvar isKeepAlive = (vnode) => vnode.type.__isKeepAlive;\nvar KeepAliveImpl = {\n  name: `KeepAlive`,\n  // Marker for special handling inside the renderer. We are not using a ===\n  // check directly on KeepAlive in the renderer, because importing it directly\n  // would prevent it from being tree-shaken.\n  __isKeepAlive: true,\n  props: {\n    include: [String, RegExp, Array],\n    exclude: [String, RegExp, Array],\n    max: [String, Number]\n  },\n  setup(props, { slots }) {\n    const instance = getCurrentInstance();\n    const sharedContext = instance.ctx;\n    if (!sharedContext.renderer) {\n      return () => {\n        const children = slots.default && slots.default();\n        return children && children.length === 1 ? children[0] : children;\n      };\n    }\n    const cache = /* @__PURE__ */ new Map();\n    const keys = /* @__PURE__ */ new Set();\n    let current = null;\n    if (true) {\n      instance.__v_cache = cache;\n    }\n    const parentSuspense = instance.suspense;\n    const {\n      renderer: {\n        p: patch,\n        m: move,\n        um: _unmount,\n        o: { createElement }\n      }\n    } = sharedContext;\n    const storageContainer = createElement(\"div\");\n    sharedContext.activate = (vnode, container, anchor, namespace, optimized) => {\n      const instance2 = vnode.component;\n      move(vnode, container, anchor, 0, parentSuspense);\n      patch(\n        instance2.vnode,\n        vnode,\n        container,\n        anchor,\n        instance2,\n        parentSuspense,\n        namespace,\n        vnode.slotScopeIds,\n        optimized\n      );\n      queuePostRenderEffect(() => {\n        instance2.isDeactivated = false;\n        if (instance2.a) {\n          invokeArrayFns(instance2.a);\n        }\n        const vnodeHook = vnode.props && vnode.props.onVnodeMounted;\n        if (vnodeHook) {\n          invokeVNodeHook(vnodeHook, instance2.parent, vnode);\n        }\n      }, parentSuspense);\n      if (true) {\n        devtoolsComponentAdded(instance2);\n      }\n    };\n    sharedContext.deactivate = (vnode) => {\n      const instance2 = vnode.component;\n      invalidateMount(instance2.m);\n      invalidateMount(instance2.a);\n      move(vnode, storageContainer, null, 1, parentSuspense);\n      queuePostRenderEffect(() => {\n        if (instance2.da) {\n          invokeArrayFns(instance2.da);\n        }\n        const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted;\n        if (vnodeHook) {\n          invokeVNodeHook(vnodeHook, instance2.parent, vnode);\n        }\n        instance2.isDeactivated = true;\n      }, parentSuspense);\n      if (true) {\n        devtoolsComponentAdded(instance2);\n      }\n    };\n    function unmount(vnode) {\n      resetShapeFlag(vnode);\n      _unmount(vnode, instance, parentSuspense, true);\n    }\n    function pruneCache(filter) {\n      cache.forEach((vnode, key) => {\n        const name = getComponentName(vnode.type);\n        if (name && !filter(name)) {\n          pruneCacheEntry(key);\n        }\n      });\n    }\n    function pruneCacheEntry(key) {\n      const cached = cache.get(key);\n      if (cached && (!current || !isSameVNodeType(cached, current))) {\n        unmount(cached);\n      } else if (current) {\n        resetShapeFlag(current);\n      }\n      cache.delete(key);\n      keys.delete(key);\n    }\n    watch2(\n      () => [props.include, props.exclude],\n      ([include, exclude]) => {\n        include && pruneCache((name) => matches(include, name));\n        exclude && pruneCache((name) => !matches(exclude, name));\n      },\n      // prune post-render after `current` has been updated\n      { flush: \"post\", deep: true }\n    );\n    let pendingCacheKey = null;\n    const cacheSubtree = () => {\n      if (pendingCacheKey != null) {\n        if (isSuspense(instance.subTree.type)) {\n          queuePostRenderEffect(() => {\n            cache.set(pendingCacheKey, getInnerChild(instance.subTree));\n          }, instance.subTree.suspense);\n        } else {\n          cache.set(pendingCacheKey, getInnerChild(instance.subTree));\n        }\n      }\n    };\n    onMounted(cacheSubtree);\n    onUpdated(cacheSubtree);\n    onBeforeUnmount(() => {\n      cache.forEach((cached) => {\n        const { subTree, suspense } = instance;\n        const vnode = getInnerChild(subTree);\n        if (cached.type === vnode.type && cached.key === vnode.key) {\n          resetShapeFlag(vnode);\n          const da = vnode.component.da;\n          da && queuePostRenderEffect(da, suspense);\n          return;\n        }\n        unmount(cached);\n      });\n    });\n    return () => {\n      pendingCacheKey = null;\n      if (!slots.default) {\n        return current = null;\n      }\n      const children = slots.default();\n      const rawVNode = children[0];\n      if (children.length > 1) {\n        if (true) {\n          warn$1(`KeepAlive should contain exactly one component child.`);\n        }\n        current = null;\n        return children;\n      } else if (!isVNode(rawVNode) || !(rawVNode.shapeFlag & 4) && !(rawVNode.shapeFlag & 128)) {\n        current = null;\n        return rawVNode;\n      }\n      let vnode = getInnerChild(rawVNode);\n      if (vnode.type === Comment) {\n        current = null;\n        return vnode;\n      }\n      const comp = vnode.type;\n      const name = getComponentName(\n        isAsyncWrapper(vnode) ? vnode.type.__asyncResolved || {} : comp\n      );\n      const { include, exclude, max } = props;\n      if (include && (!name || !matches(include, name)) || exclude && name && matches(exclude, name)) {\n        vnode.shapeFlag &= ~256;\n        current = vnode;\n        return rawVNode;\n      }\n      const key = vnode.key == null ? comp : vnode.key;\n      const cachedVNode = cache.get(key);\n      if (vnode.el) {\n        vnode = cloneVNode(vnode);\n        if (rawVNode.shapeFlag & 128) {\n          rawVNode.ssContent = vnode;\n        }\n      }\n      pendingCacheKey = key;\n      if (cachedVNode) {\n        vnode.el = cachedVNode.el;\n        vnode.component = cachedVNode.component;\n        if (vnode.transition) {\n          setTransitionHooks(vnode, vnode.transition);\n        }\n        vnode.shapeFlag |= 512;\n        keys.delete(key);\n        keys.add(key);\n      } else {\n        keys.add(key);\n        if (max && keys.size > parseInt(max, 10)) {\n          pruneCacheEntry(keys.values().next().value);\n        }\n      }\n      vnode.shapeFlag |= 256;\n      current = vnode;\n      return isSuspense(rawVNode.type) ? rawVNode : vnode;\n    };\n  }\n};\nvar KeepAlive = KeepAliveImpl;\nfunction matches(pattern, name) {\n  if (isArray(pattern)) {\n    return pattern.some((p2) => matches(p2, name));\n  } else if (isString(pattern)) {\n    return pattern.split(\",\").includes(name);\n  } else if (isRegExp(pattern)) {\n    pattern.lastIndex = 0;\n    return pattern.test(name);\n  }\n  return false;\n}\nfunction onActivated(hook, target) {\n  registerKeepAliveHook(hook, \"a\", target);\n}\nfunction onDeactivated(hook, target) {\n  registerKeepAliveHook(hook, \"da\", target);\n}\nfunction registerKeepAliveHook(hook, type, target = currentInstance) {\n  const wrappedHook = hook.__wdc || (hook.__wdc = () => {\n    let current = target;\n    while (current) {\n      if (current.isDeactivated) {\n        return;\n      }\n      current = current.parent;\n    }\n    return hook();\n  });\n  injectHook(type, wrappedHook, target);\n  if (target) {\n    let current = target.parent;\n    while (current && current.parent) {\n      if (isKeepAlive(current.parent.vnode)) {\n        injectToKeepAliveRoot(wrappedHook, type, target, current);\n      }\n      current = current.parent;\n    }\n  }\n}\nfunction injectToKeepAliveRoot(hook, type, target, keepAliveRoot) {\n  const injected = injectHook(\n    type,\n    hook,\n    keepAliveRoot,\n    true\n    /* prepend */\n  );\n  onUnmounted(() => {\n    remove(keepAliveRoot[type], injected);\n  }, target);\n}\nfunction resetShapeFlag(vnode) {\n  vnode.shapeFlag &= ~256;\n  vnode.shapeFlag &= ~512;\n}\nfunction getInnerChild(vnode) {\n  return vnode.shapeFlag & 128 ? vnode.ssContent : vnode;\n}\nfunction injectHook(type, hook, target = currentInstance, prepend = false) {\n  if (target) {\n    const hooks = target[type] || (target[type] = []);\n    const wrappedHook = hook.__weh || (hook.__weh = (...args) => {\n      pauseTracking();\n      const reset = setCurrentInstance(target);\n      const res = callWithAsyncErrorHandling(hook, target, type, args);\n      reset();\n      resetTracking();\n      return res;\n    });\n    if (prepend) {\n      hooks.unshift(wrappedHook);\n    } else {\n      hooks.push(wrappedHook);\n    }\n    return wrappedHook;\n  } else if (true) {\n    const apiName = toHandlerKey(ErrorTypeStrings$1[type].replace(/ hook$/, \"\"));\n    warn$1(\n      `${apiName} is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.`\n    );\n  }\n}\nvar createHook = (lifecycle) => (hook, target = currentInstance) => {\n  if (!isInSSRComponentSetup || lifecycle === \"sp\") {\n    injectHook(lifecycle, (...args) => hook(...args), target);\n  }\n};\nvar onBeforeMount = createHook(\"bm\");\nvar onMounted = createHook(\"m\");\nvar onBeforeUpdate = createHook(\n  \"bu\"\n);\nvar onUpdated = createHook(\"u\");\nvar onBeforeUnmount = createHook(\n  \"bum\"\n);\nvar onUnmounted = createHook(\"um\");\nvar onServerPrefetch = createHook(\n  \"sp\"\n);\nvar onRenderTriggered = createHook(\"rtg\");\nvar onRenderTracked = createHook(\"rtc\");\nfunction onErrorCaptured(hook, target = currentInstance) {\n  injectHook(\"ec\", hook, target);\n}\nvar COMPONENTS = \"components\";\nvar DIRECTIVES = \"directives\";\nfunction resolveComponent(name, maybeSelfReference) {\n  return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name;\n}\nvar NULL_DYNAMIC_COMPONENT = Symbol.for(\"v-ndc\");\nfunction resolveDynamicComponent(component) {\n  if (isString(component)) {\n    return resolveAsset(COMPONENTS, component, false) || component;\n  } else {\n    return component || NULL_DYNAMIC_COMPONENT;\n  }\n}\nfunction resolveDirective(name) {\n  return resolveAsset(DIRECTIVES, name);\n}\nfunction resolveAsset(type, name, warnMissing = true, maybeSelfReference = false) {\n  const instance = currentRenderingInstance || currentInstance;\n  if (instance) {\n    const Component = instance.type;\n    if (type === COMPONENTS) {\n      const selfName = getComponentName(\n        Component,\n        false\n      );\n      if (selfName && (selfName === name || selfName === camelize(name) || selfName === capitalize(camelize(name)))) {\n        return Component;\n      }\n    }\n    const res = (\n      // local registration\n      // check instance[type] first which is resolved for options API\n      resolve(instance[type] || Component[type], name) || // global registration\n      resolve(instance.appContext[type], name)\n    );\n    if (!res && maybeSelfReference) {\n      return Component;\n    }\n    if (warnMissing && !res) {\n      const extra = type === COMPONENTS ? `\nIf this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.` : ``;\n      warn$1(`Failed to resolve ${type.slice(0, -1)}: ${name}${extra}`);\n    }\n    return res;\n  } else if (true) {\n    warn$1(\n      `resolve${capitalize(type.slice(0, -1))} can only be used in render() or setup().`\n    );\n  }\n}\nfunction resolve(registry, name) {\n  return registry && (registry[name] || registry[camelize(name)] || registry[capitalize(camelize(name))]);\n}\nfunction renderList(source, renderItem, cache, index) {\n  let ret;\n  const cached = cache && cache[index];\n  const sourceIsArray = isArray(source);\n  if (sourceIsArray || isString(source)) {\n    const sourceIsReactiveArray = sourceIsArray && isReactive(source);\n    let needsWrap = false;\n    if (sourceIsReactiveArray) {\n      needsWrap = !isShallow(source);\n      source = shallowReadArray(source);\n    }\n    ret = new Array(source.length);\n    for (let i = 0, l = source.length; i < l; i++) {\n      ret[i] = renderItem(\n        needsWrap ? toReactive(source[i]) : source[i],\n        i,\n        void 0,\n        cached && cached[i]\n      );\n    }\n  } else if (typeof source === \"number\") {\n    if (!Number.isInteger(source)) {\n      warn$1(`The v-for range expect an integer value but got ${source}.`);\n    }\n    ret = new Array(source);\n    for (let i = 0; i < source; i++) {\n      ret[i] = renderItem(i + 1, i, void 0, cached && cached[i]);\n    }\n  } else if (isObject(source)) {\n    if (source[Symbol.iterator]) {\n      ret = Array.from(\n        source,\n        (item, i) => renderItem(item, i, void 0, cached && cached[i])\n      );\n    } else {\n      const keys = Object.keys(source);\n      ret = new Array(keys.length);\n      for (let i = 0, l = keys.length; i < l; i++) {\n        const key = keys[i];\n        ret[i] = renderItem(source[key], key, i, cached && cached[i]);\n      }\n    }\n  } else {\n    ret = [];\n  }\n  if (cache) {\n    cache[index] = ret;\n  }\n  return ret;\n}\nfunction createSlots(slots, dynamicSlots) {\n  for (let i = 0; i < dynamicSlots.length; i++) {\n    const slot = dynamicSlots[i];\n    if (isArray(slot)) {\n      for (let j = 0; j < slot.length; j++) {\n        slots[slot[j].name] = slot[j].fn;\n      }\n    } else if (slot) {\n      slots[slot.name] = slot.key ? (...args) => {\n        const res = slot.fn(...args);\n        if (res) res.key = slot.key;\n        return res;\n      } : slot.fn;\n    }\n  }\n  return slots;\n}\nfunction renderSlot(slots, name, props = {}, fallback, noSlotted) {\n  if (currentRenderingInstance.ce || currentRenderingInstance.parent && isAsyncWrapper(currentRenderingInstance.parent) && currentRenderingInstance.parent.ce) {\n    if (name !== \"default\") props.name = name;\n    return openBlock(), createBlock(\n      Fragment,\n      null,\n      [createVNode(\"slot\", props, fallback && fallback())],\n      64\n    );\n  }\n  let slot = slots[name];\n  if (slot && slot.length > 1) {\n    warn$1(\n      `SSR-optimized slot function detected in a non-SSR-optimized render function. You need to mark this component with $dynamic-slots in the parent template.`\n    );\n    slot = () => [];\n  }\n  if (slot && slot._c) {\n    slot._d = false;\n  }\n  openBlock();\n  const validSlotContent = slot && ensureValidVNode(slot(props));\n  const slotKey = props.key || // slot content array of a dynamic conditional slot may have a branch\n  // key attached in the `createSlots` helper, respect that\n  validSlotContent && validSlotContent.key;\n  const rendered = createBlock(\n    Fragment,\n    {\n      key: (slotKey && !isSymbol(slotKey) ? slotKey : `_${name}`) + // #7256 force differentiate fallback content from actual content\n      (!validSlotContent && fallback ? \"_fb\" : \"\")\n    },\n    validSlotContent || (fallback ? fallback() : []),\n    validSlotContent && slots._ === 1 ? 64 : -2\n  );\n  if (!noSlotted && rendered.scopeId) {\n    rendered.slotScopeIds = [rendered.scopeId + \"-s\"];\n  }\n  if (slot && slot._c) {\n    slot._d = true;\n  }\n  return rendered;\n}\nfunction ensureValidVNode(vnodes) {\n  return vnodes.some((child) => {\n    if (!isVNode(child)) return true;\n    if (child.type === Comment) return false;\n    if (child.type === Fragment && !ensureValidVNode(child.children))\n      return false;\n    return true;\n  }) ? vnodes : null;\n}\nfunction toHandlers(obj, preserveCaseIfNecessary) {\n  const ret = {};\n  if (!isObject(obj)) {\n    warn$1(`v-on with no argument expects an object value.`);\n    return ret;\n  }\n  for (const key in obj) {\n    ret[preserveCaseIfNecessary && /[A-Z]/.test(key) ? `on:${key}` : toHandlerKey(key)] = obj[key];\n  }\n  return ret;\n}\nvar getPublicInstance = (i) => {\n  if (!i) return null;\n  if (isStatefulComponent(i)) return getComponentPublicInstance(i);\n  return getPublicInstance(i.parent);\n};\nvar publicPropertiesMap = (\n  // Move PURE marker to new line to workaround compiler discarding it\n  // due to type annotation\n  extend(/* @__PURE__ */ Object.create(null), {\n    $: (i) => i,\n    $el: (i) => i.vnode.el,\n    $data: (i) => i.data,\n    $props: (i) => true ? shallowReadonly(i.props) : i.props,\n    $attrs: (i) => true ? shallowReadonly(i.attrs) : i.attrs,\n    $slots: (i) => true ? shallowReadonly(i.slots) : i.slots,\n    $refs: (i) => true ? shallowReadonly(i.refs) : i.refs,\n    $parent: (i) => getPublicInstance(i.parent),\n    $root: (i) => getPublicInstance(i.root),\n    $host: (i) => i.ce,\n    $emit: (i) => i.emit,\n    $options: (i) => __VUE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type,\n    $forceUpdate: (i) => i.f || (i.f = () => {\n      queueJob(i.update);\n    }),\n    $nextTick: (i) => i.n || (i.n = nextTick.bind(i.proxy)),\n    $watch: (i) => __VUE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP\n  })\n);\nvar isReservedPrefix = (key) => key === \"_\" || key === \"$\";\nvar hasSetupBinding = (state, key) => state !== EMPTY_OBJ && !state.__isScriptSetup && hasOwn(state, key);\nvar PublicInstanceProxyHandlers = {\n  get({ _: instance }, key) {\n    if (key === \"__v_skip\") {\n      return true;\n    }\n    const { ctx, setupState, data, props, accessCache, type, appContext } = instance;\n    if (key === \"__isVue\") {\n      return true;\n    }\n    let normalizedProps;\n    if (key[0] !== \"$\") {\n      const n = accessCache[key];\n      if (n !== void 0) {\n        switch (n) {\n          case 1:\n            return setupState[key];\n          case 2:\n            return data[key];\n          case 4:\n            return ctx[key];\n          case 3:\n            return props[key];\n        }\n      } else if (hasSetupBinding(setupState, key)) {\n        accessCache[key] = 1;\n        return setupState[key];\n      } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {\n        accessCache[key] = 2;\n        return data[key];\n      } else if (\n        // only cache other properties when instance has declared (thus stable)\n        // props\n        (normalizedProps = instance.propsOptions[0]) && hasOwn(normalizedProps, key)\n      ) {\n        accessCache[key] = 3;\n        return props[key];\n      } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {\n        accessCache[key] = 4;\n        return ctx[key];\n      } else if (!__VUE_OPTIONS_API__ || shouldCacheAccess) {\n        accessCache[key] = 0;\n      }\n    }\n    const publicGetter = publicPropertiesMap[key];\n    let cssModule, globalProperties;\n    if (publicGetter) {\n      if (key === \"$attrs\") {\n        track(instance.attrs, \"get\", \"\");\n        markAttrsAccessed();\n      } else if (key === \"$slots\") {\n        track(instance, \"get\", key);\n      }\n      return publicGetter(instance);\n    } else if (\n      // css module (injected by vue-loader)\n      (cssModule = type.__cssModules) && (cssModule = cssModule[key])\n    ) {\n      return cssModule;\n    } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {\n      accessCache[key] = 4;\n      return ctx[key];\n    } else if (\n      // global properties\n      globalProperties = appContext.config.globalProperties, hasOwn(globalProperties, key)\n    ) {\n      {\n        return globalProperties[key];\n      }\n    } else if (currentRenderingInstance && (!isString(key) || // #1091 avoid internal isRef/isVNode checks on component instance leading\n    // to infinite warning loop\n    key.indexOf(\"__v\") !== 0)) {\n      if (data !== EMPTY_OBJ && isReservedPrefix(key[0]) && hasOwn(data, key)) {\n        warn$1(\n          `Property ${JSON.stringify(\n            key\n          )} must be accessed via $data because it starts with a reserved character (\"$\" or \"_\") and is not proxied on the render context.`\n        );\n      } else if (instance === currentRenderingInstance) {\n        warn$1(\n          `Property ${JSON.stringify(key)} was accessed during render but is not defined on instance.`\n        );\n      }\n    }\n  },\n  set({ _: instance }, key, value) {\n    const { data, setupState, ctx } = instance;\n    if (hasSetupBinding(setupState, key)) {\n      setupState[key] = value;\n      return true;\n    } else if (setupState.__isScriptSetup && hasOwn(setupState, key)) {\n      warn$1(`Cannot mutate <script setup> binding \"${key}\" from Options API.`);\n      return false;\n    } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {\n      data[key] = value;\n      return true;\n    } else if (hasOwn(instance.props, key)) {\n      warn$1(`Attempting to mutate prop \"${key}\". Props are readonly.`);\n      return false;\n    }\n    if (key[0] === \"$\" && key.slice(1) in instance) {\n      warn$1(\n        `Attempting to mutate public property \"${key}\". Properties starting with $ are reserved and readonly.`\n      );\n      return false;\n    } else {\n      if (key in instance.appContext.config.globalProperties) {\n        Object.defineProperty(ctx, key, {\n          enumerable: true,\n          configurable: true,\n          value\n        });\n      } else {\n        ctx[key] = value;\n      }\n    }\n    return true;\n  },\n  has({\n    _: { data, setupState, accessCache, ctx, appContext, propsOptions }\n  }, key) {\n    let normalizedProps;\n    return !!accessCache[key] || data !== EMPTY_OBJ && hasOwn(data, key) || hasSetupBinding(setupState, key) || (normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key) || hasOwn(ctx, key) || hasOwn(publicPropertiesMap, key) || hasOwn(appContext.config.globalProperties, key);\n  },\n  defineProperty(target, key, descriptor) {\n    if (descriptor.get != null) {\n      target._.accessCache[key] = 0;\n    } else if (hasOwn(descriptor, \"value\")) {\n      this.set(target, key, descriptor.value, null);\n    }\n    return Reflect.defineProperty(target, key, descriptor);\n  }\n};\nif (true) {\n  PublicInstanceProxyHandlers.ownKeys = (target) => {\n    warn$1(\n      `Avoid app logic that relies on enumerating keys on a component instance. The keys will be empty in production mode to avoid performance overhead.`\n    );\n    return Reflect.ownKeys(target);\n  };\n}\nvar RuntimeCompiledPublicInstanceProxyHandlers = extend({}, PublicInstanceProxyHandlers, {\n  get(target, key) {\n    if (key === Symbol.unscopables) {\n      return;\n    }\n    return PublicInstanceProxyHandlers.get(target, key, target);\n  },\n  has(_, key) {\n    const has = key[0] !== \"_\" && !isGloballyAllowed(key);\n    if (!has && PublicInstanceProxyHandlers.has(_, key)) {\n      warn$1(\n        `Property ${JSON.stringify(\n          key\n        )} should not start with _ which is a reserved prefix for Vue internals.`\n      );\n    }\n    return has;\n  }\n});\nfunction createDevRenderContext(instance) {\n  const target = {};\n  Object.defineProperty(target, `_`, {\n    configurable: true,\n    enumerable: false,\n    get: () => instance\n  });\n  Object.keys(publicPropertiesMap).forEach((key) => {\n    Object.defineProperty(target, key, {\n      configurable: true,\n      enumerable: false,\n      get: () => publicPropertiesMap[key](instance),\n      // intercepted by the proxy so no need for implementation,\n      // but needed to prevent set errors\n      set: NOOP\n    });\n  });\n  return target;\n}\nfunction exposePropsOnRenderContext(instance) {\n  const {\n    ctx,\n    propsOptions: [propsOptions]\n  } = instance;\n  if (propsOptions) {\n    Object.keys(propsOptions).forEach((key) => {\n      Object.defineProperty(ctx, key, {\n        enumerable: true,\n        configurable: true,\n        get: () => instance.props[key],\n        set: NOOP\n      });\n    });\n  }\n}\nfunction exposeSetupStateOnRenderContext(instance) {\n  const { ctx, setupState } = instance;\n  Object.keys(toRaw(setupState)).forEach((key) => {\n    if (!setupState.__isScriptSetup) {\n      if (isReservedPrefix(key[0])) {\n        warn$1(\n          `setup() return property ${JSON.stringify(\n            key\n          )} should not start with \"$\" or \"_\" which are reserved prefixes for Vue internals.`\n        );\n        return;\n      }\n      Object.defineProperty(ctx, key, {\n        enumerable: true,\n        configurable: true,\n        get: () => setupState[key],\n        set: NOOP\n      });\n    }\n  });\n}\nvar warnRuntimeUsage = (method) => warn$1(\n  `${method}() is a compiler-hint helper that is only usable inside <script setup> of a single file component. Its arguments should be compiled away and passing it at runtime has no effect.`\n);\nfunction defineProps() {\n  if (true) {\n    warnRuntimeUsage(`defineProps`);\n  }\n  return null;\n}\nfunction defineEmits() {\n  if (true) {\n    warnRuntimeUsage(`defineEmits`);\n  }\n  return null;\n}\nfunction defineExpose(exposed) {\n  if (true) {\n    warnRuntimeUsage(`defineExpose`);\n  }\n}\nfunction defineOptions(options) {\n  if (true) {\n    warnRuntimeUsage(`defineOptions`);\n  }\n}\nfunction defineSlots() {\n  if (true) {\n    warnRuntimeUsage(`defineSlots`);\n  }\n  return null;\n}\nfunction defineModel() {\n  if (true) {\n    warnRuntimeUsage(\"defineModel\");\n  }\n}\nfunction withDefaults(props, defaults) {\n  if (true) {\n    warnRuntimeUsage(`withDefaults`);\n  }\n  return null;\n}\nfunction useSlots() {\n  return getContext().slots;\n}\nfunction useAttrs() {\n  return getContext().attrs;\n}\nfunction getContext() {\n  const i = getCurrentInstance();\n  if (!i) {\n    warn$1(`useContext() called without active instance.`);\n  }\n  return i.setupContext || (i.setupContext = createSetupContext(i));\n}\nfunction normalizePropsOrEmits(props) {\n  return isArray(props) ? props.reduce(\n    (normalized, p2) => (normalized[p2] = null, normalized),\n    {}\n  ) : props;\n}\nfunction mergeDefaults(raw, defaults) {\n  const props = normalizePropsOrEmits(raw);\n  for (const key in defaults) {\n    if (key.startsWith(\"__skip\")) continue;\n    let opt = props[key];\n    if (opt) {\n      if (isArray(opt) || isFunction(opt)) {\n        opt = props[key] = { type: opt, default: defaults[key] };\n      } else {\n        opt.default = defaults[key];\n      }\n    } else if (opt === null) {\n      opt = props[key] = { default: defaults[key] };\n    } else if (true) {\n      warn$1(`props default key \"${key}\" has no corresponding declaration.`);\n    }\n    if (opt && defaults[`__skip_${key}`]) {\n      opt.skipFactory = true;\n    }\n  }\n  return props;\n}\nfunction mergeModels(a, b) {\n  if (!a || !b) return a || b;\n  if (isArray(a) && isArray(b)) return a.concat(b);\n  return extend({}, normalizePropsOrEmits(a), normalizePropsOrEmits(b));\n}\nfunction createPropsRestProxy(props, excludedKeys) {\n  const ret = {};\n  for (const key in props) {\n    if (!excludedKeys.includes(key)) {\n      Object.defineProperty(ret, key, {\n        enumerable: true,\n        get: () => props[key]\n      });\n    }\n  }\n  return ret;\n}\nfunction withAsyncContext(getAwaitable) {\n  const ctx = getCurrentInstance();\n  if (!ctx) {\n    warn$1(\n      `withAsyncContext called without active current instance. This is likely a bug.`\n    );\n  }\n  let awaitable = getAwaitable();\n  unsetCurrentInstance();\n  if (isPromise(awaitable)) {\n    awaitable = awaitable.catch((e) => {\n      setCurrentInstance(ctx);\n      throw e;\n    });\n  }\n  return [awaitable, () => setCurrentInstance(ctx)];\n}\nfunction createDuplicateChecker() {\n  const cache = /* @__PURE__ */ Object.create(null);\n  return (type, key) => {\n    if (cache[key]) {\n      warn$1(`${type} property \"${key}\" is already defined in ${cache[key]}.`);\n    } else {\n      cache[key] = type;\n    }\n  };\n}\nvar shouldCacheAccess = true;\nfunction applyOptions(instance) {\n  const options = resolveMergedOptions(instance);\n  const publicThis = instance.proxy;\n  const ctx = instance.ctx;\n  shouldCacheAccess = false;\n  if (options.beforeCreate) {\n    callHook(options.beforeCreate, instance, \"bc\");\n  }\n  const {\n    // state\n    data: dataOptions,\n    computed: computedOptions,\n    methods,\n    watch: watchOptions,\n    provide: provideOptions,\n    inject: injectOptions,\n    // lifecycle\n    created,\n    beforeMount,\n    mounted,\n    beforeUpdate,\n    updated,\n    activated,\n    deactivated,\n    beforeDestroy,\n    beforeUnmount,\n    destroyed,\n    unmounted,\n    render: render2,\n    renderTracked,\n    renderTriggered,\n    errorCaptured,\n    serverPrefetch,\n    // public API\n    expose,\n    inheritAttrs,\n    // assets\n    components,\n    directives,\n    filters\n  } = options;\n  const checkDuplicateProperties = true ? createDuplicateChecker() : null;\n  if (true) {\n    const [propsOptions] = instance.propsOptions;\n    if (propsOptions) {\n      for (const key in propsOptions) {\n        checkDuplicateProperties(\"Props\", key);\n      }\n    }\n  }\n  if (injectOptions) {\n    resolveInjections(injectOptions, ctx, checkDuplicateProperties);\n  }\n  if (methods) {\n    for (const key in methods) {\n      const methodHandler = methods[key];\n      if (isFunction(methodHandler)) {\n        if (true) {\n          Object.defineProperty(ctx, key, {\n            value: methodHandler.bind(publicThis),\n            configurable: true,\n            enumerable: true,\n            writable: true\n          });\n        } else {\n          ctx[key] = methodHandler.bind(publicThis);\n        }\n        if (true) {\n          checkDuplicateProperties(\"Methods\", key);\n        }\n      } else if (true) {\n        warn$1(\n          `Method \"${key}\" has type \"${typeof methodHandler}\" in the component definition. Did you reference the function correctly?`\n        );\n      }\n    }\n  }\n  if (dataOptions) {\n    if (!isFunction(dataOptions)) {\n      warn$1(\n        `The data option must be a function. Plain object usage is no longer supported.`\n      );\n    }\n    const data = dataOptions.call(publicThis, publicThis);\n    if (isPromise(data)) {\n      warn$1(\n        `data() returned a Promise - note data() cannot be async; If you intend to perform data fetching before component renders, use async setup() + <Suspense>.`\n      );\n    }\n    if (!isObject(data)) {\n      warn$1(`data() should return an object.`);\n    } else {\n      instance.data = reactive(data);\n      if (true) {\n        for (const key in data) {\n          checkDuplicateProperties(\"Data\", key);\n          if (!isReservedPrefix(key[0])) {\n            Object.defineProperty(ctx, key, {\n              configurable: true,\n              enumerable: true,\n              get: () => data[key],\n              set: NOOP\n            });\n          }\n        }\n      }\n    }\n  }\n  shouldCacheAccess = true;\n  if (computedOptions) {\n    for (const key in computedOptions) {\n      const opt = computedOptions[key];\n      const get = isFunction(opt) ? opt.bind(publicThis, publicThis) : isFunction(opt.get) ? opt.get.bind(publicThis, publicThis) : NOOP;\n      if (get === NOOP) {\n        warn$1(`Computed property \"${key}\" has no getter.`);\n      }\n      const set = !isFunction(opt) && isFunction(opt.set) ? opt.set.bind(publicThis) : true ? () => {\n        warn$1(\n          `Write operation failed: computed property \"${key}\" is readonly.`\n        );\n      } : NOOP;\n      const c = computed2({\n        get,\n        set\n      });\n      Object.defineProperty(ctx, key, {\n        enumerable: true,\n        configurable: true,\n        get: () => c.value,\n        set: (v) => c.value = v\n      });\n      if (true) {\n        checkDuplicateProperties(\"Computed\", key);\n      }\n    }\n  }\n  if (watchOptions) {\n    for (const key in watchOptions) {\n      createWatcher(watchOptions[key], ctx, publicThis, key);\n    }\n  }\n  if (provideOptions) {\n    const provides = isFunction(provideOptions) ? provideOptions.call(publicThis) : provideOptions;\n    Reflect.ownKeys(provides).forEach((key) => {\n      provide(key, provides[key]);\n    });\n  }\n  if (created) {\n    callHook(created, instance, \"c\");\n  }\n  function registerLifecycleHook(register, hook) {\n    if (isArray(hook)) {\n      hook.forEach((_hook) => register(_hook.bind(publicThis)));\n    } else if (hook) {\n      register(hook.bind(publicThis));\n    }\n  }\n  registerLifecycleHook(onBeforeMount, beforeMount);\n  registerLifecycleHook(onMounted, mounted);\n  registerLifecycleHook(onBeforeUpdate, beforeUpdate);\n  registerLifecycleHook(onUpdated, updated);\n  registerLifecycleHook(onActivated, activated);\n  registerLifecycleHook(onDeactivated, deactivated);\n  registerLifecycleHook(onErrorCaptured, errorCaptured);\n  registerLifecycleHook(onRenderTracked, renderTracked);\n  registerLifecycleHook(onRenderTriggered, renderTriggered);\n  registerLifecycleHook(onBeforeUnmount, beforeUnmount);\n  registerLifecycleHook(onUnmounted, unmounted);\n  registerLifecycleHook(onServerPrefetch, serverPrefetch);\n  if (isArray(expose)) {\n    if (expose.length) {\n      const exposed = instance.exposed || (instance.exposed = {});\n      expose.forEach((key) => {\n        Object.defineProperty(exposed, key, {\n          get: () => publicThis[key],\n          set: (val) => publicThis[key] = val\n        });\n      });\n    } else if (!instance.exposed) {\n      instance.exposed = {};\n    }\n  }\n  if (render2 && instance.render === NOOP) {\n    instance.render = render2;\n  }\n  if (inheritAttrs != null) {\n    instance.inheritAttrs = inheritAttrs;\n  }\n  if (components) instance.components = components;\n  if (directives) instance.directives = directives;\n  if (serverPrefetch) {\n    markAsyncBoundary(instance);\n  }\n}\nfunction resolveInjections(injectOptions, ctx, checkDuplicateProperties = NOOP) {\n  if (isArray(injectOptions)) {\n    injectOptions = normalizeInject(injectOptions);\n  }\n  for (const key in injectOptions) {\n    const opt = injectOptions[key];\n    let injected;\n    if (isObject(opt)) {\n      if (\"default\" in opt) {\n        injected = inject(\n          opt.from || key,\n          opt.default,\n          true\n        );\n      } else {\n        injected = inject(opt.from || key);\n      }\n    } else {\n      injected = inject(opt);\n    }\n    if (isRef2(injected)) {\n      Object.defineProperty(ctx, key, {\n        enumerable: true,\n        configurable: true,\n        get: () => injected.value,\n        set: (v) => injected.value = v\n      });\n    } else {\n      ctx[key] = injected;\n    }\n    if (true) {\n      checkDuplicateProperties(\"Inject\", key);\n    }\n  }\n}\nfunction callHook(hook, instance, type) {\n  callWithAsyncErrorHandling(\n    isArray(hook) ? hook.map((h2) => h2.bind(instance.proxy)) : hook.bind(instance.proxy),\n    instance,\n    type\n  );\n}\nfunction createWatcher(raw, ctx, publicThis, key) {\n  let getter = key.includes(\".\") ? createPathGetter(publicThis, key) : () => publicThis[key];\n  if (isString(raw)) {\n    const handler = ctx[raw];\n    if (isFunction(handler)) {\n      {\n        watch2(getter, handler);\n      }\n    } else if (true) {\n      warn$1(`Invalid watch handler specified by key \"${raw}\"`, handler);\n    }\n  } else if (isFunction(raw)) {\n    {\n      watch2(getter, raw.bind(publicThis));\n    }\n  } else if (isObject(raw)) {\n    if (isArray(raw)) {\n      raw.forEach((r) => createWatcher(r, ctx, publicThis, key));\n    } else {\n      const handler = isFunction(raw.handler) ? raw.handler.bind(publicThis) : ctx[raw.handler];\n      if (isFunction(handler)) {\n        watch2(getter, handler, raw);\n      } else if (true) {\n        warn$1(`Invalid watch handler specified by key \"${raw.handler}\"`, handler);\n      }\n    }\n  } else if (true) {\n    warn$1(`Invalid watch option: \"${key}\"`, raw);\n  }\n}\nfunction resolveMergedOptions(instance) {\n  const base = instance.type;\n  const { mixins, extends: extendsOptions } = base;\n  const {\n    mixins: globalMixins,\n    optionsCache: cache,\n    config: { optionMergeStrategies }\n  } = instance.appContext;\n  const cached = cache.get(base);\n  let resolved;\n  if (cached) {\n    resolved = cached;\n  } else if (!globalMixins.length && !mixins && !extendsOptions) {\n    {\n      resolved = base;\n    }\n  } else {\n    resolved = {};\n    if (globalMixins.length) {\n      globalMixins.forEach(\n        (m) => mergeOptions(resolved, m, optionMergeStrategies, true)\n      );\n    }\n    mergeOptions(resolved, base, optionMergeStrategies);\n  }\n  if (isObject(base)) {\n    cache.set(base, resolved);\n  }\n  return resolved;\n}\nfunction mergeOptions(to, from, strats, asMixin = false) {\n  const { mixins, extends: extendsOptions } = from;\n  if (extendsOptions) {\n    mergeOptions(to, extendsOptions, strats, true);\n  }\n  if (mixins) {\n    mixins.forEach(\n      (m) => mergeOptions(to, m, strats, true)\n    );\n  }\n  for (const key in from) {\n    if (asMixin && key === \"expose\") {\n      warn$1(\n        `\"expose\" option is ignored when declared in mixins or extends. It should only be declared in the base component itself.`\n      );\n    } else {\n      const strat = internalOptionMergeStrats[key] || strats && strats[key];\n      to[key] = strat ? strat(to[key], from[key]) : from[key];\n    }\n  }\n  return to;\n}\nvar internalOptionMergeStrats = {\n  data: mergeDataFn,\n  props: mergeEmitsOrPropsOptions,\n  emits: mergeEmitsOrPropsOptions,\n  // objects\n  methods: mergeObjectOptions,\n  computed: mergeObjectOptions,\n  // lifecycle\n  beforeCreate: mergeAsArray,\n  created: mergeAsArray,\n  beforeMount: mergeAsArray,\n  mounted: mergeAsArray,\n  beforeUpdate: mergeAsArray,\n  updated: mergeAsArray,\n  beforeDestroy: mergeAsArray,\n  beforeUnmount: mergeAsArray,\n  destroyed: mergeAsArray,\n  unmounted: mergeAsArray,\n  activated: mergeAsArray,\n  deactivated: mergeAsArray,\n  errorCaptured: mergeAsArray,\n  serverPrefetch: mergeAsArray,\n  // assets\n  components: mergeObjectOptions,\n  directives: mergeObjectOptions,\n  // watch\n  watch: mergeWatchOptions,\n  // provide / inject\n  provide: mergeDataFn,\n  inject: mergeInject\n};\nfunction mergeDataFn(to, from) {\n  if (!from) {\n    return to;\n  }\n  if (!to) {\n    return from;\n  }\n  return function mergedDataFn() {\n    return extend(\n      isFunction(to) ? to.call(this, this) : to,\n      isFunction(from) ? from.call(this, this) : from\n    );\n  };\n}\nfunction mergeInject(to, from) {\n  return mergeObjectOptions(normalizeInject(to), normalizeInject(from));\n}\nfunction normalizeInject(raw) {\n  if (isArray(raw)) {\n    const res = {};\n    for (let i = 0; i < raw.length; i++) {\n      res[raw[i]] = raw[i];\n    }\n    return res;\n  }\n  return raw;\n}\nfunction mergeAsArray(to, from) {\n  return to ? [...new Set([].concat(to, from))] : from;\n}\nfunction mergeObjectOptions(to, from) {\n  return to ? extend(/* @__PURE__ */ Object.create(null), to, from) : from;\n}\nfunction mergeEmitsOrPropsOptions(to, from) {\n  if (to) {\n    if (isArray(to) && isArray(from)) {\n      return [.../* @__PURE__ */ new Set([...to, ...from])];\n    }\n    return extend(\n      /* @__PURE__ */ Object.create(null),\n      normalizePropsOrEmits(to),\n      normalizePropsOrEmits(from != null ? from : {})\n    );\n  } else {\n    return from;\n  }\n}\nfunction mergeWatchOptions(to, from) {\n  if (!to) return from;\n  if (!from) return to;\n  const merged = extend(/* @__PURE__ */ Object.create(null), to);\n  for (const key in from) {\n    merged[key] = mergeAsArray(to[key], from[key]);\n  }\n  return merged;\n}\nfunction createAppContext() {\n  return {\n    app: null,\n    config: {\n      isNativeTag: NO,\n      performance: false,\n      globalProperties: {},\n      optionMergeStrategies: {},\n      errorHandler: void 0,\n      warnHandler: void 0,\n      compilerOptions: {}\n    },\n    mixins: [],\n    components: {},\n    directives: {},\n    provides: /* @__PURE__ */ Object.create(null),\n    optionsCache: /* @__PURE__ */ new WeakMap(),\n    propsCache: /* @__PURE__ */ new WeakMap(),\n    emitsCache: /* @__PURE__ */ new WeakMap()\n  };\n}\nvar uid$1 = 0;\nfunction createAppAPI(render2, hydrate2) {\n  return function createApp2(rootComponent, rootProps = null) {\n    if (!isFunction(rootComponent)) {\n      rootComponent = extend({}, rootComponent);\n    }\n    if (rootProps != null && !isObject(rootProps)) {\n      warn$1(`root props passed to app.mount() must be an object.`);\n      rootProps = null;\n    }\n    const context = createAppContext();\n    const installedPlugins = /* @__PURE__ */ new WeakSet();\n    const pluginCleanupFns = [];\n    let isMounted = false;\n    const app = context.app = {\n      _uid: uid$1++,\n      _component: rootComponent,\n      _props: rootProps,\n      _container: null,\n      _context: context,\n      _instance: null,\n      version,\n      get config() {\n        return context.config;\n      },\n      set config(v) {\n        if (true) {\n          warn$1(\n            `app.config cannot be replaced. Modify individual options instead.`\n          );\n        }\n      },\n      use(plugin, ...options) {\n        if (installedPlugins.has(plugin)) {\n          warn$1(`Plugin has already been applied to target app.`);\n        } else if (plugin && isFunction(plugin.install)) {\n          installedPlugins.add(plugin);\n          plugin.install(app, ...options);\n        } else if (isFunction(plugin)) {\n          installedPlugins.add(plugin);\n          plugin(app, ...options);\n        } else if (true) {\n          warn$1(\n            `A plugin must either be a function or an object with an \"install\" function.`\n          );\n        }\n        return app;\n      },\n      mixin(mixin) {\n        if (__VUE_OPTIONS_API__) {\n          if (!context.mixins.includes(mixin)) {\n            context.mixins.push(mixin);\n          } else if (true) {\n            warn$1(\n              \"Mixin has already been applied to target app\" + (mixin.name ? `: ${mixin.name}` : \"\")\n            );\n          }\n        } else if (true) {\n          warn$1(\"Mixins are only available in builds supporting Options API\");\n        }\n        return app;\n      },\n      component(name, component) {\n        if (true) {\n          validateComponentName(name, context.config);\n        }\n        if (!component) {\n          return context.components[name];\n        }\n        if (context.components[name]) {\n          warn$1(`Component \"${name}\" has already been registered in target app.`);\n        }\n        context.components[name] = component;\n        return app;\n      },\n      directive(name, directive) {\n        if (true) {\n          validateDirectiveName(name);\n        }\n        if (!directive) {\n          return context.directives[name];\n        }\n        if (context.directives[name]) {\n          warn$1(`Directive \"${name}\" has already been registered in target app.`);\n        }\n        context.directives[name] = directive;\n        return app;\n      },\n      mount(rootContainer, isHydrate, namespace) {\n        if (!isMounted) {\n          if (rootContainer.__vue_app__) {\n            warn$1(\n              `There is already an app instance mounted on the host container.\n If you want to mount another app on the same host container, you need to unmount the previous app by calling \\`app.unmount()\\` first.`\n            );\n          }\n          const vnode = app._ceVNode || createVNode(rootComponent, rootProps);\n          vnode.appContext = context;\n          if (namespace === true) {\n            namespace = \"svg\";\n          } else if (namespace === false) {\n            namespace = void 0;\n          }\n          if (true) {\n            context.reload = () => {\n              render2(\n                cloneVNode(vnode),\n                rootContainer,\n                namespace\n              );\n            };\n          }\n          if (isHydrate && hydrate2) {\n            hydrate2(vnode, rootContainer);\n          } else {\n            render2(vnode, rootContainer, namespace);\n          }\n          isMounted = true;\n          app._container = rootContainer;\n          rootContainer.__vue_app__ = app;\n          if (true) {\n            app._instance = vnode.component;\n            devtoolsInitApp(app, version);\n          }\n          return getComponentPublicInstance(vnode.component);\n        } else if (true) {\n          warn$1(\n            `App has already been mounted.\nIf you want to remount the same app, move your app creation logic into a factory function and create fresh app instances for each mount - e.g. \\`const createMyApp = () => createApp(App)\\``\n          );\n        }\n      },\n      onUnmount(cleanupFn) {\n        if (typeof cleanupFn !== \"function\") {\n          warn$1(\n            `Expected function as first argument to app.onUnmount(), but got ${typeof cleanupFn}`\n          );\n        }\n        pluginCleanupFns.push(cleanupFn);\n      },\n      unmount() {\n        if (isMounted) {\n          callWithAsyncErrorHandling(\n            pluginCleanupFns,\n            app._instance,\n            16\n          );\n          render2(null, app._container);\n          if (true) {\n            app._instance = null;\n            devtoolsUnmountApp(app);\n          }\n          delete app._container.__vue_app__;\n        } else if (true) {\n          warn$1(`Cannot unmount an app that is not mounted.`);\n        }\n      },\n      provide(key, value) {\n        if (key in context.provides) {\n          warn$1(\n            `App already provides property with key \"${String(key)}\". It will be overwritten with the new value.`\n          );\n        }\n        context.provides[key] = value;\n        return app;\n      },\n      runWithContext(fn) {\n        const lastApp = currentApp;\n        currentApp = app;\n        try {\n          return fn();\n        } finally {\n          currentApp = lastApp;\n        }\n      }\n    };\n    return app;\n  };\n}\nvar currentApp = null;\nfunction provide(key, value) {\n  if (!currentInstance) {\n    if (true) {\n      warn$1(`provide() can only be used inside setup().`);\n    }\n  } else {\n    let provides = currentInstance.provides;\n    const parentProvides = currentInstance.parent && currentInstance.parent.provides;\n    if (parentProvides === provides) {\n      provides = currentInstance.provides = Object.create(parentProvides);\n    }\n    provides[key] = value;\n  }\n}\nfunction inject(key, defaultValue, treatDefaultAsFactory = false) {\n  const instance = currentInstance || currentRenderingInstance;\n  if (instance || currentApp) {\n    const provides = currentApp ? currentApp._context.provides : instance ? instance.parent == null ? instance.vnode.appContext && instance.vnode.appContext.provides : instance.parent.provides : void 0;\n    if (provides && key in provides) {\n      return provides[key];\n    } else if (arguments.length > 1) {\n      return treatDefaultAsFactory && isFunction(defaultValue) ? defaultValue.call(instance && instance.proxy) : defaultValue;\n    } else if (true) {\n      warn$1(`injection \"${String(key)}\" not found.`);\n    }\n  } else if (true) {\n    warn$1(`inject() can only be used inside setup() or functional components.`);\n  }\n}\nfunction hasInjectionContext() {\n  return !!(currentInstance || currentRenderingInstance || currentApp);\n}\nvar internalObjectProto = {};\nvar createInternalObject = () => Object.create(internalObjectProto);\nvar isInternalObject = (obj) => Object.getPrototypeOf(obj) === internalObjectProto;\nfunction initProps(instance, rawProps, isStateful, isSSR = false) {\n  const props = {};\n  const attrs = createInternalObject();\n  instance.propsDefaults = /* @__PURE__ */ Object.create(null);\n  setFullProps(instance, rawProps, props, attrs);\n  for (const key in instance.propsOptions[0]) {\n    if (!(key in props)) {\n      props[key] = void 0;\n    }\n  }\n  if (true) {\n    validateProps(rawProps || {}, props, instance);\n  }\n  if (isStateful) {\n    instance.props = isSSR ? props : shallowReactive(props);\n  } else {\n    if (!instance.type.props) {\n      instance.props = attrs;\n    } else {\n      instance.props = props;\n    }\n  }\n  instance.attrs = attrs;\n}\nfunction isInHmrContext(instance) {\n  while (instance) {\n    if (instance.type.__hmrId) return true;\n    instance = instance.parent;\n  }\n}\nfunction updateProps(instance, rawProps, rawPrevProps, optimized) {\n  const {\n    props,\n    attrs,\n    vnode: { patchFlag }\n  } = instance;\n  const rawCurrentProps = toRaw(props);\n  const [options] = instance.propsOptions;\n  let hasAttrsChanged = false;\n  if (\n    // always force full diff in dev\n    // - #1942 if hmr is enabled with sfc component\n    // - vite#872 non-sfc component used by sfc component\n    !isInHmrContext(instance) && (optimized || patchFlag > 0) && !(patchFlag & 16)\n  ) {\n    if (patchFlag & 8) {\n      const propsToUpdate = instance.vnode.dynamicProps;\n      for (let i = 0; i < propsToUpdate.length; i++) {\n        let key = propsToUpdate[i];\n        if (isEmitListener(instance.emitsOptions, key)) {\n          continue;\n        }\n        const value = rawProps[key];\n        if (options) {\n          if (hasOwn(attrs, key)) {\n            if (value !== attrs[key]) {\n              attrs[key] = value;\n              hasAttrsChanged = true;\n            }\n          } else {\n            const camelizedKey = camelize(key);\n            props[camelizedKey] = resolvePropValue(\n              options,\n              rawCurrentProps,\n              camelizedKey,\n              value,\n              instance,\n              false\n            );\n          }\n        } else {\n          if (value !== attrs[key]) {\n            attrs[key] = value;\n            hasAttrsChanged = true;\n          }\n        }\n      }\n    }\n  } else {\n    if (setFullProps(instance, rawProps, props, attrs)) {\n      hasAttrsChanged = true;\n    }\n    let kebabKey;\n    for (const key in rawCurrentProps) {\n      if (!rawProps || // for camelCase\n      !hasOwn(rawProps, key) && // it's possible the original props was passed in as kebab-case\n      // and converted to camelCase (#955)\n      ((kebabKey = hyphenate(key)) === key || !hasOwn(rawProps, kebabKey))) {\n        if (options) {\n          if (rawPrevProps && // for camelCase\n          (rawPrevProps[key] !== void 0 || // for kebab-case\n          rawPrevProps[kebabKey] !== void 0)) {\n            props[key] = resolvePropValue(\n              options,\n              rawCurrentProps,\n              key,\n              void 0,\n              instance,\n              true\n            );\n          }\n        } else {\n          delete props[key];\n        }\n      }\n    }\n    if (attrs !== rawCurrentProps) {\n      for (const key in attrs) {\n        if (!rawProps || !hasOwn(rawProps, key) && true) {\n          delete attrs[key];\n          hasAttrsChanged = true;\n        }\n      }\n    }\n  }\n  if (hasAttrsChanged) {\n    trigger(instance.attrs, \"set\", \"\");\n  }\n  if (true) {\n    validateProps(rawProps || {}, props, instance);\n  }\n}\nfunction setFullProps(instance, rawProps, props, attrs) {\n  const [options, needCastKeys] = instance.propsOptions;\n  let hasAttrsChanged = false;\n  let rawCastValues;\n  if (rawProps) {\n    for (let key in rawProps) {\n      if (isReservedProp(key)) {\n        continue;\n      }\n      const value = rawProps[key];\n      let camelKey;\n      if (options && hasOwn(options, camelKey = camelize(key))) {\n        if (!needCastKeys || !needCastKeys.includes(camelKey)) {\n          props[camelKey] = value;\n        } else {\n          (rawCastValues || (rawCastValues = {}))[camelKey] = value;\n        }\n      } else if (!isEmitListener(instance.emitsOptions, key)) {\n        if (!(key in attrs) || value !== attrs[key]) {\n          attrs[key] = value;\n          hasAttrsChanged = true;\n        }\n      }\n    }\n  }\n  if (needCastKeys) {\n    const rawCurrentProps = toRaw(props);\n    const castValues = rawCastValues || EMPTY_OBJ;\n    for (let i = 0; i < needCastKeys.length; i++) {\n      const key = needCastKeys[i];\n      props[key] = resolvePropValue(\n        options,\n        rawCurrentProps,\n        key,\n        castValues[key],\n        instance,\n        !hasOwn(castValues, key)\n      );\n    }\n  }\n  return hasAttrsChanged;\n}\nfunction resolvePropValue(options, props, key, value, instance, isAbsent) {\n  const opt = options[key];\n  if (opt != null) {\n    const hasDefault = hasOwn(opt, \"default\");\n    if (hasDefault && value === void 0) {\n      const defaultValue = opt.default;\n      if (opt.type !== Function && !opt.skipFactory && isFunction(defaultValue)) {\n        const { propsDefaults } = instance;\n        if (key in propsDefaults) {\n          value = propsDefaults[key];\n        } else {\n          const reset = setCurrentInstance(instance);\n          value = propsDefaults[key] = defaultValue.call(\n            null,\n            props\n          );\n          reset();\n        }\n      } else {\n        value = defaultValue;\n      }\n      if (instance.ce) {\n        instance.ce._setProp(key, value);\n      }\n    }\n    if (opt[\n      0\n      /* shouldCast */\n    ]) {\n      if (isAbsent && !hasDefault) {\n        value = false;\n      } else if (opt[\n        1\n        /* shouldCastTrue */\n      ] && (value === \"\" || value === hyphenate(key))) {\n        value = true;\n      }\n    }\n  }\n  return value;\n}\nvar mixinPropsCache = /* @__PURE__ */ new WeakMap();\nfunction normalizePropsOptions(comp, appContext, asMixin = false) {\n  const cache = __VUE_OPTIONS_API__ && asMixin ? mixinPropsCache : appContext.propsCache;\n  const cached = cache.get(comp);\n  if (cached) {\n    return cached;\n  }\n  const raw = comp.props;\n  const normalized = {};\n  const needCastKeys = [];\n  let hasExtends = false;\n  if (__VUE_OPTIONS_API__ && !isFunction(comp)) {\n    const extendProps = (raw2) => {\n      hasExtends = true;\n      const [props, keys] = normalizePropsOptions(raw2, appContext, true);\n      extend(normalized, props);\n      if (keys) needCastKeys.push(...keys);\n    };\n    if (!asMixin && appContext.mixins.length) {\n      appContext.mixins.forEach(extendProps);\n    }\n    if (comp.extends) {\n      extendProps(comp.extends);\n    }\n    if (comp.mixins) {\n      comp.mixins.forEach(extendProps);\n    }\n  }\n  if (!raw && !hasExtends) {\n    if (isObject(comp)) {\n      cache.set(comp, EMPTY_ARR);\n    }\n    return EMPTY_ARR;\n  }\n  if (isArray(raw)) {\n    for (let i = 0; i < raw.length; i++) {\n      if (!isString(raw[i])) {\n        warn$1(`props must be strings when using array syntax.`, raw[i]);\n      }\n      const normalizedKey = camelize(raw[i]);\n      if (validatePropName(normalizedKey)) {\n        normalized[normalizedKey] = EMPTY_OBJ;\n      }\n    }\n  } else if (raw) {\n    if (!isObject(raw)) {\n      warn$1(`invalid props options`, raw);\n    }\n    for (const key in raw) {\n      const normalizedKey = camelize(key);\n      if (validatePropName(normalizedKey)) {\n        const opt = raw[key];\n        const prop = normalized[normalizedKey] = isArray(opt) || isFunction(opt) ? { type: opt } : extend({}, opt);\n        const propType = prop.type;\n        let shouldCast = false;\n        let shouldCastTrue = true;\n        if (isArray(propType)) {\n          for (let index = 0; index < propType.length; ++index) {\n            const type = propType[index];\n            const typeName = isFunction(type) && type.name;\n            if (typeName === \"Boolean\") {\n              shouldCast = true;\n              break;\n            } else if (typeName === \"String\") {\n              shouldCastTrue = false;\n            }\n          }\n        } else {\n          shouldCast = isFunction(propType) && propType.name === \"Boolean\";\n        }\n        prop[\n          0\n          /* shouldCast */\n        ] = shouldCast;\n        prop[\n          1\n          /* shouldCastTrue */\n        ] = shouldCastTrue;\n        if (shouldCast || hasOwn(prop, \"default\")) {\n          needCastKeys.push(normalizedKey);\n        }\n      }\n    }\n  }\n  const res = [normalized, needCastKeys];\n  if (isObject(comp)) {\n    cache.set(comp, res);\n  }\n  return res;\n}\nfunction validatePropName(key) {\n  if (key[0] !== \"$\" && !isReservedProp(key)) {\n    return true;\n  } else if (true) {\n    warn$1(`Invalid prop name: \"${key}\" is a reserved property.`);\n  }\n  return false;\n}\nfunction getType(ctor) {\n  if (ctor === null) {\n    return \"null\";\n  }\n  if (typeof ctor === \"function\") {\n    return ctor.name || \"\";\n  } else if (typeof ctor === \"object\") {\n    const name = ctor.constructor && ctor.constructor.name;\n    return name || \"\";\n  }\n  return \"\";\n}\nfunction validateProps(rawProps, props, instance) {\n  const resolvedValues = toRaw(props);\n  const options = instance.propsOptions[0];\n  const camelizePropsKey = Object.keys(rawProps).map((key) => camelize(key));\n  for (const key in options) {\n    let opt = options[key];\n    if (opt == null) continue;\n    validateProp(\n      key,\n      resolvedValues[key],\n      opt,\n      true ? shallowReadonly(resolvedValues) : resolvedValues,\n      !camelizePropsKey.includes(key)\n    );\n  }\n}\nfunction validateProp(name, value, prop, props, isAbsent) {\n  const { type, required, validator, skipCheck } = prop;\n  if (required && isAbsent) {\n    warn$1('Missing required prop: \"' + name + '\"');\n    return;\n  }\n  if (value == null && !required) {\n    return;\n  }\n  if (type != null && type !== true && !skipCheck) {\n    let isValid = false;\n    const types = isArray(type) ? type : [type];\n    const expectedTypes = [];\n    for (let i = 0; i < types.length && !isValid; i++) {\n      const { valid, expectedType } = assertType(value, types[i]);\n      expectedTypes.push(expectedType || \"\");\n      isValid = valid;\n    }\n    if (!isValid) {\n      warn$1(getInvalidTypeMessage(name, value, expectedTypes));\n      return;\n    }\n  }\n  if (validator && !validator(value, props)) {\n    warn$1('Invalid prop: custom validator check failed for prop \"' + name + '\".');\n  }\n}\nvar isSimpleType = makeMap(\n  \"String,Number,Boolean,Function,Symbol,BigInt\"\n);\nfunction assertType(value, type) {\n  let valid;\n  const expectedType = getType(type);\n  if (expectedType === \"null\") {\n    valid = value === null;\n  } else if (isSimpleType(expectedType)) {\n    const t = typeof value;\n    valid = t === expectedType.toLowerCase();\n    if (!valid && t === \"object\") {\n      valid = value instanceof type;\n    }\n  } else if (expectedType === \"Object\") {\n    valid = isObject(value);\n  } else if (expectedType === \"Array\") {\n    valid = isArray(value);\n  } else {\n    valid = value instanceof type;\n  }\n  return {\n    valid,\n    expectedType\n  };\n}\nfunction getInvalidTypeMessage(name, value, expectedTypes) {\n  if (expectedTypes.length === 0) {\n    return `Prop type [] for prop \"${name}\" won't match anything. Did you mean to use type Array instead?`;\n  }\n  let message = `Invalid prop: type check failed for prop \"${name}\". Expected ${expectedTypes.map(capitalize).join(\" | \")}`;\n  const expectedType = expectedTypes[0];\n  const receivedType = toRawType(value);\n  const expectedValue = styleValue(value, expectedType);\n  const receivedValue = styleValue(value, receivedType);\n  if (expectedTypes.length === 1 && isExplicable(expectedType) && !isBoolean(expectedType, receivedType)) {\n    message += ` with value ${expectedValue}`;\n  }\n  message += `, got ${receivedType} `;\n  if (isExplicable(receivedType)) {\n    message += `with value ${receivedValue}.`;\n  }\n  return message;\n}\nfunction styleValue(value, type) {\n  if (type === \"String\") {\n    return `\"${value}\"`;\n  } else if (type === \"Number\") {\n    return `${Number(value)}`;\n  } else {\n    return `${value}`;\n  }\n}\nfunction isExplicable(type) {\n  const explicitTypes = [\"string\", \"number\", \"boolean\"];\n  return explicitTypes.some((elem) => type.toLowerCase() === elem);\n}\nfunction isBoolean(...args) {\n  return args.some((elem) => elem.toLowerCase() === \"boolean\");\n}\nvar isInternalKey = (key) => key[0] === \"_\" || key === \"$stable\";\nvar normalizeSlotValue = (value) => isArray(value) ? value.map(normalizeVNode) : [normalizeVNode(value)];\nvar normalizeSlot = (key, rawSlot, ctx) => {\n  if (rawSlot._n) {\n    return rawSlot;\n  }\n  const normalized = withCtx((...args) => {\n    if (currentInstance && (!ctx || ctx.root === currentInstance.root)) {\n      warn$1(\n        `Slot \"${key}\" invoked outside of the render function: this will not track dependencies used in the slot. Invoke the slot function inside the render function instead.`\n      );\n    }\n    return normalizeSlotValue(rawSlot(...args));\n  }, ctx);\n  normalized._c = false;\n  return normalized;\n};\nvar normalizeObjectSlots = (rawSlots, slots, instance) => {\n  const ctx = rawSlots._ctx;\n  for (const key in rawSlots) {\n    if (isInternalKey(key)) continue;\n    const value = rawSlots[key];\n    if (isFunction(value)) {\n      slots[key] = normalizeSlot(key, value, ctx);\n    } else if (value != null) {\n      if (true) {\n        warn$1(\n          `Non-function value encountered for slot \"${key}\". Prefer function slots for better performance.`\n        );\n      }\n      const normalized = normalizeSlotValue(value);\n      slots[key] = () => normalized;\n    }\n  }\n};\nvar normalizeVNodeSlots = (instance, children) => {\n  if (!isKeepAlive(instance.vnode) && true) {\n    warn$1(\n      `Non-function value encountered for default slot. Prefer function slots for better performance.`\n    );\n  }\n  const normalized = normalizeSlotValue(children);\n  instance.slots.default = () => normalized;\n};\nvar assignSlots = (slots, children, optimized) => {\n  for (const key in children) {\n    if (optimized || key !== \"_\") {\n      slots[key] = children[key];\n    }\n  }\n};\nvar initSlots = (instance, children, optimized) => {\n  const slots = instance.slots = createInternalObject();\n  if (instance.vnode.shapeFlag & 32) {\n    const type = children._;\n    if (type) {\n      assignSlots(slots, children, optimized);\n      if (optimized) {\n        def(slots, \"_\", type, true);\n      }\n    } else {\n      normalizeObjectSlots(children, slots);\n    }\n  } else if (children) {\n    normalizeVNodeSlots(instance, children);\n  }\n};\nvar updateSlots = (instance, children, optimized) => {\n  const { vnode, slots } = instance;\n  let needDeletionCheck = true;\n  let deletionComparisonTarget = EMPTY_OBJ;\n  if (vnode.shapeFlag & 32) {\n    const type = children._;\n    if (type) {\n      if (isHmrUpdating) {\n        assignSlots(slots, children, optimized);\n        trigger(instance, \"set\", \"$slots\");\n      } else if (optimized && type === 1) {\n        needDeletionCheck = false;\n      } else {\n        assignSlots(slots, children, optimized);\n      }\n    } else {\n      needDeletionCheck = !children.$stable;\n      normalizeObjectSlots(children, slots);\n    }\n    deletionComparisonTarget = children;\n  } else if (children) {\n    normalizeVNodeSlots(instance, children);\n    deletionComparisonTarget = { default: 1 };\n  }\n  if (needDeletionCheck) {\n    for (const key in slots) {\n      if (!isInternalKey(key) && deletionComparisonTarget[key] == null) {\n        delete slots[key];\n      }\n    }\n  }\n};\nvar supported;\nvar perf;\nfunction startMeasure(instance, type) {\n  if (instance.appContext.config.performance && isSupported()) {\n    perf.mark(`vue-${type}-${instance.uid}`);\n  }\n  if (true) {\n    devtoolsPerfStart(instance, type, isSupported() ? perf.now() : Date.now());\n  }\n}\nfunction endMeasure(instance, type) {\n  if (instance.appContext.config.performance && isSupported()) {\n    const startTag = `vue-${type}-${instance.uid}`;\n    const endTag = startTag + `:end`;\n    perf.mark(endTag);\n    perf.measure(\n      `<${formatComponentName(instance, instance.type)}> ${type}`,\n      startTag,\n      endTag\n    );\n    perf.clearMarks(startTag);\n    perf.clearMarks(endTag);\n  }\n  if (true) {\n    devtoolsPerfEnd(instance, type, isSupported() ? perf.now() : Date.now());\n  }\n}\nfunction isSupported() {\n  if (supported !== void 0) {\n    return supported;\n  }\n  if (typeof window !== \"undefined\" && window.performance) {\n    supported = true;\n    perf = window.performance;\n  } else {\n    supported = false;\n  }\n  return supported;\n}\nfunction initFeatureFlags() {\n  const needWarn = [];\n  if (typeof __VUE_OPTIONS_API__ !== \"boolean\") {\n    needWarn.push(`__VUE_OPTIONS_API__`);\n    getGlobalThis().__VUE_OPTIONS_API__ = true;\n  }\n  if (typeof __VUE_PROD_DEVTOOLS__ !== \"boolean\") {\n    needWarn.push(`__VUE_PROD_DEVTOOLS__`);\n    getGlobalThis().__VUE_PROD_DEVTOOLS__ = false;\n  }\n  if (typeof __VUE_PROD_HYDRATION_MISMATCH_DETAILS__ !== \"boolean\") {\n    needWarn.push(`__VUE_PROD_HYDRATION_MISMATCH_DETAILS__`);\n    getGlobalThis().__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ = false;\n  }\n  if (needWarn.length) {\n    const multi = needWarn.length > 1;\n    console.warn(\n      `Feature flag${multi ? `s` : ``} ${needWarn.join(\", \")} ${multi ? `are` : `is`} not explicitly defined. You are running the esm-bundler build of Vue, which expects these compile-time feature flags to be globally injected via the bundler config in order to get better tree-shaking in the production bundle.\n\nFor more details, see https://link.vuejs.org/feature-flags.`\n    );\n  }\n}\nvar queuePostRenderEffect = queueEffectWithSuspense;\nfunction createRenderer(options) {\n  return baseCreateRenderer(options);\n}\nfunction createHydrationRenderer(options) {\n  return baseCreateRenderer(options, createHydrationFunctions);\n}\nfunction baseCreateRenderer(options, createHydrationFns) {\n  {\n    initFeatureFlags();\n  }\n  const target = getGlobalThis();\n  target.__VUE__ = true;\n  if (true) {\n    setDevtoolsHook$1(target.__VUE_DEVTOOLS_GLOBAL_HOOK__, target);\n  }\n  const {\n    insert: hostInsert,\n    remove: hostRemove,\n    patchProp: hostPatchProp,\n    createElement: hostCreateElement,\n    createText: hostCreateText,\n    createComment: hostCreateComment,\n    setText: hostSetText,\n    setElementText: hostSetElementText,\n    parentNode: hostParentNode,\n    nextSibling: hostNextSibling,\n    setScopeId: hostSetScopeId = NOOP,\n    insertStaticContent: hostInsertStaticContent\n  } = options;\n  const patch = (n1, n2, container, anchor = null, parentComponent = null, parentSuspense = null, namespace = void 0, slotScopeIds = null, optimized = isHmrUpdating ? false : !!n2.dynamicChildren) => {\n    if (n1 === n2) {\n      return;\n    }\n    if (n1 && !isSameVNodeType(n1, n2)) {\n      anchor = getNextHostNode(n1);\n      unmount(n1, parentComponent, parentSuspense, true);\n      n1 = null;\n    }\n    if (n2.patchFlag === -2) {\n      optimized = false;\n      n2.dynamicChildren = null;\n    }\n    const { type, ref: ref2, shapeFlag } = n2;\n    switch (type) {\n      case Text:\n        processText(n1, n2, container, anchor);\n        break;\n      case Comment:\n        processCommentNode(n1, n2, container, anchor);\n        break;\n      case Static:\n        if (n1 == null) {\n          mountStaticNode(n2, container, anchor, namespace);\n        } else if (true) {\n          patchStaticNode(n1, n2, container, namespace);\n        }\n        break;\n      case Fragment:\n        processFragment(\n          n1,\n          n2,\n          container,\n          anchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        break;\n      default:\n        if (shapeFlag & 1) {\n          processElement(\n            n1,\n            n2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        } else if (shapeFlag & 6) {\n          processComponent(\n            n1,\n            n2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        } else if (shapeFlag & 64) {\n          type.process(\n            n1,\n            n2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized,\n            internals\n          );\n        } else if (shapeFlag & 128) {\n          type.process(\n            n1,\n            n2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized,\n            internals\n          );\n        } else if (true) {\n          warn$1(\"Invalid VNode type:\", type, `(${typeof type})`);\n        }\n    }\n    if (ref2 != null && parentComponent) {\n      setRef(ref2, n1 && n1.ref, parentSuspense, n2 || n1, !n2);\n    }\n  };\n  const processText = (n1, n2, container, anchor) => {\n    if (n1 == null) {\n      hostInsert(\n        n2.el = hostCreateText(n2.children),\n        container,\n        anchor\n      );\n    } else {\n      const el = n2.el = n1.el;\n      if (n2.children !== n1.children) {\n        hostSetText(el, n2.children);\n      }\n    }\n  };\n  const processCommentNode = (n1, n2, container, anchor) => {\n    if (n1 == null) {\n      hostInsert(\n        n2.el = hostCreateComment(n2.children || \"\"),\n        container,\n        anchor\n      );\n    } else {\n      n2.el = n1.el;\n    }\n  };\n  const mountStaticNode = (n2, container, anchor, namespace) => {\n    [n2.el, n2.anchor] = hostInsertStaticContent(\n      n2.children,\n      container,\n      anchor,\n      namespace,\n      n2.el,\n      n2.anchor\n    );\n  };\n  const patchStaticNode = (n1, n2, container, namespace) => {\n    if (n2.children !== n1.children) {\n      const anchor = hostNextSibling(n1.anchor);\n      removeStaticNode(n1);\n      [n2.el, n2.anchor] = hostInsertStaticContent(\n        n2.children,\n        container,\n        anchor,\n        namespace\n      );\n    } else {\n      n2.el = n1.el;\n      n2.anchor = n1.anchor;\n    }\n  };\n  const moveStaticNode = ({ el, anchor }, container, nextSibling) => {\n    let next;\n    while (el && el !== anchor) {\n      next = hostNextSibling(el);\n      hostInsert(el, container, nextSibling);\n      el = next;\n    }\n    hostInsert(anchor, container, nextSibling);\n  };\n  const removeStaticNode = ({ el, anchor }) => {\n    let next;\n    while (el && el !== anchor) {\n      next = hostNextSibling(el);\n      hostRemove(el);\n      el = next;\n    }\n    hostRemove(anchor);\n  };\n  const processElement = (n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    if (n2.type === \"svg\") {\n      namespace = \"svg\";\n    } else if (n2.type === \"math\") {\n      namespace = \"mathml\";\n    }\n    if (n1 == null) {\n      mountElement(\n        n2,\n        container,\n        anchor,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n    } else {\n      patchElement(\n        n1,\n        n2,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n    }\n  };\n  const mountElement = (vnode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    let el;\n    let vnodeHook;\n    const { props, shapeFlag, transition, dirs } = vnode;\n    el = vnode.el = hostCreateElement(\n      vnode.type,\n      namespace,\n      props && props.is,\n      props\n    );\n    if (shapeFlag & 8) {\n      hostSetElementText(el, vnode.children);\n    } else if (shapeFlag & 16) {\n      mountChildren(\n        vnode.children,\n        el,\n        null,\n        parentComponent,\n        parentSuspense,\n        resolveChildrenNamespace(vnode, namespace),\n        slotScopeIds,\n        optimized\n      );\n    }\n    if (dirs) {\n      invokeDirectiveHook(vnode, null, parentComponent, \"created\");\n    }\n    setScopeId(el, vnode, vnode.scopeId, slotScopeIds, parentComponent);\n    if (props) {\n      for (const key in props) {\n        if (key !== \"value\" && !isReservedProp(key)) {\n          hostPatchProp(el, key, null, props[key], namespace, parentComponent);\n        }\n      }\n      if (\"value\" in props) {\n        hostPatchProp(el, \"value\", null, props.value, namespace);\n      }\n      if (vnodeHook = props.onVnodeBeforeMount) {\n        invokeVNodeHook(vnodeHook, parentComponent, vnode);\n      }\n    }\n    if (true) {\n      def(el, \"__vnode\", vnode, true);\n      def(el, \"__vueParentComponent\", parentComponent, true);\n    }\n    if (dirs) {\n      invokeDirectiveHook(vnode, null, parentComponent, \"beforeMount\");\n    }\n    const needCallTransitionHooks = needTransition(parentSuspense, transition);\n    if (needCallTransitionHooks) {\n      transition.beforeEnter(el);\n    }\n    hostInsert(el, container, anchor);\n    if ((vnodeHook = props && props.onVnodeMounted) || needCallTransitionHooks || dirs) {\n      queuePostRenderEffect(() => {\n        vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);\n        needCallTransitionHooks && transition.enter(el);\n        dirs && invokeDirectiveHook(vnode, null, parentComponent, \"mounted\");\n      }, parentSuspense);\n    }\n  };\n  const setScopeId = (el, vnode, scopeId, slotScopeIds, parentComponent) => {\n    if (scopeId) {\n      hostSetScopeId(el, scopeId);\n    }\n    if (slotScopeIds) {\n      for (let i = 0; i < slotScopeIds.length; i++) {\n        hostSetScopeId(el, slotScopeIds[i]);\n      }\n    }\n    if (parentComponent) {\n      let subTree = parentComponent.subTree;\n      if (subTree.patchFlag > 0 && subTree.patchFlag & 2048) {\n        subTree = filterSingleRoot(subTree.children) || subTree;\n      }\n      if (vnode === subTree || isSuspense(subTree.type) && (subTree.ssContent === vnode || subTree.ssFallback === vnode)) {\n        const parentVNode = parentComponent.vnode;\n        setScopeId(\n          el,\n          parentVNode,\n          parentVNode.scopeId,\n          parentVNode.slotScopeIds,\n          parentComponent.parent\n        );\n      }\n    }\n  };\n  const mountChildren = (children, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, start = 0) => {\n    for (let i = start; i < children.length; i++) {\n      const child = children[i] = optimized ? cloneIfMounted(children[i]) : normalizeVNode(children[i]);\n      patch(\n        null,\n        child,\n        container,\n        anchor,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n    }\n  };\n  const patchElement = (n1, n2, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    const el = n2.el = n1.el;\n    if (true) {\n      el.__vnode = n2;\n    }\n    let { patchFlag, dynamicChildren, dirs } = n2;\n    patchFlag |= n1.patchFlag & 16;\n    const oldProps = n1.props || EMPTY_OBJ;\n    const newProps = n2.props || EMPTY_OBJ;\n    let vnodeHook;\n    parentComponent && toggleRecurse(parentComponent, false);\n    if (vnodeHook = newProps.onVnodeBeforeUpdate) {\n      invokeVNodeHook(vnodeHook, parentComponent, n2, n1);\n    }\n    if (dirs) {\n      invokeDirectiveHook(n2, n1, parentComponent, \"beforeUpdate\");\n    }\n    parentComponent && toggleRecurse(parentComponent, true);\n    if (isHmrUpdating) {\n      patchFlag = 0;\n      optimized = false;\n      dynamicChildren = null;\n    }\n    if (oldProps.innerHTML && newProps.innerHTML == null || oldProps.textContent && newProps.textContent == null) {\n      hostSetElementText(el, \"\");\n    }\n    if (dynamicChildren) {\n      patchBlockChildren(\n        n1.dynamicChildren,\n        dynamicChildren,\n        el,\n        parentComponent,\n        parentSuspense,\n        resolveChildrenNamespace(n2, namespace),\n        slotScopeIds\n      );\n      if (true) {\n        traverseStaticChildren(n1, n2);\n      }\n    } else if (!optimized) {\n      patchChildren(\n        n1,\n        n2,\n        el,\n        null,\n        parentComponent,\n        parentSuspense,\n        resolveChildrenNamespace(n2, namespace),\n        slotScopeIds,\n        false\n      );\n    }\n    if (patchFlag > 0) {\n      if (patchFlag & 16) {\n        patchProps(el, oldProps, newProps, parentComponent, namespace);\n      } else {\n        if (patchFlag & 2) {\n          if (oldProps.class !== newProps.class) {\n            hostPatchProp(el, \"class\", null, newProps.class, namespace);\n          }\n        }\n        if (patchFlag & 4) {\n          hostPatchProp(el, \"style\", oldProps.style, newProps.style, namespace);\n        }\n        if (patchFlag & 8) {\n          const propsToUpdate = n2.dynamicProps;\n          for (let i = 0; i < propsToUpdate.length; i++) {\n            const key = propsToUpdate[i];\n            const prev = oldProps[key];\n            const next = newProps[key];\n            if (next !== prev || key === \"value\") {\n              hostPatchProp(el, key, prev, next, namespace, parentComponent);\n            }\n          }\n        }\n      }\n      if (patchFlag & 1) {\n        if (n1.children !== n2.children) {\n          hostSetElementText(el, n2.children);\n        }\n      }\n    } else if (!optimized && dynamicChildren == null) {\n      patchProps(el, oldProps, newProps, parentComponent, namespace);\n    }\n    if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {\n      queuePostRenderEffect(() => {\n        vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1);\n        dirs && invokeDirectiveHook(n2, n1, parentComponent, \"updated\");\n      }, parentSuspense);\n    }\n  };\n  const patchBlockChildren = (oldChildren, newChildren, fallbackContainer, parentComponent, parentSuspense, namespace, slotScopeIds) => {\n    for (let i = 0; i < newChildren.length; i++) {\n      const oldVNode = oldChildren[i];\n      const newVNode = newChildren[i];\n      const container = (\n        // oldVNode may be an errored async setup() component inside Suspense\n        // which will not have a mounted element\n        oldVNode.el && // - In the case of a Fragment, we need to provide the actual parent\n        // of the Fragment itself so it can move its children.\n        (oldVNode.type === Fragment || // - In the case of different nodes, there is going to be a replacement\n        // which also requires the correct parent container\n        !isSameVNodeType(oldVNode, newVNode) || // - In the case of a component, it could contain anything.\n        oldVNode.shapeFlag & (6 | 64)) ? hostParentNode(oldVNode.el) : (\n          // In other cases, the parent container is not actually used so we\n          // just pass the block element here to avoid a DOM parentNode call.\n          fallbackContainer\n        )\n      );\n      patch(\n        oldVNode,\n        newVNode,\n        container,\n        null,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        true\n      );\n    }\n  };\n  const patchProps = (el, oldProps, newProps, parentComponent, namespace) => {\n    if (oldProps !== newProps) {\n      if (oldProps !== EMPTY_OBJ) {\n        for (const key in oldProps) {\n          if (!isReservedProp(key) && !(key in newProps)) {\n            hostPatchProp(\n              el,\n              key,\n              oldProps[key],\n              null,\n              namespace,\n              parentComponent\n            );\n          }\n        }\n      }\n      for (const key in newProps) {\n        if (isReservedProp(key)) continue;\n        const next = newProps[key];\n        const prev = oldProps[key];\n        if (next !== prev && key !== \"value\") {\n          hostPatchProp(el, key, prev, next, namespace, parentComponent);\n        }\n      }\n      if (\"value\" in newProps) {\n        hostPatchProp(el, \"value\", oldProps.value, newProps.value, namespace);\n      }\n    }\n  };\n  const processFragment = (n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    const fragmentStartAnchor = n2.el = n1 ? n1.el : hostCreateText(\"\");\n    const fragmentEndAnchor = n2.anchor = n1 ? n1.anchor : hostCreateText(\"\");\n    let { patchFlag, dynamicChildren, slotScopeIds: fragmentSlotScopeIds } = n2;\n    if (\n      // #5523 dev root fragment may inherit directives\n      isHmrUpdating || patchFlag & 2048\n    ) {\n      patchFlag = 0;\n      optimized = false;\n      dynamicChildren = null;\n    }\n    if (fragmentSlotScopeIds) {\n      slotScopeIds = slotScopeIds ? slotScopeIds.concat(fragmentSlotScopeIds) : fragmentSlotScopeIds;\n    }\n    if (n1 == null) {\n      hostInsert(fragmentStartAnchor, container, anchor);\n      hostInsert(fragmentEndAnchor, container, anchor);\n      mountChildren(\n        // #10007\n        // such fragment like `<></>` will be compiled into\n        // a fragment which doesn't have a children.\n        // In this case fallback to an empty array\n        n2.children || [],\n        container,\n        fragmentEndAnchor,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n    } else {\n      if (patchFlag > 0 && patchFlag & 64 && dynamicChildren && // #2715 the previous fragment could've been a BAILed one as a result\n      // of renderSlot() with no valid children\n      n1.dynamicChildren) {\n        patchBlockChildren(\n          n1.dynamicChildren,\n          dynamicChildren,\n          container,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds\n        );\n        if (true) {\n          traverseStaticChildren(n1, n2);\n        } else if (\n          // #2080 if the stable fragment has a key, it's a <template v-for> that may\n          //  get moved around. Make sure all root level vnodes inherit el.\n          // #2134 or if it's a component root, it may also get moved around\n          // as the component is being moved.\n          n2.key != null || parentComponent && n2 === parentComponent.subTree\n        ) {\n          traverseStaticChildren(\n            n1,\n            n2,\n            true\n            /* shallow */\n          );\n        }\n      } else {\n        patchChildren(\n          n1,\n          n2,\n          container,\n          fragmentEndAnchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n      }\n    }\n  };\n  const processComponent = (n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    n2.slotScopeIds = slotScopeIds;\n    if (n1 == null) {\n      if (n2.shapeFlag & 512) {\n        parentComponent.ctx.activate(\n          n2,\n          container,\n          anchor,\n          namespace,\n          optimized\n        );\n      } else {\n        mountComponent(\n          n2,\n          container,\n          anchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          optimized\n        );\n      }\n    } else {\n      updateComponent(n1, n2, optimized);\n    }\n  };\n  const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, namespace, optimized) => {\n    const instance = initialVNode.component = createComponentInstance(\n      initialVNode,\n      parentComponent,\n      parentSuspense\n    );\n    if (instance.type.__hmrId) {\n      registerHMR(instance);\n    }\n    if (true) {\n      pushWarningContext(initialVNode);\n      startMeasure(instance, `mount`);\n    }\n    if (isKeepAlive(initialVNode)) {\n      instance.ctx.renderer = internals;\n    }\n    {\n      if (true) {\n        startMeasure(instance, `init`);\n      }\n      setupComponent(instance, false, optimized);\n      if (true) {\n        endMeasure(instance, `init`);\n      }\n    }\n    if (instance.asyncDep) {\n      if (isHmrUpdating) initialVNode.el = null;\n      parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect, optimized);\n      if (!initialVNode.el) {\n        const placeholder = instance.subTree = createVNode(Comment);\n        processCommentNode(null, placeholder, container, anchor);\n      }\n    } else {\n      setupRenderEffect(\n        instance,\n        initialVNode,\n        container,\n        anchor,\n        parentSuspense,\n        namespace,\n        optimized\n      );\n    }\n    if (true) {\n      popWarningContext();\n      endMeasure(instance, `mount`);\n    }\n  };\n  const updateComponent = (n1, n2, optimized) => {\n    const instance = n2.component = n1.component;\n    if (shouldUpdateComponent(n1, n2, optimized)) {\n      if (instance.asyncDep && !instance.asyncResolved) {\n        if (true) {\n          pushWarningContext(n2);\n        }\n        updateComponentPreRender(instance, n2, optimized);\n        if (true) {\n          popWarningContext();\n        }\n        return;\n      } else {\n        instance.next = n2;\n        instance.update();\n      }\n    } else {\n      n2.el = n1.el;\n      instance.vnode = n2;\n    }\n  };\n  const setupRenderEffect = (instance, initialVNode, container, anchor, parentSuspense, namespace, optimized) => {\n    const componentUpdateFn = () => {\n      if (!instance.isMounted) {\n        let vnodeHook;\n        const { el, props } = initialVNode;\n        const { bm, m, parent, root, type } = instance;\n        const isAsyncWrapperVNode = isAsyncWrapper(initialVNode);\n        toggleRecurse(instance, false);\n        if (bm) {\n          invokeArrayFns(bm);\n        }\n        if (!isAsyncWrapperVNode && (vnodeHook = props && props.onVnodeBeforeMount)) {\n          invokeVNodeHook(vnodeHook, parent, initialVNode);\n        }\n        toggleRecurse(instance, true);\n        if (el && hydrateNode) {\n          const hydrateSubTree = () => {\n            if (true) {\n              startMeasure(instance, `render`);\n            }\n            instance.subTree = renderComponentRoot(instance);\n            if (true) {\n              endMeasure(instance, `render`);\n            }\n            if (true) {\n              startMeasure(instance, `hydrate`);\n            }\n            hydrateNode(\n              el,\n              instance.subTree,\n              instance,\n              parentSuspense,\n              null\n            );\n            if (true) {\n              endMeasure(instance, `hydrate`);\n            }\n          };\n          if (isAsyncWrapperVNode && type.__asyncHydrate) {\n            type.__asyncHydrate(\n              el,\n              instance,\n              hydrateSubTree\n            );\n          } else {\n            hydrateSubTree();\n          }\n        } else {\n          if (root.ce) {\n            root.ce._injectChildStyle(type);\n          }\n          if (true) {\n            startMeasure(instance, `render`);\n          }\n          const subTree = instance.subTree = renderComponentRoot(instance);\n          if (true) {\n            endMeasure(instance, `render`);\n          }\n          if (true) {\n            startMeasure(instance, `patch`);\n          }\n          patch(\n            null,\n            subTree,\n            container,\n            anchor,\n            instance,\n            parentSuspense,\n            namespace\n          );\n          if (true) {\n            endMeasure(instance, `patch`);\n          }\n          initialVNode.el = subTree.el;\n        }\n        if (m) {\n          queuePostRenderEffect(m, parentSuspense);\n        }\n        if (!isAsyncWrapperVNode && (vnodeHook = props && props.onVnodeMounted)) {\n          const scopedInitialVNode = initialVNode;\n          queuePostRenderEffect(\n            () => invokeVNodeHook(vnodeHook, parent, scopedInitialVNode),\n            parentSuspense\n          );\n        }\n        if (initialVNode.shapeFlag & 256 || parent && isAsyncWrapper(parent.vnode) && parent.vnode.shapeFlag & 256) {\n          instance.a && queuePostRenderEffect(instance.a, parentSuspense);\n        }\n        instance.isMounted = true;\n        if (true) {\n          devtoolsComponentAdded(instance);\n        }\n        initialVNode = container = anchor = null;\n      } else {\n        let { next, bu, u, parent, vnode } = instance;\n        {\n          const nonHydratedAsyncRoot = locateNonHydratedAsyncRoot(instance);\n          if (nonHydratedAsyncRoot) {\n            if (next) {\n              next.el = vnode.el;\n              updateComponentPreRender(instance, next, optimized);\n            }\n            nonHydratedAsyncRoot.asyncDep.then(() => {\n              if (!instance.isUnmounted) {\n                componentUpdateFn();\n              }\n            });\n            return;\n          }\n        }\n        let originNext = next;\n        let vnodeHook;\n        if (true) {\n          pushWarningContext(next || instance.vnode);\n        }\n        toggleRecurse(instance, false);\n        if (next) {\n          next.el = vnode.el;\n          updateComponentPreRender(instance, next, optimized);\n        } else {\n          next = vnode;\n        }\n        if (bu) {\n          invokeArrayFns(bu);\n        }\n        if (vnodeHook = next.props && next.props.onVnodeBeforeUpdate) {\n          invokeVNodeHook(vnodeHook, parent, next, vnode);\n        }\n        toggleRecurse(instance, true);\n        if (true) {\n          startMeasure(instance, `render`);\n        }\n        const nextTree = renderComponentRoot(instance);\n        if (true) {\n          endMeasure(instance, `render`);\n        }\n        const prevTree = instance.subTree;\n        instance.subTree = nextTree;\n        if (true) {\n          startMeasure(instance, `patch`);\n        }\n        patch(\n          prevTree,\n          nextTree,\n          // parent may have changed if it's in a teleport\n          hostParentNode(prevTree.el),\n          // anchor may have changed if it's in a fragment\n          getNextHostNode(prevTree),\n          instance,\n          parentSuspense,\n          namespace\n        );\n        if (true) {\n          endMeasure(instance, `patch`);\n        }\n        next.el = nextTree.el;\n        if (originNext === null) {\n          updateHOCHostEl(instance, nextTree.el);\n        }\n        if (u) {\n          queuePostRenderEffect(u, parentSuspense);\n        }\n        if (vnodeHook = next.props && next.props.onVnodeUpdated) {\n          queuePostRenderEffect(\n            () => invokeVNodeHook(vnodeHook, parent, next, vnode),\n            parentSuspense\n          );\n        }\n        if (true) {\n          devtoolsComponentUpdated(instance);\n        }\n        if (true) {\n          popWarningContext();\n        }\n      }\n    };\n    instance.scope.on();\n    const effect2 = instance.effect = new ReactiveEffect(componentUpdateFn);\n    instance.scope.off();\n    const update = instance.update = effect2.run.bind(effect2);\n    const job = instance.job = effect2.runIfDirty.bind(effect2);\n    job.i = instance;\n    job.id = instance.uid;\n    effect2.scheduler = () => queueJob(job);\n    toggleRecurse(instance, true);\n    if (true) {\n      effect2.onTrack = instance.rtc ? (e) => invokeArrayFns(instance.rtc, e) : void 0;\n      effect2.onTrigger = instance.rtg ? (e) => invokeArrayFns(instance.rtg, e) : void 0;\n    }\n    update();\n  };\n  const updateComponentPreRender = (instance, nextVNode, optimized) => {\n    nextVNode.component = instance;\n    const prevProps = instance.vnode.props;\n    instance.vnode = nextVNode;\n    instance.next = null;\n    updateProps(instance, nextVNode.props, prevProps, optimized);\n    updateSlots(instance, nextVNode.children, optimized);\n    pauseTracking();\n    flushPreFlushCbs(instance);\n    resetTracking();\n  };\n  const patchChildren = (n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized = false) => {\n    const c1 = n1 && n1.children;\n    const prevShapeFlag = n1 ? n1.shapeFlag : 0;\n    const c2 = n2.children;\n    const { patchFlag, shapeFlag } = n2;\n    if (patchFlag > 0) {\n      if (patchFlag & 128) {\n        patchKeyedChildren(\n          c1,\n          c2,\n          container,\n          anchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        return;\n      } else if (patchFlag & 256) {\n        patchUnkeyedChildren(\n          c1,\n          c2,\n          container,\n          anchor,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        return;\n      }\n    }\n    if (shapeFlag & 8) {\n      if (prevShapeFlag & 16) {\n        unmountChildren(c1, parentComponent, parentSuspense);\n      }\n      if (c2 !== c1) {\n        hostSetElementText(container, c2);\n      }\n    } else {\n      if (prevShapeFlag & 16) {\n        if (shapeFlag & 16) {\n          patchKeyedChildren(\n            c1,\n            c2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        } else {\n          unmountChildren(c1, parentComponent, parentSuspense, true);\n        }\n      } else {\n        if (prevShapeFlag & 8) {\n          hostSetElementText(container, \"\");\n        }\n        if (shapeFlag & 16) {\n          mountChildren(\n            c2,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        }\n      }\n    }\n  };\n  const patchUnkeyedChildren = (c1, c2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    c1 = c1 || EMPTY_ARR;\n    c2 = c2 || EMPTY_ARR;\n    const oldLength = c1.length;\n    const newLength = c2.length;\n    const commonLength = Math.min(oldLength, newLength);\n    let i;\n    for (i = 0; i < commonLength; i++) {\n      const nextChild = c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]);\n      patch(\n        c1[i],\n        nextChild,\n        container,\n        null,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n    }\n    if (oldLength > newLength) {\n      unmountChildren(\n        c1,\n        parentComponent,\n        parentSuspense,\n        true,\n        false,\n        commonLength\n      );\n    } else {\n      mountChildren(\n        c2,\n        container,\n        anchor,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized,\n        commonLength\n      );\n    }\n  };\n  const patchKeyedChildren = (c1, c2, container, parentAnchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized) => {\n    let i = 0;\n    const l2 = c2.length;\n    let e1 = c1.length - 1;\n    let e2 = l2 - 1;\n    while (i <= e1 && i <= e2) {\n      const n1 = c1[i];\n      const n2 = c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]);\n      if (isSameVNodeType(n1, n2)) {\n        patch(\n          n1,\n          n2,\n          container,\n          null,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n      } else {\n        break;\n      }\n      i++;\n    }\n    while (i <= e1 && i <= e2) {\n      const n1 = c1[e1];\n      const n2 = c2[e2] = optimized ? cloneIfMounted(c2[e2]) : normalizeVNode(c2[e2]);\n      if (isSameVNodeType(n1, n2)) {\n        patch(\n          n1,\n          n2,\n          container,\n          null,\n          parentComponent,\n          parentSuspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n      } else {\n        break;\n      }\n      e1--;\n      e2--;\n    }\n    if (i > e1) {\n      if (i <= e2) {\n        const nextPos = e2 + 1;\n        const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor;\n        while (i <= e2) {\n          patch(\n            null,\n            c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]),\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n          i++;\n        }\n      }\n    } else if (i > e2) {\n      while (i <= e1) {\n        unmount(c1[i], parentComponent, parentSuspense, true);\n        i++;\n      }\n    } else {\n      const s1 = i;\n      const s2 = i;\n      const keyToNewIndexMap = /* @__PURE__ */ new Map();\n      for (i = s2; i <= e2; i++) {\n        const nextChild = c2[i] = optimized ? cloneIfMounted(c2[i]) : normalizeVNode(c2[i]);\n        if (nextChild.key != null) {\n          if (keyToNewIndexMap.has(nextChild.key)) {\n            warn$1(\n              `Duplicate keys found during update:`,\n              JSON.stringify(nextChild.key),\n              `Make sure keys are unique.`\n            );\n          }\n          keyToNewIndexMap.set(nextChild.key, i);\n        }\n      }\n      let j;\n      let patched = 0;\n      const toBePatched = e2 - s2 + 1;\n      let moved = false;\n      let maxNewIndexSoFar = 0;\n      const newIndexToOldIndexMap = new Array(toBePatched);\n      for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0;\n      for (i = s1; i <= e1; i++) {\n        const prevChild = c1[i];\n        if (patched >= toBePatched) {\n          unmount(prevChild, parentComponent, parentSuspense, true);\n          continue;\n        }\n        let newIndex;\n        if (prevChild.key != null) {\n          newIndex = keyToNewIndexMap.get(prevChild.key);\n        } else {\n          for (j = s2; j <= e2; j++) {\n            if (newIndexToOldIndexMap[j - s2] === 0 && isSameVNodeType(prevChild, c2[j])) {\n              newIndex = j;\n              break;\n            }\n          }\n        }\n        if (newIndex === void 0) {\n          unmount(prevChild, parentComponent, parentSuspense, true);\n        } else {\n          newIndexToOldIndexMap[newIndex - s2] = i + 1;\n          if (newIndex >= maxNewIndexSoFar) {\n            maxNewIndexSoFar = newIndex;\n          } else {\n            moved = true;\n          }\n          patch(\n            prevChild,\n            c2[newIndex],\n            container,\n            null,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n          patched++;\n        }\n      }\n      const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : EMPTY_ARR;\n      j = increasingNewIndexSequence.length - 1;\n      for (i = toBePatched - 1; i >= 0; i--) {\n        const nextIndex = s2 + i;\n        const nextChild = c2[nextIndex];\n        const anchor = nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor;\n        if (newIndexToOldIndexMap[i] === 0) {\n          patch(\n            null,\n            nextChild,\n            container,\n            anchor,\n            parentComponent,\n            parentSuspense,\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n        } else if (moved) {\n          if (j < 0 || i !== increasingNewIndexSequence[j]) {\n            move(nextChild, container, anchor, 2);\n          } else {\n            j--;\n          }\n        }\n      }\n    }\n  };\n  const move = (vnode, container, anchor, moveType, parentSuspense = null) => {\n    const { el, type, transition, children, shapeFlag } = vnode;\n    if (shapeFlag & 6) {\n      move(vnode.component.subTree, container, anchor, moveType);\n      return;\n    }\n    if (shapeFlag & 128) {\n      vnode.suspense.move(container, anchor, moveType);\n      return;\n    }\n    if (shapeFlag & 64) {\n      type.move(vnode, container, anchor, internals);\n      return;\n    }\n    if (type === Fragment) {\n      hostInsert(el, container, anchor);\n      for (let i = 0; i < children.length; i++) {\n        move(children[i], container, anchor, moveType);\n      }\n      hostInsert(vnode.anchor, container, anchor);\n      return;\n    }\n    if (type === Static) {\n      moveStaticNode(vnode, container, anchor);\n      return;\n    }\n    const needTransition2 = moveType !== 2 && shapeFlag & 1 && transition;\n    if (needTransition2) {\n      if (moveType === 0) {\n        transition.beforeEnter(el);\n        hostInsert(el, container, anchor);\n        queuePostRenderEffect(() => transition.enter(el), parentSuspense);\n      } else {\n        const { leave, delayLeave, afterLeave } = transition;\n        const remove22 = () => hostInsert(el, container, anchor);\n        const performLeave = () => {\n          leave(el, () => {\n            remove22();\n            afterLeave && afterLeave();\n          });\n        };\n        if (delayLeave) {\n          delayLeave(el, remove22, performLeave);\n        } else {\n          performLeave();\n        }\n      }\n    } else {\n      hostInsert(el, container, anchor);\n    }\n  };\n  const unmount = (vnode, parentComponent, parentSuspense, doRemove = false, optimized = false) => {\n    const {\n      type,\n      props,\n      ref: ref2,\n      children,\n      dynamicChildren,\n      shapeFlag,\n      patchFlag,\n      dirs,\n      cacheIndex\n    } = vnode;\n    if (patchFlag === -2) {\n      optimized = false;\n    }\n    if (ref2 != null) {\n      setRef(ref2, null, parentSuspense, vnode, true);\n    }\n    if (cacheIndex != null) {\n      parentComponent.renderCache[cacheIndex] = void 0;\n    }\n    if (shapeFlag & 256) {\n      parentComponent.ctx.deactivate(vnode);\n      return;\n    }\n    const shouldInvokeDirs = shapeFlag & 1 && dirs;\n    const shouldInvokeVnodeHook = !isAsyncWrapper(vnode);\n    let vnodeHook;\n    if (shouldInvokeVnodeHook && (vnodeHook = props && props.onVnodeBeforeUnmount)) {\n      invokeVNodeHook(vnodeHook, parentComponent, vnode);\n    }\n    if (shapeFlag & 6) {\n      unmountComponent(vnode.component, parentSuspense, doRemove);\n    } else {\n      if (shapeFlag & 128) {\n        vnode.suspense.unmount(parentSuspense, doRemove);\n        return;\n      }\n      if (shouldInvokeDirs) {\n        invokeDirectiveHook(vnode, null, parentComponent, \"beforeUnmount\");\n      }\n      if (shapeFlag & 64) {\n        vnode.type.remove(\n          vnode,\n          parentComponent,\n          parentSuspense,\n          internals,\n          doRemove\n        );\n      } else if (dynamicChildren && // #5154\n      // when v-once is used inside a block, setBlockTracking(-1) marks the\n      // parent block with hasOnce: true\n      // so that it doesn't take the fast path during unmount - otherwise\n      // components nested in v-once are never unmounted.\n      !dynamicChildren.hasOnce && // #1153: fast path should not be taken for non-stable (v-for) fragments\n      (type !== Fragment || patchFlag > 0 && patchFlag & 64)) {\n        unmountChildren(\n          dynamicChildren,\n          parentComponent,\n          parentSuspense,\n          false,\n          true\n        );\n      } else if (type === Fragment && patchFlag & (128 | 256) || !optimized && shapeFlag & 16) {\n        unmountChildren(children, parentComponent, parentSuspense);\n      }\n      if (doRemove) {\n        remove2(vnode);\n      }\n    }\n    if (shouldInvokeVnodeHook && (vnodeHook = props && props.onVnodeUnmounted) || shouldInvokeDirs) {\n      queuePostRenderEffect(() => {\n        vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode);\n        shouldInvokeDirs && invokeDirectiveHook(vnode, null, parentComponent, \"unmounted\");\n      }, parentSuspense);\n    }\n  };\n  const remove2 = (vnode) => {\n    const { type, el, anchor, transition } = vnode;\n    if (type === Fragment) {\n      if (vnode.patchFlag > 0 && vnode.patchFlag & 2048 && transition && !transition.persisted) {\n        vnode.children.forEach((child) => {\n          if (child.type === Comment) {\n            hostRemove(child.el);\n          } else {\n            remove2(child);\n          }\n        });\n      } else {\n        removeFragment(el, anchor);\n      }\n      return;\n    }\n    if (type === Static) {\n      removeStaticNode(vnode);\n      return;\n    }\n    const performRemove = () => {\n      hostRemove(el);\n      if (transition && !transition.persisted && transition.afterLeave) {\n        transition.afterLeave();\n      }\n    };\n    if (vnode.shapeFlag & 1 && transition && !transition.persisted) {\n      const { leave, delayLeave } = transition;\n      const performLeave = () => leave(el, performRemove);\n      if (delayLeave) {\n        delayLeave(vnode.el, performRemove, performLeave);\n      } else {\n        performLeave();\n      }\n    } else {\n      performRemove();\n    }\n  };\n  const removeFragment = (cur, end) => {\n    let next;\n    while (cur !== end) {\n      next = hostNextSibling(cur);\n      hostRemove(cur);\n      cur = next;\n    }\n    hostRemove(end);\n  };\n  const unmountComponent = (instance, parentSuspense, doRemove) => {\n    if (instance.type.__hmrId) {\n      unregisterHMR(instance);\n    }\n    const { bum, scope, job, subTree, um, m, a } = instance;\n    invalidateMount(m);\n    invalidateMount(a);\n    if (bum) {\n      invokeArrayFns(bum);\n    }\n    scope.stop();\n    if (job) {\n      job.flags |= 8;\n      unmount(subTree, instance, parentSuspense, doRemove);\n    }\n    if (um) {\n      queuePostRenderEffect(um, parentSuspense);\n    }\n    queuePostRenderEffect(() => {\n      instance.isUnmounted = true;\n    }, parentSuspense);\n    if (parentSuspense && parentSuspense.pendingBranch && !parentSuspense.isUnmounted && instance.asyncDep && !instance.asyncResolved && instance.suspenseId === parentSuspense.pendingId) {\n      parentSuspense.deps--;\n      if (parentSuspense.deps === 0) {\n        parentSuspense.resolve();\n      }\n    }\n    if (true) {\n      devtoolsComponentRemoved(instance);\n    }\n  };\n  const unmountChildren = (children, parentComponent, parentSuspense, doRemove = false, optimized = false, start = 0) => {\n    for (let i = start; i < children.length; i++) {\n      unmount(children[i], parentComponent, parentSuspense, doRemove, optimized);\n    }\n  };\n  const getNextHostNode = (vnode) => {\n    if (vnode.shapeFlag & 6) {\n      return getNextHostNode(vnode.component.subTree);\n    }\n    if (vnode.shapeFlag & 128) {\n      return vnode.suspense.next();\n    }\n    const el = hostNextSibling(vnode.anchor || vnode.el);\n    const teleportEnd = el && el[TeleportEndKey];\n    return teleportEnd ? hostNextSibling(teleportEnd) : el;\n  };\n  let isFlushing = false;\n  const render2 = (vnode, container, namespace) => {\n    if (vnode == null) {\n      if (container._vnode) {\n        unmount(container._vnode, null, null, true);\n      }\n    } else {\n      patch(\n        container._vnode || null,\n        vnode,\n        container,\n        null,\n        null,\n        null,\n        namespace\n      );\n    }\n    container._vnode = vnode;\n    if (!isFlushing) {\n      isFlushing = true;\n      flushPreFlushCbs();\n      flushPostFlushCbs();\n      isFlushing = false;\n    }\n  };\n  const internals = {\n    p: patch,\n    um: unmount,\n    m: move,\n    r: remove2,\n    mt: mountComponent,\n    mc: mountChildren,\n    pc: patchChildren,\n    pbc: patchBlockChildren,\n    n: getNextHostNode,\n    o: options\n  };\n  let hydrate2;\n  let hydrateNode;\n  if (createHydrationFns) {\n    [hydrate2, hydrateNode] = createHydrationFns(\n      internals\n    );\n  }\n  return {\n    render: render2,\n    hydrate: hydrate2,\n    createApp: createAppAPI(render2, hydrate2)\n  };\n}\nfunction resolveChildrenNamespace({ type, props }, currentNamespace) {\n  return currentNamespace === \"svg\" && type === \"foreignObject\" || currentNamespace === \"mathml\" && type === \"annotation-xml\" && props && props.encoding && props.encoding.includes(\"html\") ? void 0 : currentNamespace;\n}\nfunction toggleRecurse({ effect: effect2, job }, allowed) {\n  if (allowed) {\n    effect2.flags |= 32;\n    job.flags |= 4;\n  } else {\n    effect2.flags &= ~32;\n    job.flags &= ~4;\n  }\n}\nfunction needTransition(parentSuspense, transition) {\n  return (!parentSuspense || parentSuspense && !parentSuspense.pendingBranch) && transition && !transition.persisted;\n}\nfunction traverseStaticChildren(n1, n2, shallow = false) {\n  const ch1 = n1.children;\n  const ch2 = n2.children;\n  if (isArray(ch1) && isArray(ch2)) {\n    for (let i = 0; i < ch1.length; i++) {\n      const c1 = ch1[i];\n      let c2 = ch2[i];\n      if (c2.shapeFlag & 1 && !c2.dynamicChildren) {\n        if (c2.patchFlag <= 0 || c2.patchFlag === 32) {\n          c2 = ch2[i] = cloneIfMounted(ch2[i]);\n          c2.el = c1.el;\n        }\n        if (!shallow && c2.patchFlag !== -2)\n          traverseStaticChildren(c1, c2);\n      }\n      if (c2.type === Text) {\n        c2.el = c1.el;\n      }\n      if (c2.type === Comment && !c2.el) {\n        c2.el = c1.el;\n      }\n    }\n  }\n}\nfunction getSequence(arr) {\n  const p2 = arr.slice();\n  const result = [0];\n  let i, j, u, v, c;\n  const len = arr.length;\n  for (i = 0; i < len; i++) {\n    const arrI = arr[i];\n    if (arrI !== 0) {\n      j = result[result.length - 1];\n      if (arr[j] < arrI) {\n        p2[i] = j;\n        result.push(i);\n        continue;\n      }\n      u = 0;\n      v = result.length - 1;\n      while (u < v) {\n        c = u + v >> 1;\n        if (arr[result[c]] < arrI) {\n          u = c + 1;\n        } else {\n          v = c;\n        }\n      }\n      if (arrI < arr[result[u]]) {\n        if (u > 0) {\n          p2[i] = result[u - 1];\n        }\n        result[u] = i;\n      }\n    }\n  }\n  u = result.length;\n  v = result[u - 1];\n  while (u-- > 0) {\n    result[u] = v;\n    v = p2[v];\n  }\n  return result;\n}\nfunction locateNonHydratedAsyncRoot(instance) {\n  const subComponent = instance.subTree.component;\n  if (subComponent) {\n    if (subComponent.asyncDep && !subComponent.asyncResolved) {\n      return subComponent;\n    } else {\n      return locateNonHydratedAsyncRoot(subComponent);\n    }\n  }\n}\nfunction invalidateMount(hooks) {\n  if (hooks) {\n    for (let i = 0; i < hooks.length; i++)\n      hooks[i].flags |= 8;\n  }\n}\nvar ssrContextKey = Symbol.for(\"v-scx\");\nvar useSSRContext = () => {\n  {\n    const ctx = inject(ssrContextKey);\n    if (!ctx) {\n      warn$1(\n        `Server rendering context not provided. Make sure to only call useSSRContext() conditionally in the server build.`\n      );\n    }\n    return ctx;\n  }\n};\nfunction watchEffect(effect2, options) {\n  return doWatch(effect2, null, options);\n}\nfunction watchPostEffect(effect2, options) {\n  return doWatch(\n    effect2,\n    null,\n    true ? extend({}, options, { flush: \"post\" }) : { flush: \"post\" }\n  );\n}\nfunction watchSyncEffect(effect2, options) {\n  return doWatch(\n    effect2,\n    null,\n    true ? extend({}, options, { flush: \"sync\" }) : { flush: \"sync\" }\n  );\n}\nfunction watch2(source, cb, options) {\n  if (!isFunction(cb)) {\n    warn$1(\n      `\\`watch(fn, options?)\\` signature has been moved to a separate API. Use \\`watchEffect(fn, options?)\\` instead. \\`watch\\` now only supports \\`watch(source, cb, options?) signature.`\n    );\n  }\n  return doWatch(source, cb, options);\n}\nfunction doWatch(source, cb, options = EMPTY_OBJ) {\n  const { immediate, deep, flush, once } = options;\n  if (!cb) {\n    if (immediate !== void 0) {\n      warn$1(\n        `watch() \"immediate\" option is only respected when using the watch(source, callback, options?) signature.`\n      );\n    }\n    if (deep !== void 0) {\n      warn$1(\n        `watch() \"deep\" option is only respected when using the watch(source, callback, options?) signature.`\n      );\n    }\n    if (once !== void 0) {\n      warn$1(\n        `watch() \"once\" option is only respected when using the watch(source, callback, options?) signature.`\n      );\n    }\n  }\n  const baseWatchOptions = extend({}, options);\n  if (true) baseWatchOptions.onWarn = warn$1;\n  const runsImmediately = cb && immediate || !cb && flush !== \"post\";\n  let ssrCleanup;\n  if (isInSSRComponentSetup) {\n    if (flush === \"sync\") {\n      const ctx = useSSRContext();\n      ssrCleanup = ctx.__watcherHandles || (ctx.__watcherHandles = []);\n    } else if (!runsImmediately) {\n      const watchStopHandle = () => {\n      };\n      watchStopHandle.stop = NOOP;\n      watchStopHandle.resume = NOOP;\n      watchStopHandle.pause = NOOP;\n      return watchStopHandle;\n    }\n  }\n  const instance = currentInstance;\n  baseWatchOptions.call = (fn, type, args) => callWithAsyncErrorHandling(fn, instance, type, args);\n  let isPre = false;\n  if (flush === \"post\") {\n    baseWatchOptions.scheduler = (job) => {\n      queuePostRenderEffect(job, instance && instance.suspense);\n    };\n  } else if (flush !== \"sync\") {\n    isPre = true;\n    baseWatchOptions.scheduler = (job, isFirstRun) => {\n      if (isFirstRun) {\n        job();\n      } else {\n        queueJob(job);\n      }\n    };\n  }\n  baseWatchOptions.augmentJob = (job) => {\n    if (cb) {\n      job.flags |= 4;\n    }\n    if (isPre) {\n      job.flags |= 2;\n      if (instance) {\n        job.id = instance.uid;\n        job.i = instance;\n      }\n    }\n  };\n  const watchHandle = watch(source, cb, baseWatchOptions);\n  if (isInSSRComponentSetup) {\n    if (ssrCleanup) {\n      ssrCleanup.push(watchHandle);\n    } else if (runsImmediately) {\n      watchHandle();\n    }\n  }\n  return watchHandle;\n}\nfunction instanceWatch(source, value, options) {\n  const publicThis = this.proxy;\n  const getter = isString(source) ? source.includes(\".\") ? createPathGetter(publicThis, source) : () => publicThis[source] : source.bind(publicThis, publicThis);\n  let cb;\n  if (isFunction(value)) {\n    cb = value;\n  } else {\n    cb = value.handler;\n    options = value;\n  }\n  const reset = setCurrentInstance(this);\n  const res = doWatch(getter, cb.bind(publicThis), options);\n  reset();\n  return res;\n}\nfunction createPathGetter(ctx, path) {\n  const segments = path.split(\".\");\n  return () => {\n    let cur = ctx;\n    for (let i = 0; i < segments.length && cur; i++) {\n      cur = cur[segments[i]];\n    }\n    return cur;\n  };\n}\nfunction useModel(props, name, options = EMPTY_OBJ) {\n  const i = getCurrentInstance();\n  if (!i) {\n    warn$1(`useModel() called without active instance.`);\n    return ref();\n  }\n  const camelizedName = camelize(name);\n  if (!i.propsOptions[0][camelizedName]) {\n    warn$1(`useModel() called with prop \"${name}\" which is not declared.`);\n    return ref();\n  }\n  const hyphenatedName = hyphenate(name);\n  const modifiers = getModelModifiers(props, camelizedName);\n  const res = customRef((track2, trigger2) => {\n    let localValue;\n    let prevSetValue = EMPTY_OBJ;\n    let prevEmittedValue;\n    watchSyncEffect(() => {\n      const propValue = props[camelizedName];\n      if (hasChanged(localValue, propValue)) {\n        localValue = propValue;\n        trigger2();\n      }\n    });\n    return {\n      get() {\n        track2();\n        return options.get ? options.get(localValue) : localValue;\n      },\n      set(value) {\n        const emittedValue = options.set ? options.set(value) : value;\n        if (!hasChanged(emittedValue, localValue) && !(prevSetValue !== EMPTY_OBJ && hasChanged(value, prevSetValue))) {\n          return;\n        }\n        const rawProps = i.vnode.props;\n        if (!(rawProps && // check if parent has passed v-model\n        (name in rawProps || camelizedName in rawProps || hyphenatedName in rawProps) && (`onUpdate:${name}` in rawProps || `onUpdate:${camelizedName}` in rawProps || `onUpdate:${hyphenatedName}` in rawProps))) {\n          localValue = value;\n          trigger2();\n        }\n        i.emit(`update:${name}`, emittedValue);\n        if (hasChanged(value, emittedValue) && hasChanged(value, prevSetValue) && !hasChanged(emittedValue, prevEmittedValue)) {\n          trigger2();\n        }\n        prevSetValue = value;\n        prevEmittedValue = emittedValue;\n      }\n    };\n  });\n  res[Symbol.iterator] = () => {\n    let i2 = 0;\n    return {\n      next() {\n        if (i2 < 2) {\n          return { value: i2++ ? modifiers || EMPTY_OBJ : res, done: false };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  };\n  return res;\n}\nvar getModelModifiers = (props, modelName) => {\n  return modelName === \"modelValue\" || modelName === \"model-value\" ? props.modelModifiers : props[`${modelName}Modifiers`] || props[`${camelize(modelName)}Modifiers`] || props[`${hyphenate(modelName)}Modifiers`];\n};\nfunction emit(instance, event, ...rawArgs) {\n  if (instance.isUnmounted) return;\n  const props = instance.vnode.props || EMPTY_OBJ;\n  if (true) {\n    const {\n      emitsOptions,\n      propsOptions: [propsOptions]\n    } = instance;\n    if (emitsOptions) {\n      if (!(event in emitsOptions) && true) {\n        if (!propsOptions || !(toHandlerKey(camelize(event)) in propsOptions)) {\n          warn$1(\n            `Component emitted event \"${event}\" but it is neither declared in the emits option nor as an \"${toHandlerKey(camelize(event))}\" prop.`\n          );\n        }\n      } else {\n        const validator = emitsOptions[event];\n        if (isFunction(validator)) {\n          const isValid = validator(...rawArgs);\n          if (!isValid) {\n            warn$1(\n              `Invalid event arguments: event validation failed for event \"${event}\".`\n            );\n          }\n        }\n      }\n    }\n  }\n  let args = rawArgs;\n  const isModelListener2 = event.startsWith(\"update:\");\n  const modifiers = isModelListener2 && getModelModifiers(props, event.slice(7));\n  if (modifiers) {\n    if (modifiers.trim) {\n      args = rawArgs.map((a) => isString(a) ? a.trim() : a);\n    }\n    if (modifiers.number) {\n      args = rawArgs.map(looseToNumber);\n    }\n  }\n  if (true) {\n    devtoolsComponentEmit(instance, event, args);\n  }\n  if (true) {\n    const lowerCaseEvent = event.toLowerCase();\n    if (lowerCaseEvent !== event && props[toHandlerKey(lowerCaseEvent)]) {\n      warn$1(\n        `Event \"${lowerCaseEvent}\" is emitted in component ${formatComponentName(\n          instance,\n          instance.type\n        )} but the handler is registered for \"${event}\". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use \"${hyphenate(\n          event\n        )}\" instead of \"${event}\".`\n      );\n    }\n  }\n  let handlerName;\n  let handler = props[handlerName = toHandlerKey(event)] || // also try camelCase event handler (#2249)\n  props[handlerName = toHandlerKey(camelize(event))];\n  if (!handler && isModelListener2) {\n    handler = props[handlerName = toHandlerKey(hyphenate(event))];\n  }\n  if (handler) {\n    callWithAsyncErrorHandling(\n      handler,\n      instance,\n      6,\n      args\n    );\n  }\n  const onceHandler = props[handlerName + `Once`];\n  if (onceHandler) {\n    if (!instance.emitted) {\n      instance.emitted = {};\n    } else if (instance.emitted[handlerName]) {\n      return;\n    }\n    instance.emitted[handlerName] = true;\n    callWithAsyncErrorHandling(\n      onceHandler,\n      instance,\n      6,\n      args\n    );\n  }\n}\nfunction normalizeEmitsOptions(comp, appContext, asMixin = false) {\n  const cache = appContext.emitsCache;\n  const cached = cache.get(comp);\n  if (cached !== void 0) {\n    return cached;\n  }\n  const raw = comp.emits;\n  let normalized = {};\n  let hasExtends = false;\n  if (__VUE_OPTIONS_API__ && !isFunction(comp)) {\n    const extendEmits = (raw2) => {\n      const normalizedFromExtend = normalizeEmitsOptions(raw2, appContext, true);\n      if (normalizedFromExtend) {\n        hasExtends = true;\n        extend(normalized, normalizedFromExtend);\n      }\n    };\n    if (!asMixin && appContext.mixins.length) {\n      appContext.mixins.forEach(extendEmits);\n    }\n    if (comp.extends) {\n      extendEmits(comp.extends);\n    }\n    if (comp.mixins) {\n      comp.mixins.forEach(extendEmits);\n    }\n  }\n  if (!raw && !hasExtends) {\n    if (isObject(comp)) {\n      cache.set(comp, null);\n    }\n    return null;\n  }\n  if (isArray(raw)) {\n    raw.forEach((key) => normalized[key] = null);\n  } else {\n    extend(normalized, raw);\n  }\n  if (isObject(comp)) {\n    cache.set(comp, normalized);\n  }\n  return normalized;\n}\nfunction isEmitListener(options, key) {\n  if (!options || !isOn(key)) {\n    return false;\n  }\n  key = key.slice(2).replace(/Once$/, \"\");\n  return hasOwn(options, key[0].toLowerCase() + key.slice(1)) || hasOwn(options, hyphenate(key)) || hasOwn(options, key);\n}\nvar accessedAttrs = false;\nfunction markAttrsAccessed() {\n  accessedAttrs = true;\n}\nfunction renderComponentRoot(instance) {\n  const {\n    type: Component,\n    vnode,\n    proxy,\n    withProxy,\n    propsOptions: [propsOptions],\n    slots,\n    attrs,\n    emit: emit2,\n    render: render2,\n    renderCache,\n    props,\n    data,\n    setupState,\n    ctx,\n    inheritAttrs\n  } = instance;\n  const prev = setCurrentRenderingInstance(instance);\n  let result;\n  let fallthroughAttrs;\n  if (true) {\n    accessedAttrs = false;\n  }\n  try {\n    if (vnode.shapeFlag & 4) {\n      const proxyToUse = withProxy || proxy;\n      const thisProxy = setupState.__isScriptSetup ? new Proxy(proxyToUse, {\n        get(target, key, receiver) {\n          warn$1(\n            `Property '${String(\n              key\n            )}' was accessed via 'this'. Avoid using 'this' in templates.`\n          );\n          return Reflect.get(target, key, receiver);\n        }\n      }) : proxyToUse;\n      result = normalizeVNode(\n        render2.call(\n          thisProxy,\n          proxyToUse,\n          renderCache,\n          true ? shallowReadonly(props) : props,\n          setupState,\n          data,\n          ctx\n        )\n      );\n      fallthroughAttrs = attrs;\n    } else {\n      const render22 = Component;\n      if (attrs === props) {\n        markAttrsAccessed();\n      }\n      result = normalizeVNode(\n        render22.length > 1 ? render22(\n          true ? shallowReadonly(props) : props,\n          true ? {\n            get attrs() {\n              markAttrsAccessed();\n              return shallowReadonly(attrs);\n            },\n            slots,\n            emit: emit2\n          } : { attrs, slots, emit: emit2 }\n        ) : render22(\n          true ? shallowReadonly(props) : props,\n          null\n        )\n      );\n      fallthroughAttrs = Component.props ? attrs : getFunctionalFallthrough(attrs);\n    }\n  } catch (err) {\n    blockStack.length = 0;\n    handleError(err, instance, 1);\n    result = createVNode(Comment);\n  }\n  let root = result;\n  let setRoot = void 0;\n  if (result.patchFlag > 0 && result.patchFlag & 2048) {\n    [root, setRoot] = getChildRoot(result);\n  }\n  if (fallthroughAttrs && inheritAttrs !== false) {\n    const keys = Object.keys(fallthroughAttrs);\n    const { shapeFlag } = root;\n    if (keys.length) {\n      if (shapeFlag & (1 | 6)) {\n        if (propsOptions && keys.some(isModelListener)) {\n          fallthroughAttrs = filterModelListeners(\n            fallthroughAttrs,\n            propsOptions\n          );\n        }\n        root = cloneVNode(root, fallthroughAttrs, false, true);\n      } else if (!accessedAttrs && root.type !== Comment) {\n        const allAttrs = Object.keys(attrs);\n        const eventAttrs = [];\n        const extraAttrs = [];\n        for (let i = 0, l = allAttrs.length; i < l; i++) {\n          const key = allAttrs[i];\n          if (isOn(key)) {\n            if (!isModelListener(key)) {\n              eventAttrs.push(key[2].toLowerCase() + key.slice(3));\n            }\n          } else {\n            extraAttrs.push(key);\n          }\n        }\n        if (extraAttrs.length) {\n          warn$1(\n            `Extraneous non-props attributes (${extraAttrs.join(\", \")}) were passed to component but could not be automatically inherited because component renders fragment or text or teleport root nodes.`\n          );\n        }\n        if (eventAttrs.length) {\n          warn$1(\n            `Extraneous non-emits event listeners (${eventAttrs.join(\", \")}) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the \"emits\" option.`\n          );\n        }\n      }\n    }\n  }\n  if (vnode.dirs) {\n    if (!isElementRoot(root)) {\n      warn$1(\n        `Runtime directive used on component with non-element root node. The directives will not function as intended.`\n      );\n    }\n    root = cloneVNode(root, null, false, true);\n    root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs;\n  }\n  if (vnode.transition) {\n    if (!isElementRoot(root)) {\n      warn$1(\n        `Component inside <Transition> renders non-element root node that cannot be animated.`\n      );\n    }\n    setTransitionHooks(root, vnode.transition);\n  }\n  if (setRoot) {\n    setRoot(root);\n  } else {\n    result = root;\n  }\n  setCurrentRenderingInstance(prev);\n  return result;\n}\nvar getChildRoot = (vnode) => {\n  const rawChildren = vnode.children;\n  const dynamicChildren = vnode.dynamicChildren;\n  const childRoot = filterSingleRoot(rawChildren, false);\n  if (!childRoot) {\n    return [vnode, void 0];\n  } else if (childRoot.patchFlag > 0 && childRoot.patchFlag & 2048) {\n    return getChildRoot(childRoot);\n  }\n  const index = rawChildren.indexOf(childRoot);\n  const dynamicIndex = dynamicChildren ? dynamicChildren.indexOf(childRoot) : -1;\n  const setRoot = (updatedRoot) => {\n    rawChildren[index] = updatedRoot;\n    if (dynamicChildren) {\n      if (dynamicIndex > -1) {\n        dynamicChildren[dynamicIndex] = updatedRoot;\n      } else if (updatedRoot.patchFlag > 0) {\n        vnode.dynamicChildren = [...dynamicChildren, updatedRoot];\n      }\n    }\n  };\n  return [normalizeVNode(childRoot), setRoot];\n};\nfunction filterSingleRoot(children, recurse = true) {\n  let singleRoot;\n  for (let i = 0; i < children.length; i++) {\n    const child = children[i];\n    if (isVNode(child)) {\n      if (child.type !== Comment || child.children === \"v-if\") {\n        if (singleRoot) {\n          return;\n        } else {\n          singleRoot = child;\n          if (recurse && singleRoot.patchFlag > 0 && singleRoot.patchFlag & 2048) {\n            return filterSingleRoot(singleRoot.children);\n          }\n        }\n      }\n    } else {\n      return;\n    }\n  }\n  return singleRoot;\n}\nvar getFunctionalFallthrough = (attrs) => {\n  let res;\n  for (const key in attrs) {\n    if (key === \"class\" || key === \"style\" || isOn(key)) {\n      (res || (res = {}))[key] = attrs[key];\n    }\n  }\n  return res;\n};\nvar filterModelListeners = (attrs, props) => {\n  const res = {};\n  for (const key in attrs) {\n    if (!isModelListener(key) || !(key.slice(9) in props)) {\n      res[key] = attrs[key];\n    }\n  }\n  return res;\n};\nvar isElementRoot = (vnode) => {\n  return vnode.shapeFlag & (6 | 1) || vnode.type === Comment;\n};\nfunction shouldUpdateComponent(prevVNode, nextVNode, optimized) {\n  const { props: prevProps, children: prevChildren, component } = prevVNode;\n  const { props: nextProps, children: nextChildren, patchFlag } = nextVNode;\n  const emits = component.emitsOptions;\n  if ((prevChildren || nextChildren) && isHmrUpdating) {\n    return true;\n  }\n  if (nextVNode.dirs || nextVNode.transition) {\n    return true;\n  }\n  if (optimized && patchFlag >= 0) {\n    if (patchFlag & 1024) {\n      return true;\n    }\n    if (patchFlag & 16) {\n      if (!prevProps) {\n        return !!nextProps;\n      }\n      return hasPropsChanged(prevProps, nextProps, emits);\n    } else if (patchFlag & 8) {\n      const dynamicProps = nextVNode.dynamicProps;\n      for (let i = 0; i < dynamicProps.length; i++) {\n        const key = dynamicProps[i];\n        if (nextProps[key] !== prevProps[key] && !isEmitListener(emits, key)) {\n          return true;\n        }\n      }\n    }\n  } else {\n    if (prevChildren || nextChildren) {\n      if (!nextChildren || !nextChildren.$stable) {\n        return true;\n      }\n    }\n    if (prevProps === nextProps) {\n      return false;\n    }\n    if (!prevProps) {\n      return !!nextProps;\n    }\n    if (!nextProps) {\n      return true;\n    }\n    return hasPropsChanged(prevProps, nextProps, emits);\n  }\n  return false;\n}\nfunction hasPropsChanged(prevProps, nextProps, emitsOptions) {\n  const nextKeys = Object.keys(nextProps);\n  if (nextKeys.length !== Object.keys(prevProps).length) {\n    return true;\n  }\n  for (let i = 0; i < nextKeys.length; i++) {\n    const key = nextKeys[i];\n    if (nextProps[key] !== prevProps[key] && !isEmitListener(emitsOptions, key)) {\n      return true;\n    }\n  }\n  return false;\n}\nfunction updateHOCHostEl({ vnode, parent }, el) {\n  while (parent) {\n    const root = parent.subTree;\n    if (root.suspense && root.suspense.activeBranch === vnode) {\n      root.el = vnode.el;\n    }\n    if (root === vnode) {\n      (vnode = parent.vnode).el = el;\n      parent = parent.parent;\n    } else {\n      break;\n    }\n  }\n}\nvar isSuspense = (type) => type.__isSuspense;\nvar suspenseId = 0;\nvar SuspenseImpl = {\n  name: \"Suspense\",\n  // In order to make Suspense tree-shakable, we need to avoid importing it\n  // directly in the renderer. The renderer checks for the __isSuspense flag\n  // on a vnode's type and calls the `process` method, passing in renderer\n  // internals.\n  __isSuspense: true,\n  process(n1, n2, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) {\n    if (n1 == null) {\n      mountSuspense(\n        n2,\n        container,\n        anchor,\n        parentComponent,\n        parentSuspense,\n        namespace,\n        slotScopeIds,\n        optimized,\n        rendererInternals\n      );\n    } else {\n      if (parentSuspense && parentSuspense.deps > 0 && !n1.suspense.isInFallback) {\n        n2.suspense = n1.suspense;\n        n2.suspense.vnode = n2;\n        n2.el = n1.el;\n        return;\n      }\n      patchSuspense(\n        n1,\n        n2,\n        container,\n        anchor,\n        parentComponent,\n        namespace,\n        slotScopeIds,\n        optimized,\n        rendererInternals\n      );\n    }\n  },\n  hydrate: hydrateSuspense,\n  normalize: normalizeSuspenseChildren\n};\nvar Suspense = SuspenseImpl;\nfunction triggerEvent(vnode, name) {\n  const eventListener = vnode.props && vnode.props[name];\n  if (isFunction(eventListener)) {\n    eventListener();\n  }\n}\nfunction mountSuspense(vnode, container, anchor, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals) {\n  const {\n    p: patch,\n    o: { createElement }\n  } = rendererInternals;\n  const hiddenContainer = createElement(\"div\");\n  const suspense = vnode.suspense = createSuspenseBoundary(\n    vnode,\n    parentSuspense,\n    parentComponent,\n    container,\n    hiddenContainer,\n    anchor,\n    namespace,\n    slotScopeIds,\n    optimized,\n    rendererInternals\n  );\n  patch(\n    null,\n    suspense.pendingBranch = vnode.ssContent,\n    hiddenContainer,\n    null,\n    parentComponent,\n    suspense,\n    namespace,\n    slotScopeIds\n  );\n  if (suspense.deps > 0) {\n    triggerEvent(vnode, \"onPending\");\n    triggerEvent(vnode, \"onFallback\");\n    patch(\n      null,\n      vnode.ssFallback,\n      container,\n      anchor,\n      parentComponent,\n      null,\n      // fallback tree will not have suspense context\n      namespace,\n      slotScopeIds\n    );\n    setActiveBranch(suspense, vnode.ssFallback);\n  } else {\n    suspense.resolve(false, true);\n  }\n}\nfunction patchSuspense(n1, n2, container, anchor, parentComponent, namespace, slotScopeIds, optimized, { p: patch, um: unmount, o: { createElement } }) {\n  const suspense = n2.suspense = n1.suspense;\n  suspense.vnode = n2;\n  n2.el = n1.el;\n  const newBranch = n2.ssContent;\n  const newFallback = n2.ssFallback;\n  const { activeBranch, pendingBranch, isInFallback, isHydrating } = suspense;\n  if (pendingBranch) {\n    suspense.pendingBranch = newBranch;\n    if (isSameVNodeType(newBranch, pendingBranch)) {\n      patch(\n        pendingBranch,\n        newBranch,\n        suspense.hiddenContainer,\n        null,\n        parentComponent,\n        suspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n      if (suspense.deps <= 0) {\n        suspense.resolve();\n      } else if (isInFallback) {\n        if (!isHydrating) {\n          patch(\n            activeBranch,\n            newFallback,\n            container,\n            anchor,\n            parentComponent,\n            null,\n            // fallback tree will not have suspense context\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n          setActiveBranch(suspense, newFallback);\n        }\n      }\n    } else {\n      suspense.pendingId = suspenseId++;\n      if (isHydrating) {\n        suspense.isHydrating = false;\n        suspense.activeBranch = pendingBranch;\n      } else {\n        unmount(pendingBranch, parentComponent, suspense);\n      }\n      suspense.deps = 0;\n      suspense.effects.length = 0;\n      suspense.hiddenContainer = createElement(\"div\");\n      if (isInFallback) {\n        patch(\n          null,\n          newBranch,\n          suspense.hiddenContainer,\n          null,\n          parentComponent,\n          suspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        if (suspense.deps <= 0) {\n          suspense.resolve();\n        } else {\n          patch(\n            activeBranch,\n            newFallback,\n            container,\n            anchor,\n            parentComponent,\n            null,\n            // fallback tree will not have suspense context\n            namespace,\n            slotScopeIds,\n            optimized\n          );\n          setActiveBranch(suspense, newFallback);\n        }\n      } else if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {\n        patch(\n          activeBranch,\n          newBranch,\n          container,\n          anchor,\n          parentComponent,\n          suspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        suspense.resolve(true);\n      } else {\n        patch(\n          null,\n          newBranch,\n          suspense.hiddenContainer,\n          null,\n          parentComponent,\n          suspense,\n          namespace,\n          slotScopeIds,\n          optimized\n        );\n        if (suspense.deps <= 0) {\n          suspense.resolve();\n        }\n      }\n    }\n  } else {\n    if (activeBranch && isSameVNodeType(newBranch, activeBranch)) {\n      patch(\n        activeBranch,\n        newBranch,\n        container,\n        anchor,\n        parentComponent,\n        suspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n      setActiveBranch(suspense, newBranch);\n    } else {\n      triggerEvent(n2, \"onPending\");\n      suspense.pendingBranch = newBranch;\n      if (newBranch.shapeFlag & 512) {\n        suspense.pendingId = newBranch.component.suspenseId;\n      } else {\n        suspense.pendingId = suspenseId++;\n      }\n      patch(\n        null,\n        newBranch,\n        suspense.hiddenContainer,\n        null,\n        parentComponent,\n        suspense,\n        namespace,\n        slotScopeIds,\n        optimized\n      );\n      if (suspense.deps <= 0) {\n        suspense.resolve();\n      } else {\n        const { timeout, pendingId } = suspense;\n        if (timeout > 0) {\n          setTimeout(() => {\n            if (suspense.pendingId === pendingId) {\n              suspense.fallback(newFallback);\n            }\n          }, timeout);\n        } else if (timeout === 0) {\n          suspense.fallback(newFallback);\n        }\n      }\n    }\n  }\n}\nvar hasWarned = false;\nfunction createSuspenseBoundary(vnode, parentSuspense, parentComponent, container, hiddenContainer, anchor, namespace, slotScopeIds, optimized, rendererInternals, isHydrating = false) {\n  if (!hasWarned) {\n    hasWarned = true;\n    console[console.info ? \"info\" : \"log\"](\n      `<Suspense> is an experimental feature and its API will likely change.`\n    );\n  }\n  const {\n    p: patch,\n    m: move,\n    um: unmount,\n    n: next,\n    o: { parentNode, remove: remove2 }\n  } = rendererInternals;\n  let parentSuspenseId;\n  const isSuspensible = isVNodeSuspensible(vnode);\n  if (isSuspensible) {\n    if (parentSuspense && parentSuspense.pendingBranch) {\n      parentSuspenseId = parentSuspense.pendingId;\n      parentSuspense.deps++;\n    }\n  }\n  const timeout = vnode.props ? toNumber(vnode.props.timeout) : void 0;\n  if (true) {\n    assertNumber(timeout, `Suspense timeout`);\n  }\n  const initialAnchor = anchor;\n  const suspense = {\n    vnode,\n    parent: parentSuspense,\n    parentComponent,\n    namespace,\n    container,\n    hiddenContainer,\n    deps: 0,\n    pendingId: suspenseId++,\n    timeout: typeof timeout === \"number\" ? timeout : -1,\n    activeBranch: null,\n    pendingBranch: null,\n    isInFallback: !isHydrating,\n    isHydrating,\n    isUnmounted: false,\n    effects: [],\n    resolve(resume = false, sync = false) {\n      if (true) {\n        if (!resume && !suspense.pendingBranch) {\n          throw new Error(\n            `suspense.resolve() is called without a pending branch.`\n          );\n        }\n        if (suspense.isUnmounted) {\n          throw new Error(\n            `suspense.resolve() is called on an already unmounted suspense boundary.`\n          );\n        }\n      }\n      const {\n        vnode: vnode2,\n        activeBranch,\n        pendingBranch,\n        pendingId,\n        effects,\n        parentComponent: parentComponent2,\n        container: container2\n      } = suspense;\n      let delayEnter = false;\n      if (suspense.isHydrating) {\n        suspense.isHydrating = false;\n      } else if (!resume) {\n        delayEnter = activeBranch && pendingBranch.transition && pendingBranch.transition.mode === \"out-in\";\n        if (delayEnter) {\n          activeBranch.transition.afterLeave = () => {\n            if (pendingId === suspense.pendingId) {\n              move(\n                pendingBranch,\n                container2,\n                anchor === initialAnchor ? next(activeBranch) : anchor,\n                0\n              );\n              queuePostFlushCb(effects);\n            }\n          };\n        }\n        if (activeBranch) {\n          if (parentNode(activeBranch.el) === container2) {\n            anchor = next(activeBranch);\n          }\n          unmount(activeBranch, parentComponent2, suspense, true);\n        }\n        if (!delayEnter) {\n          move(pendingBranch, container2, anchor, 0);\n        }\n      }\n      setActiveBranch(suspense, pendingBranch);\n      suspense.pendingBranch = null;\n      suspense.isInFallback = false;\n      let parent = suspense.parent;\n      let hasUnresolvedAncestor = false;\n      while (parent) {\n        if (parent.pendingBranch) {\n          parent.effects.push(...effects);\n          hasUnresolvedAncestor = true;\n          break;\n        }\n        parent = parent.parent;\n      }\n      if (!hasUnresolvedAncestor && !delayEnter) {\n        queuePostFlushCb(effects);\n      }\n      suspense.effects = [];\n      if (isSuspensible) {\n        if (parentSuspense && parentSuspense.pendingBranch && parentSuspenseId === parentSuspense.pendingId) {\n          parentSuspense.deps--;\n          if (parentSuspense.deps === 0 && !sync) {\n            parentSuspense.resolve();\n          }\n        }\n      }\n      triggerEvent(vnode2, \"onResolve\");\n    },\n    fallback(fallbackVNode) {\n      if (!suspense.pendingBranch) {\n        return;\n      }\n      const { vnode: vnode2, activeBranch, parentComponent: parentComponent2, container: container2, namespace: namespace2 } = suspense;\n      triggerEvent(vnode2, \"onFallback\");\n      const anchor2 = next(activeBranch);\n      const mountFallback = () => {\n        if (!suspense.isInFallback) {\n          return;\n        }\n        patch(\n          null,\n          fallbackVNode,\n          container2,\n          anchor2,\n          parentComponent2,\n          null,\n          // fallback tree will not have suspense context\n          namespace2,\n          slotScopeIds,\n          optimized\n        );\n        setActiveBranch(suspense, fallbackVNode);\n      };\n      const delayEnter = fallbackVNode.transition && fallbackVNode.transition.mode === \"out-in\";\n      if (delayEnter) {\n        activeBranch.transition.afterLeave = mountFallback;\n      }\n      suspense.isInFallback = true;\n      unmount(\n        activeBranch,\n        parentComponent2,\n        null,\n        // no suspense so unmount hooks fire now\n        true\n        // shouldRemove\n      );\n      if (!delayEnter) {\n        mountFallback();\n      }\n    },\n    move(container2, anchor2, type) {\n      suspense.activeBranch && move(suspense.activeBranch, container2, anchor2, type);\n      suspense.container = container2;\n    },\n    next() {\n      return suspense.activeBranch && next(suspense.activeBranch);\n    },\n    registerDep(instance, setupRenderEffect, optimized2) {\n      const isInPendingSuspense = !!suspense.pendingBranch;\n      if (isInPendingSuspense) {\n        suspense.deps++;\n      }\n      const hydratedEl = instance.vnode.el;\n      instance.asyncDep.catch((err) => {\n        handleError(err, instance, 0);\n      }).then((asyncSetupResult) => {\n        if (instance.isUnmounted || suspense.isUnmounted || suspense.pendingId !== instance.suspenseId) {\n          return;\n        }\n        instance.asyncResolved = true;\n        const { vnode: vnode2 } = instance;\n        if (true) {\n          pushWarningContext(vnode2);\n        }\n        handleSetupResult(instance, asyncSetupResult, false);\n        if (hydratedEl) {\n          vnode2.el = hydratedEl;\n        }\n        const placeholder = !hydratedEl && instance.subTree.el;\n        setupRenderEffect(\n          instance,\n          vnode2,\n          // component may have been moved before resolve.\n          // if this is not a hydration, instance.subTree will be the comment\n          // placeholder.\n          parentNode(hydratedEl || instance.subTree.el),\n          // anchor will not be used if this is hydration, so only need to\n          // consider the comment placeholder case.\n          hydratedEl ? null : next(instance.subTree),\n          suspense,\n          namespace,\n          optimized2\n        );\n        if (placeholder) {\n          remove2(placeholder);\n        }\n        updateHOCHostEl(instance, vnode2.el);\n        if (true) {\n          popWarningContext();\n        }\n        if (isInPendingSuspense && --suspense.deps === 0) {\n          suspense.resolve();\n        }\n      });\n    },\n    unmount(parentSuspense2, doRemove) {\n      suspense.isUnmounted = true;\n      if (suspense.activeBranch) {\n        unmount(\n          suspense.activeBranch,\n          parentComponent,\n          parentSuspense2,\n          doRemove\n        );\n      }\n      if (suspense.pendingBranch) {\n        unmount(\n          suspense.pendingBranch,\n          parentComponent,\n          parentSuspense2,\n          doRemove\n        );\n      }\n    }\n  };\n  return suspense;\n}\nfunction hydrateSuspense(node, vnode, parentComponent, parentSuspense, namespace, slotScopeIds, optimized, rendererInternals, hydrateNode) {\n  const suspense = vnode.suspense = createSuspenseBoundary(\n    vnode,\n    parentSuspense,\n    parentComponent,\n    node.parentNode,\n    // eslint-disable-next-line no-restricted-globals\n    document.createElement(\"div\"),\n    null,\n    namespace,\n    slotScopeIds,\n    optimized,\n    rendererInternals,\n    true\n  );\n  const result = hydrateNode(\n    node,\n    suspense.pendingBranch = vnode.ssContent,\n    parentComponent,\n    suspense,\n    slotScopeIds,\n    optimized\n  );\n  if (suspense.deps === 0) {\n    suspense.resolve(false, true);\n  }\n  return result;\n}\nfunction normalizeSuspenseChildren(vnode) {\n  const { shapeFlag, children } = vnode;\n  const isSlotChildren = shapeFlag & 32;\n  vnode.ssContent = normalizeSuspenseSlot(\n    isSlotChildren ? children.default : children\n  );\n  vnode.ssFallback = isSlotChildren ? normalizeSuspenseSlot(children.fallback) : createVNode(Comment);\n}\nfunction normalizeSuspenseSlot(s) {\n  let block;\n  if (isFunction(s)) {\n    const trackBlock = isBlockTreeEnabled && s._c;\n    if (trackBlock) {\n      s._d = false;\n      openBlock();\n    }\n    s = s();\n    if (trackBlock) {\n      s._d = true;\n      block = currentBlock;\n      closeBlock();\n    }\n  }\n  if (isArray(s)) {\n    const singleChild = filterSingleRoot(s);\n    if (!singleChild && s.filter((child) => child !== NULL_DYNAMIC_COMPONENT).length > 0) {\n      warn$1(`<Suspense> slots expect a single root node.`);\n    }\n    s = singleChild;\n  }\n  s = normalizeVNode(s);\n  if (block && !s.dynamicChildren) {\n    s.dynamicChildren = block.filter((c) => c !== s);\n  }\n  return s;\n}\nfunction queueEffectWithSuspense(fn, suspense) {\n  if (suspense && suspense.pendingBranch) {\n    if (isArray(fn)) {\n      suspense.effects.push(...fn);\n    } else {\n      suspense.effects.push(fn);\n    }\n  } else {\n    queuePostFlushCb(fn);\n  }\n}\nfunction setActiveBranch(suspense, branch) {\n  suspense.activeBranch = branch;\n  const { vnode, parentComponent } = suspense;\n  let el = branch.el;\n  while (!el && branch.component) {\n    branch = branch.component.subTree;\n    el = branch.el;\n  }\n  vnode.el = el;\n  if (parentComponent && parentComponent.subTree === vnode) {\n    parentComponent.vnode.el = el;\n    updateHOCHostEl(parentComponent, el);\n  }\n}\nfunction isVNodeSuspensible(vnode) {\n  const suspensible = vnode.props && vnode.props.suspensible;\n  return suspensible != null && suspensible !== false;\n}\nvar Fragment = Symbol.for(\"v-fgt\");\nvar Text = Symbol.for(\"v-txt\");\nvar Comment = Symbol.for(\"v-cmt\");\nvar Static = Symbol.for(\"v-stc\");\nvar blockStack = [];\nvar currentBlock = null;\nfunction openBlock(disableTracking = false) {\n  blockStack.push(currentBlock = disableTracking ? null : []);\n}\nfunction closeBlock() {\n  blockStack.pop();\n  currentBlock = blockStack[blockStack.length - 1] || null;\n}\nvar isBlockTreeEnabled = 1;\nfunction setBlockTracking(value, inVOnce = false) {\n  isBlockTreeEnabled += value;\n  if (value < 0 && currentBlock && inVOnce) {\n    currentBlock.hasOnce = true;\n  }\n}\nfunction setupBlock(vnode) {\n  vnode.dynamicChildren = isBlockTreeEnabled > 0 ? currentBlock || EMPTY_ARR : null;\n  closeBlock();\n  if (isBlockTreeEnabled > 0 && currentBlock) {\n    currentBlock.push(vnode);\n  }\n  return vnode;\n}\nfunction createElementBlock(type, props, children, patchFlag, dynamicProps, shapeFlag) {\n  return setupBlock(\n    createBaseVNode(\n      type,\n      props,\n      children,\n      patchFlag,\n      dynamicProps,\n      shapeFlag,\n      true\n    )\n  );\n}\nfunction createBlock(type, props, children, patchFlag, dynamicProps) {\n  return setupBlock(\n    createVNode(\n      type,\n      props,\n      children,\n      patchFlag,\n      dynamicProps,\n      true\n    )\n  );\n}\nfunction isVNode(value) {\n  return value ? value.__v_isVNode === true : false;\n}\nfunction isSameVNodeType(n1, n2) {\n  if (n2.shapeFlag & 6 && n1.component) {\n    const dirtyInstances = hmrDirtyComponents.get(n2.type);\n    if (dirtyInstances && dirtyInstances.has(n1.component)) {\n      n1.shapeFlag &= ~256;\n      n2.shapeFlag &= ~512;\n      return false;\n    }\n  }\n  return n1.type === n2.type && n1.key === n2.key;\n}\nvar vnodeArgsTransformer;\nfunction transformVNodeArgs(transformer) {\n  vnodeArgsTransformer = transformer;\n}\nvar createVNodeWithArgsTransform = (...args) => {\n  return _createVNode(\n    ...vnodeArgsTransformer ? vnodeArgsTransformer(args, currentRenderingInstance) : args\n  );\n};\nvar normalizeKey = ({ key }) => key != null ? key : null;\nvar normalizeRef = ({\n  ref: ref2,\n  ref_key,\n  ref_for\n}) => {\n  if (typeof ref2 === \"number\") {\n    ref2 = \"\" + ref2;\n  }\n  return ref2 != null ? isString(ref2) || isRef2(ref2) || isFunction(ref2) ? { i: currentRenderingInstance, r: ref2, k: ref_key, f: !!ref_for } : ref2 : null;\n};\nfunction createBaseVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, shapeFlag = type === Fragment ? 0 : 1, isBlockNode = false, needFullChildrenNormalization = false) {\n  const vnode = {\n    __v_isVNode: true,\n    __v_skip: true,\n    type,\n    props,\n    key: props && normalizeKey(props),\n    ref: props && normalizeRef(props),\n    scopeId: currentScopeId,\n    slotScopeIds: null,\n    children,\n    component: null,\n    suspense: null,\n    ssContent: null,\n    ssFallback: null,\n    dirs: null,\n    transition: null,\n    el: null,\n    anchor: null,\n    target: null,\n    targetStart: null,\n    targetAnchor: null,\n    staticCount: 0,\n    shapeFlag,\n    patchFlag,\n    dynamicProps,\n    dynamicChildren: null,\n    appContext: null,\n    ctx: currentRenderingInstance\n  };\n  if (needFullChildrenNormalization) {\n    normalizeChildren(vnode, children);\n    if (shapeFlag & 128) {\n      type.normalize(vnode);\n    }\n  } else if (children) {\n    vnode.shapeFlag |= isString(children) ? 8 : 16;\n  }\n  if (vnode.key !== vnode.key) {\n    warn$1(`VNode created with invalid key (NaN). VNode type:`, vnode.type);\n  }\n  if (isBlockTreeEnabled > 0 && // avoid a block node from tracking itself\n  !isBlockNode && // has current parent block\n  currentBlock && // presence of a patch flag indicates this node needs patching on updates.\n  // component nodes also should always be patched, because even if the\n  // component doesn't need to update, it needs to persist the instance on to\n  // the next vnode so that it can be properly unmounted later.\n  (vnode.patchFlag > 0 || shapeFlag & 6) && // the EVENTS flag is only for hydration and if it is the only flag, the\n  // vnode should not be considered dynamic due to handler caching.\n  vnode.patchFlag !== 32) {\n    currentBlock.push(vnode);\n  }\n  return vnode;\n}\nvar createVNode = true ? createVNodeWithArgsTransform : _createVNode;\nfunction _createVNode(type, props = null, children = null, patchFlag = 0, dynamicProps = null, isBlockNode = false) {\n  if (!type || type === NULL_DYNAMIC_COMPONENT) {\n    if (!type) {\n      warn$1(`Invalid vnode type when creating vnode: ${type}.`);\n    }\n    type = Comment;\n  }\n  if (isVNode(type)) {\n    const cloned = cloneVNode(\n      type,\n      props,\n      true\n      /* mergeRef: true */\n    );\n    if (children) {\n      normalizeChildren(cloned, children);\n    }\n    if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {\n      if (cloned.shapeFlag & 6) {\n        currentBlock[currentBlock.indexOf(type)] = cloned;\n      } else {\n        currentBlock.push(cloned);\n      }\n    }\n    cloned.patchFlag = -2;\n    return cloned;\n  }\n  if (isClassComponent(type)) {\n    type = type.__vccOpts;\n  }\n  if (props) {\n    props = guardReactiveProps(props);\n    let { class: klass, style } = props;\n    if (klass && !isString(klass)) {\n      props.class = normalizeClass(klass);\n    }\n    if (isObject(style)) {\n      if (isProxy(style) && !isArray(style)) {\n        style = extend({}, style);\n      }\n      props.style = normalizeStyle(style);\n    }\n  }\n  const shapeFlag = isString(type) ? 1 : isSuspense(type) ? 128 : isTeleport(type) ? 64 : isObject(type) ? 4 : isFunction(type) ? 2 : 0;\n  if (shapeFlag & 4 && isProxy(type)) {\n    type = toRaw(type);\n    warn$1(\n      `Vue received a Component that was made a reactive object. This can lead to unnecessary performance overhead and should be avoided by marking the component with \\`markRaw\\` or using \\`shallowRef\\` instead of \\`ref\\`.`,\n      `\nComponent that was made reactive: `,\n      type\n    );\n  }\n  return createBaseVNode(\n    type,\n    props,\n    children,\n    patchFlag,\n    dynamicProps,\n    shapeFlag,\n    isBlockNode,\n    true\n  );\n}\nfunction guardReactiveProps(props) {\n  if (!props) return null;\n  return isProxy(props) || isInternalObject(props) ? extend({}, props) : props;\n}\nfunction cloneVNode(vnode, extraProps, mergeRef = false, cloneTransition = false) {\n  const { props, ref: ref2, patchFlag, children, transition } = vnode;\n  const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props;\n  const cloned = {\n    __v_isVNode: true,\n    __v_skip: true,\n    type: vnode.type,\n    props: mergedProps,\n    key: mergedProps && normalizeKey(mergedProps),\n    ref: extraProps && extraProps.ref ? (\n      // #2078 in the case of <component :is=\"vnode\" ref=\"extra\"/>\n      // if the vnode itself already has a ref, cloneVNode will need to merge\n      // the refs so the single vnode can be set on multiple refs\n      mergeRef && ref2 ? isArray(ref2) ? ref2.concat(normalizeRef(extraProps)) : [ref2, normalizeRef(extraProps)] : normalizeRef(extraProps)\n    ) : ref2,\n    scopeId: vnode.scopeId,\n    slotScopeIds: vnode.slotScopeIds,\n    children: patchFlag === -1 && isArray(children) ? children.map(deepCloneVNode) : children,\n    target: vnode.target,\n    targetStart: vnode.targetStart,\n    targetAnchor: vnode.targetAnchor,\n    staticCount: vnode.staticCount,\n    shapeFlag: vnode.shapeFlag,\n    // if the vnode is cloned with extra props, we can no longer assume its\n    // existing patch flag to be reliable and need to add the FULL_PROPS flag.\n    // note: preserve flag for fragments since they use the flag for children\n    // fast paths only.\n    patchFlag: extraProps && vnode.type !== Fragment ? patchFlag === -1 ? 16 : patchFlag | 16 : patchFlag,\n    dynamicProps: vnode.dynamicProps,\n    dynamicChildren: vnode.dynamicChildren,\n    appContext: vnode.appContext,\n    dirs: vnode.dirs,\n    transition,\n    // These should technically only be non-null on mounted VNodes. However,\n    // they *should* be copied for kept-alive vnodes. So we just always copy\n    // them since them being non-null during a mount doesn't affect the logic as\n    // they will simply be overwritten.\n    component: vnode.component,\n    suspense: vnode.suspense,\n    ssContent: vnode.ssContent && cloneVNode(vnode.ssContent),\n    ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),\n    el: vnode.el,\n    anchor: vnode.anchor,\n    ctx: vnode.ctx,\n    ce: vnode.ce\n  };\n  if (transition && cloneTransition) {\n    setTransitionHooks(\n      cloned,\n      transition.clone(cloned)\n    );\n  }\n  return cloned;\n}\nfunction deepCloneVNode(vnode) {\n  const cloned = cloneVNode(vnode);\n  if (isArray(vnode.children)) {\n    cloned.children = vnode.children.map(deepCloneVNode);\n  }\n  return cloned;\n}\nfunction createTextVNode(text = \" \", flag = 0) {\n  return createVNode(Text, null, text, flag);\n}\nfunction createStaticVNode(content, numberOfNodes) {\n  const vnode = createVNode(Static, null, content);\n  vnode.staticCount = numberOfNodes;\n  return vnode;\n}\nfunction createCommentVNode(text = \"\", asBlock = false) {\n  return asBlock ? (openBlock(), createBlock(Comment, null, text)) : createVNode(Comment, null, text);\n}\nfunction normalizeVNode(child) {\n  if (child == null || typeof child === \"boolean\") {\n    return createVNode(Comment);\n  } else if (isArray(child)) {\n    return createVNode(\n      Fragment,\n      null,\n      // #3666, avoid reference pollution when reusing vnode\n      child.slice()\n    );\n  } else if (isVNode(child)) {\n    return cloneIfMounted(child);\n  } else {\n    return createVNode(Text, null, String(child));\n  }\n}\nfunction cloneIfMounted(child) {\n  return child.el === null && child.patchFlag !== -1 || child.memo ? child : cloneVNode(child);\n}\nfunction normalizeChildren(vnode, children) {\n  let type = 0;\n  const { shapeFlag } = vnode;\n  if (children == null) {\n    children = null;\n  } else if (isArray(children)) {\n    type = 16;\n  } else if (typeof children === \"object\") {\n    if (shapeFlag & (1 | 64)) {\n      const slot = children.default;\n      if (slot) {\n        slot._c && (slot._d = false);\n        normalizeChildren(vnode, slot());\n        slot._c && (slot._d = true);\n      }\n      return;\n    } else {\n      type = 32;\n      const slotFlag = children._;\n      if (!slotFlag && !isInternalObject(children)) {\n        children._ctx = currentRenderingInstance;\n      } else if (slotFlag === 3 && currentRenderingInstance) {\n        if (currentRenderingInstance.slots._ === 1) {\n          children._ = 1;\n        } else {\n          children._ = 2;\n          vnode.patchFlag |= 1024;\n        }\n      }\n    }\n  } else if (isFunction(children)) {\n    children = { default: children, _ctx: currentRenderingInstance };\n    type = 32;\n  } else {\n    children = String(children);\n    if (shapeFlag & 64) {\n      type = 16;\n      children = [createTextVNode(children)];\n    } else {\n      type = 8;\n    }\n  }\n  vnode.children = children;\n  vnode.shapeFlag |= type;\n}\nfunction mergeProps(...args) {\n  const ret = {};\n  for (let i = 0; i < args.length; i++) {\n    const toMerge = args[i];\n    for (const key in toMerge) {\n      if (key === \"class\") {\n        if (ret.class !== toMerge.class) {\n          ret.class = normalizeClass([ret.class, toMerge.class]);\n        }\n      } else if (key === \"style\") {\n        ret.style = normalizeStyle([ret.style, toMerge.style]);\n      } else if (isOn(key)) {\n        const existing = ret[key];\n        const incoming = toMerge[key];\n        if (incoming && existing !== incoming && !(isArray(existing) && existing.includes(incoming))) {\n          ret[key] = existing ? [].concat(existing, incoming) : incoming;\n        }\n      } else if (key !== \"\") {\n        ret[key] = toMerge[key];\n      }\n    }\n  }\n  return ret;\n}\nfunction invokeVNodeHook(hook, instance, vnode, prevVNode = null) {\n  callWithAsyncErrorHandling(hook, instance, 7, [\n    vnode,\n    prevVNode\n  ]);\n}\nvar emptyAppContext = createAppContext();\nvar uid = 0;\nfunction createComponentInstance(vnode, parent, suspense) {\n  const type = vnode.type;\n  const appContext = (parent ? parent.appContext : vnode.appContext) || emptyAppContext;\n  const instance = {\n    uid: uid++,\n    vnode,\n    type,\n    parent,\n    appContext,\n    root: null,\n    // to be immediately set\n    next: null,\n    subTree: null,\n    // will be set synchronously right after creation\n    effect: null,\n    update: null,\n    // will be set synchronously right after creation\n    job: null,\n    scope: new EffectScope(\n      true\n      /* detached */\n    ),\n    render: null,\n    proxy: null,\n    exposed: null,\n    exposeProxy: null,\n    withProxy: null,\n    provides: parent ? parent.provides : Object.create(appContext.provides),\n    ids: parent ? parent.ids : [\"\", 0, 0],\n    accessCache: null,\n    renderCache: [],\n    // local resolved assets\n    components: null,\n    directives: null,\n    // resolved props and emits options\n    propsOptions: normalizePropsOptions(type, appContext),\n    emitsOptions: normalizeEmitsOptions(type, appContext),\n    // emit\n    emit: null,\n    // to be set immediately\n    emitted: null,\n    // props default value\n    propsDefaults: EMPTY_OBJ,\n    // inheritAttrs\n    inheritAttrs: type.inheritAttrs,\n    // state\n    ctx: EMPTY_OBJ,\n    data: EMPTY_OBJ,\n    props: EMPTY_OBJ,\n    attrs: EMPTY_OBJ,\n    slots: EMPTY_OBJ,\n    refs: EMPTY_OBJ,\n    setupState: EMPTY_OBJ,\n    setupContext: null,\n    // suspense related\n    suspense,\n    suspenseId: suspense ? suspense.pendingId : 0,\n    asyncDep: null,\n    asyncResolved: false,\n    // lifecycle hooks\n    // not using enums here because it results in computed properties\n    isMounted: false,\n    isUnmounted: false,\n    isDeactivated: false,\n    bc: null,\n    c: null,\n    bm: null,\n    m: null,\n    bu: null,\n    u: null,\n    um: null,\n    bum: null,\n    da: null,\n    a: null,\n    rtg: null,\n    rtc: null,\n    ec: null,\n    sp: null\n  };\n  if (true) {\n    instance.ctx = createDevRenderContext(instance);\n  } else {\n    instance.ctx = { _: instance };\n  }\n  instance.root = parent ? parent.root : instance;\n  instance.emit = emit.bind(null, instance);\n  if (vnode.ce) {\n    vnode.ce(instance);\n  }\n  return instance;\n}\nvar currentInstance = null;\nvar getCurrentInstance = () => currentInstance || currentRenderingInstance;\nvar internalSetCurrentInstance;\nvar setInSSRSetupState;\n{\n  const g = getGlobalThis();\n  const registerGlobalSetter = (key, setter) => {\n    let setters;\n    if (!(setters = g[key])) setters = g[key] = [];\n    setters.push(setter);\n    return (v) => {\n      if (setters.length > 1) setters.forEach((set) => set(v));\n      else setters[0](v);\n    };\n  };\n  internalSetCurrentInstance = registerGlobalSetter(\n    `__VUE_INSTANCE_SETTERS__`,\n    (v) => currentInstance = v\n  );\n  setInSSRSetupState = registerGlobalSetter(\n    `__VUE_SSR_SETTERS__`,\n    (v) => isInSSRComponentSetup = v\n  );\n}\nvar setCurrentInstance = (instance) => {\n  const prev = currentInstance;\n  internalSetCurrentInstance(instance);\n  instance.scope.on();\n  return () => {\n    instance.scope.off();\n    internalSetCurrentInstance(prev);\n  };\n};\nvar unsetCurrentInstance = () => {\n  currentInstance && currentInstance.scope.off();\n  internalSetCurrentInstance(null);\n};\nvar isBuiltInTag = makeMap(\"slot,component\");\nfunction validateComponentName(name, { isNativeTag }) {\n  if (isBuiltInTag(name) || isNativeTag(name)) {\n    warn$1(\n      \"Do not use built-in or reserved HTML elements as component id: \" + name\n    );\n  }\n}\nfunction isStatefulComponent(instance) {\n  return instance.vnode.shapeFlag & 4;\n}\nvar isInSSRComponentSetup = false;\nfunction setupComponent(instance, isSSR = false, optimized = false) {\n  isSSR && setInSSRSetupState(isSSR);\n  const { props, children } = instance.vnode;\n  const isStateful = isStatefulComponent(instance);\n  initProps(instance, props, isStateful, isSSR);\n  initSlots(instance, children, optimized);\n  const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : void 0;\n  isSSR && setInSSRSetupState(false);\n  return setupResult;\n}\nfunction setupStatefulComponent(instance, isSSR) {\n  var _a;\n  const Component = instance.type;\n  if (true) {\n    if (Component.name) {\n      validateComponentName(Component.name, instance.appContext.config);\n    }\n    if (Component.components) {\n      const names = Object.keys(Component.components);\n      for (let i = 0; i < names.length; i++) {\n        validateComponentName(names[i], instance.appContext.config);\n      }\n    }\n    if (Component.directives) {\n      const names = Object.keys(Component.directives);\n      for (let i = 0; i < names.length; i++) {\n        validateDirectiveName(names[i]);\n      }\n    }\n    if (Component.compilerOptions && isRuntimeOnly()) {\n      warn$1(\n        `\"compilerOptions\" is only supported when using a build of Vue that includes the runtime compiler. Since you are using a runtime-only build, the options should be passed via your build tool config instead.`\n      );\n    }\n  }\n  instance.accessCache = /* @__PURE__ */ Object.create(null);\n  instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);\n  if (true) {\n    exposePropsOnRenderContext(instance);\n  }\n  const { setup } = Component;\n  if (setup) {\n    pauseTracking();\n    const setupContext = instance.setupContext = setup.length > 1 ? createSetupContext(instance) : null;\n    const reset = setCurrentInstance(instance);\n    const setupResult = callWithErrorHandling(\n      setup,\n      instance,\n      0,\n      [\n        true ? shallowReadonly(instance.props) : instance.props,\n        setupContext\n      ]\n    );\n    const isAsyncSetup = isPromise(setupResult);\n    resetTracking();\n    reset();\n    if ((isAsyncSetup || instance.sp) && !isAsyncWrapper(instance)) {\n      markAsyncBoundary(instance);\n    }\n    if (isAsyncSetup) {\n      setupResult.then(unsetCurrentInstance, unsetCurrentInstance);\n      if (isSSR) {\n        return setupResult.then((resolvedResult) => {\n          handleSetupResult(instance, resolvedResult, isSSR);\n        }).catch((e) => {\n          handleError(e, instance, 0);\n        });\n      } else {\n        instance.asyncDep = setupResult;\n        if (!instance.suspense) {\n          const name = (_a = Component.name) != null ? _a : \"Anonymous\";\n          warn$1(\n            `Component <${name}>: setup function returned a promise, but no <Suspense> boundary was found in the parent component tree. A component with async setup() must be nested in a <Suspense> in order to be rendered.`\n          );\n        }\n      }\n    } else {\n      handleSetupResult(instance, setupResult, isSSR);\n    }\n  } else {\n    finishComponentSetup(instance, isSSR);\n  }\n}\nfunction handleSetupResult(instance, setupResult, isSSR) {\n  if (isFunction(setupResult)) {\n    if (instance.type.__ssrInlineRender) {\n      instance.ssrRender = setupResult;\n    } else {\n      instance.render = setupResult;\n    }\n  } else if (isObject(setupResult)) {\n    if (isVNode(setupResult)) {\n      warn$1(\n        `setup() should not return VNodes directly - return a render function instead.`\n      );\n    }\n    if (true) {\n      instance.devtoolsRawSetupState = setupResult;\n    }\n    instance.setupState = proxyRefs(setupResult);\n    if (true) {\n      exposeSetupStateOnRenderContext(instance);\n    }\n  } else if (setupResult !== void 0) {\n    warn$1(\n      `setup() should return an object. Received: ${setupResult === null ? \"null\" : typeof setupResult}`\n    );\n  }\n  finishComponentSetup(instance, isSSR);\n}\nvar compile;\nvar installWithProxy;\nfunction registerRuntimeCompiler(_compile) {\n  compile = _compile;\n  installWithProxy = (i) => {\n    if (i.render._rc) {\n      i.withProxy = new Proxy(i.ctx, RuntimeCompiledPublicInstanceProxyHandlers);\n    }\n  };\n}\nvar isRuntimeOnly = () => !compile;\nfunction finishComponentSetup(instance, isSSR, skipOptions) {\n  const Component = instance.type;\n  if (!instance.render) {\n    if (!isSSR && compile && !Component.render) {\n      const template = Component.template || __VUE_OPTIONS_API__ && resolveMergedOptions(instance).template;\n      if (template) {\n        if (true) {\n          startMeasure(instance, `compile`);\n        }\n        const { isCustomElement, compilerOptions } = instance.appContext.config;\n        const { delimiters, compilerOptions: componentCompilerOptions } = Component;\n        const finalCompilerOptions = extend(\n          extend(\n            {\n              isCustomElement,\n              delimiters\n            },\n            compilerOptions\n          ),\n          componentCompilerOptions\n        );\n        Component.render = compile(template, finalCompilerOptions);\n        if (true) {\n          endMeasure(instance, `compile`);\n        }\n      }\n    }\n    instance.render = Component.render || NOOP;\n    if (installWithProxy) {\n      installWithProxy(instance);\n    }\n  }\n  if (__VUE_OPTIONS_API__ && true) {\n    const reset = setCurrentInstance(instance);\n    pauseTracking();\n    try {\n      applyOptions(instance);\n    } finally {\n      resetTracking();\n      reset();\n    }\n  }\n  if (!Component.render && instance.render === NOOP && !isSSR) {\n    if (!compile && Component.template) {\n      warn$1(\n        `Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias \"vue\" to \"vue/dist/vue.esm-bundler.js\".`\n      );\n    } else {\n      warn$1(`Component is missing template or render function: `, Component);\n    }\n  }\n}\nvar attrsProxyHandlers = true ? {\n  get(target, key) {\n    markAttrsAccessed();\n    track(target, \"get\", \"\");\n    return target[key];\n  },\n  set() {\n    warn$1(`setupContext.attrs is readonly.`);\n    return false;\n  },\n  deleteProperty() {\n    warn$1(`setupContext.attrs is readonly.`);\n    return false;\n  }\n} : {\n  get(target, key) {\n    track(target, \"get\", \"\");\n    return target[key];\n  }\n};\nfunction getSlotsProxy(instance) {\n  return new Proxy(instance.slots, {\n    get(target, key) {\n      track(instance, \"get\", \"$slots\");\n      return target[key];\n    }\n  });\n}\nfunction createSetupContext(instance) {\n  const expose = (exposed) => {\n    if (true) {\n      if (instance.exposed) {\n        warn$1(`expose() should be called only once per setup().`);\n      }\n      if (exposed != null) {\n        let exposedType = typeof exposed;\n        if (exposedType === \"object\") {\n          if (isArray(exposed)) {\n            exposedType = \"array\";\n          } else if (isRef2(exposed)) {\n            exposedType = \"ref\";\n          }\n        }\n        if (exposedType !== \"object\") {\n          warn$1(\n            `expose() should be passed a plain object, received ${exposedType}.`\n          );\n        }\n      }\n    }\n    instance.exposed = exposed || {};\n  };\n  if (true) {\n    let attrsProxy;\n    let slotsProxy;\n    return Object.freeze({\n      get attrs() {\n        return attrsProxy || (attrsProxy = new Proxy(instance.attrs, attrsProxyHandlers));\n      },\n      get slots() {\n        return slotsProxy || (slotsProxy = getSlotsProxy(instance));\n      },\n      get emit() {\n        return (event, ...args) => instance.emit(event, ...args);\n      },\n      expose\n    });\n  } else {\n    return {\n      attrs: new Proxy(instance.attrs, attrsProxyHandlers),\n      slots: instance.slots,\n      emit: instance.emit,\n      expose\n    };\n  }\n}\nfunction getComponentPublicInstance(instance) {\n  if (instance.exposed) {\n    return instance.exposeProxy || (instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {\n      get(target, key) {\n        if (key in target) {\n          return target[key];\n        } else if (key in publicPropertiesMap) {\n          return publicPropertiesMap[key](instance);\n        }\n      },\n      has(target, key) {\n        return key in target || key in publicPropertiesMap;\n      }\n    }));\n  } else {\n    return instance.proxy;\n  }\n}\nvar classifyRE = /(?:^|[-_])(\\w)/g;\nvar classify = (str) => str.replace(classifyRE, (c) => c.toUpperCase()).replace(/[-_]/g, \"\");\nfunction getComponentName(Component, includeInferred = true) {\n  return isFunction(Component) ? Component.displayName || Component.name : Component.name || includeInferred && Component.__name;\n}\nfunction formatComponentName(instance, Component, isRoot = false) {\n  let name = getComponentName(Component);\n  if (!name && Component.__file) {\n    const match = Component.__file.match(/([^/\\\\]+)\\.\\w+$/);\n    if (match) {\n      name = match[1];\n    }\n  }\n  if (!name && instance && instance.parent) {\n    const inferFromRegistry = (registry) => {\n      for (const key in registry) {\n        if (registry[key] === Component) {\n          return key;\n        }\n      }\n    };\n    name = inferFromRegistry(\n      instance.components || instance.parent.type.components\n    ) || inferFromRegistry(instance.appContext.components);\n  }\n  return name ? classify(name) : isRoot ? `App` : `Anonymous`;\n}\nfunction isClassComponent(value) {\n  return isFunction(value) && \"__vccOpts\" in value;\n}\nvar computed2 = (getterOrOptions, debugOptions) => {\n  const c = computed(getterOrOptions, debugOptions, isInSSRComponentSetup);\n  if (true) {\n    const i = getCurrentInstance();\n    if (i && i.appContext.config.warnRecursiveComputed) {\n      c._warnRecursive = true;\n    }\n  }\n  return c;\n};\nfunction h(type, propsOrChildren, children) {\n  const l = arguments.length;\n  if (l === 2) {\n    if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {\n      if (isVNode(propsOrChildren)) {\n        return createVNode(type, null, [propsOrChildren]);\n      }\n      return createVNode(type, propsOrChildren);\n    } else {\n      return createVNode(type, null, propsOrChildren);\n    }\n  } else {\n    if (l > 3) {\n      children = Array.prototype.slice.call(arguments, 2);\n    } else if (l === 3 && isVNode(children)) {\n      children = [children];\n    }\n    return createVNode(type, propsOrChildren, children);\n  }\n}\nfunction initCustomFormatter() {\n  if (typeof window === \"undefined\") {\n    return;\n  }\n  const vueStyle = { style: \"color:#3ba776\" };\n  const numberStyle = { style: \"color:#1677ff\" };\n  const stringStyle = { style: \"color:#f5222d\" };\n  const keywordStyle = { style: \"color:#eb2f96\" };\n  const formatter = {\n    __vue_custom_formatter: true,\n    header(obj) {\n      if (!isObject(obj)) {\n        return null;\n      }\n      if (obj.__isVue) {\n        return [\"div\", vueStyle, `VueInstance`];\n      } else if (isRef2(obj)) {\n        return [\n          \"div\",\n          {},\n          [\"span\", vueStyle, genRefFlag(obj)],\n          \"<\",\n          // avoid debugger accessing value affecting behavior\n          formatValue(\"_value\" in obj ? obj._value : obj),\n          `>`\n        ];\n      } else if (isReactive(obj)) {\n        return [\n          \"div\",\n          {},\n          [\"span\", vueStyle, isShallow(obj) ? \"ShallowReactive\" : \"Reactive\"],\n          \"<\",\n          formatValue(obj),\n          `>${isReadonly(obj) ? ` (readonly)` : ``}`\n        ];\n      } else if (isReadonly(obj)) {\n        return [\n          \"div\",\n          {},\n          [\"span\", vueStyle, isShallow(obj) ? \"ShallowReadonly\" : \"Readonly\"],\n          \"<\",\n          formatValue(obj),\n          \">\"\n        ];\n      }\n      return null;\n    },\n    hasBody(obj) {\n      return obj && obj.__isVue;\n    },\n    body(obj) {\n      if (obj && obj.__isVue) {\n        return [\n          \"div\",\n          {},\n          ...formatInstance(obj.$)\n        ];\n      }\n    }\n  };\n  function formatInstance(instance) {\n    const blocks = [];\n    if (instance.type.props && instance.props) {\n      blocks.push(createInstanceBlock(\"props\", toRaw(instance.props)));\n    }\n    if (instance.setupState !== EMPTY_OBJ) {\n      blocks.push(createInstanceBlock(\"setup\", instance.setupState));\n    }\n    if (instance.data !== EMPTY_OBJ) {\n      blocks.push(createInstanceBlock(\"data\", toRaw(instance.data)));\n    }\n    const computed3 = extractKeys(instance, \"computed\");\n    if (computed3) {\n      blocks.push(createInstanceBlock(\"computed\", computed3));\n    }\n    const injected = extractKeys(instance, \"inject\");\n    if (injected) {\n      blocks.push(createInstanceBlock(\"injected\", injected));\n    }\n    blocks.push([\n      \"div\",\n      {},\n      [\n        \"span\",\n        {\n          style: keywordStyle.style + \";opacity:0.66\"\n        },\n        \"$ (internal): \"\n      ],\n      [\"object\", { object: instance }]\n    ]);\n    return blocks;\n  }\n  function createInstanceBlock(type, target) {\n    target = extend({}, target);\n    if (!Object.keys(target).length) {\n      return [\"span\", {}];\n    }\n    return [\n      \"div\",\n      { style: \"line-height:1.25em;margin-bottom:0.6em\" },\n      [\n        \"div\",\n        {\n          style: \"color:#476582\"\n        },\n        type\n      ],\n      [\n        \"div\",\n        {\n          style: \"padding-left:1.25em\"\n        },\n        ...Object.keys(target).map((key) => {\n          return [\n            \"div\",\n            {},\n            [\"span\", keywordStyle, key + \": \"],\n            formatValue(target[key], false)\n          ];\n        })\n      ]\n    ];\n  }\n  function formatValue(v, asRaw = true) {\n    if (typeof v === \"number\") {\n      return [\"span\", numberStyle, v];\n    } else if (typeof v === \"string\") {\n      return [\"span\", stringStyle, JSON.stringify(v)];\n    } else if (typeof v === \"boolean\") {\n      return [\"span\", keywordStyle, v];\n    } else if (isObject(v)) {\n      return [\"object\", { object: asRaw ? toRaw(v) : v }];\n    } else {\n      return [\"span\", stringStyle, String(v)];\n    }\n  }\n  function extractKeys(instance, type) {\n    const Comp = instance.type;\n    if (isFunction(Comp)) {\n      return;\n    }\n    const extracted = {};\n    for (const key in instance.ctx) {\n      if (isKeyOfType(Comp, key, type)) {\n        extracted[key] = instance.ctx[key];\n      }\n    }\n    return extracted;\n  }\n  function isKeyOfType(Comp, key, type) {\n    const opts = Comp[type];\n    if (isArray(opts) && opts.includes(key) || isObject(opts) && key in opts) {\n      return true;\n    }\n    if (Comp.extends && isKeyOfType(Comp.extends, key, type)) {\n      return true;\n    }\n    if (Comp.mixins && Comp.mixins.some((m) => isKeyOfType(m, key, type))) {\n      return true;\n    }\n  }\n  function genRefFlag(v) {\n    if (isShallow(v)) {\n      return `ShallowRef`;\n    }\n    if (v.effect) {\n      return `ComputedRef`;\n    }\n    return `Ref`;\n  }\n  if (window.devtoolsFormatters) {\n    window.devtoolsFormatters.push(formatter);\n  } else {\n    window.devtoolsFormatters = [formatter];\n  }\n}\nfunction withMemo(memo, render2, cache, index) {\n  const cached = cache[index];\n  if (cached && isMemoSame(cached, memo)) {\n    return cached;\n  }\n  const ret = render2();\n  ret.memo = memo.slice();\n  ret.cacheIndex = index;\n  return cache[index] = ret;\n}\nfunction isMemoSame(cached, memo) {\n  const prev = cached.memo;\n  if (prev.length != memo.length) {\n    return false;\n  }\n  for (let i = 0; i < prev.length; i++) {\n    if (hasChanged(prev[i], memo[i])) {\n      return false;\n    }\n  }\n  if (isBlockTreeEnabled > 0 && currentBlock) {\n    currentBlock.push(cached);\n  }\n  return true;\n}\nvar version = \"3.5.13\";\nvar warn2 = true ? warn$1 : NOOP;\nvar ErrorTypeStrings = ErrorTypeStrings$1;\nvar devtools = true ? devtools$1 : void 0;\nvar setDevtoolsHook = true ? setDevtoolsHook$1 : NOOP;\nvar _ssrUtils = {\n  createComponentInstance,\n  setupComponent,\n  renderComponentRoot,\n  setCurrentRenderingInstance,\n  isVNode,\n  normalizeVNode,\n  getComponentPublicInstance,\n  ensureValidVNode,\n  pushWarningContext,\n  popWarningContext\n};\nvar ssrUtils = _ssrUtils;\nvar resolveFilter = null;\nvar compatUtils = null;\nvar DeprecationTypes = null;\n\n// node_modules/.pnpm/@vue+runtime-dom@3.5.13/node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js\nvar policy = void 0;\nvar tt = typeof window !== \"undefined\" && window.trustedTypes;\nif (tt) {\n  try {\n    policy = tt.createPolicy(\"vue\", {\n      createHTML: (val) => val\n    });\n  } catch (e) {\n    warn2(`Error creating trusted types policy: ${e}`);\n  }\n}\nvar unsafeToTrustedHTML = policy ? (val) => policy.createHTML(val) : (val) => val;\nvar svgNS = \"http://www.w3.org/2000/svg\";\nvar mathmlNS = \"http://www.w3.org/1998/Math/MathML\";\nvar doc = typeof document !== \"undefined\" ? document : null;\nvar templateContainer = doc && doc.createElement(\"template\");\nvar nodeOps = {\n  insert: (child, parent, anchor) => {\n    parent.insertBefore(child, anchor || null);\n  },\n  remove: (child) => {\n    const parent = child.parentNode;\n    if (parent) {\n      parent.removeChild(child);\n    }\n  },\n  createElement: (tag, namespace, is, props) => {\n    const el = namespace === \"svg\" ? doc.createElementNS(svgNS, tag) : namespace === \"mathml\" ? doc.createElementNS(mathmlNS, tag) : is ? doc.createElement(tag, { is }) : doc.createElement(tag);\n    if (tag === \"select\" && props && props.multiple != null) {\n      el.setAttribute(\"multiple\", props.multiple);\n    }\n    return el;\n  },\n  createText: (text) => doc.createTextNode(text),\n  createComment: (text) => doc.createComment(text),\n  setText: (node, text) => {\n    node.nodeValue = text;\n  },\n  setElementText: (el, text) => {\n    el.textContent = text;\n  },\n  parentNode: (node) => node.parentNode,\n  nextSibling: (node) => node.nextSibling,\n  querySelector: (selector) => doc.querySelector(selector),\n  setScopeId(el, id) {\n    el.setAttribute(id, \"\");\n  },\n  // __UNSAFE__\n  // Reason: innerHTML.\n  // Static content here can only come from compiled templates.\n  // As long as the user only uses trusted templates, this is safe.\n  insertStaticContent(content, parent, anchor, namespace, start, end) {\n    const before = anchor ? anchor.previousSibling : parent.lastChild;\n    if (start && (start === end || start.nextSibling)) {\n      while (true) {\n        parent.insertBefore(start.cloneNode(true), anchor);\n        if (start === end || !(start = start.nextSibling)) break;\n      }\n    } else {\n      templateContainer.innerHTML = unsafeToTrustedHTML(\n        namespace === \"svg\" ? `<svg>${content}</svg>` : namespace === \"mathml\" ? `<math>${content}</math>` : content\n      );\n      const template = templateContainer.content;\n      if (namespace === \"svg\" || namespace === \"mathml\") {\n        const wrapper = template.firstChild;\n        while (wrapper.firstChild) {\n          template.appendChild(wrapper.firstChild);\n        }\n        template.removeChild(wrapper);\n      }\n      parent.insertBefore(template, anchor);\n    }\n    return [\n      // first\n      before ? before.nextSibling : parent.firstChild,\n      // last\n      anchor ? anchor.previousSibling : parent.lastChild\n    ];\n  }\n};\nvar TRANSITION = \"transition\";\nvar ANIMATION = \"animation\";\nvar vtcKey = Symbol(\"_vtc\");\nvar DOMTransitionPropsValidators = {\n  name: String,\n  type: String,\n  css: {\n    type: Boolean,\n    default: true\n  },\n  duration: [String, Number, Object],\n  enterFromClass: String,\n  enterActiveClass: String,\n  enterToClass: String,\n  appearFromClass: String,\n  appearActiveClass: String,\n  appearToClass: String,\n  leaveFromClass: String,\n  leaveActiveClass: String,\n  leaveToClass: String\n};\nvar TransitionPropsValidators = extend(\n  {},\n  BaseTransitionPropsValidators,\n  DOMTransitionPropsValidators\n);\nvar decorate$1 = (t) => {\n  t.displayName = \"Transition\";\n  t.props = TransitionPropsValidators;\n  return t;\n};\nvar Transition = decorate$1(\n  (props, { slots }) => h(BaseTransition, resolveTransitionProps(props), slots)\n);\nvar callHook2 = (hook, args = []) => {\n  if (isArray(hook)) {\n    hook.forEach((h2) => h2(...args));\n  } else if (hook) {\n    hook(...args);\n  }\n};\nvar hasExplicitCallback = (hook) => {\n  return hook ? isArray(hook) ? hook.some((h2) => h2.length > 1) : hook.length > 1 : false;\n};\nfunction resolveTransitionProps(rawProps) {\n  const baseProps = {};\n  for (const key in rawProps) {\n    if (!(key in DOMTransitionPropsValidators)) {\n      baseProps[key] = rawProps[key];\n    }\n  }\n  if (rawProps.css === false) {\n    return baseProps;\n  }\n  const {\n    name = \"v\",\n    type,\n    duration,\n    enterFromClass = `${name}-enter-from`,\n    enterActiveClass = `${name}-enter-active`,\n    enterToClass = `${name}-enter-to`,\n    appearFromClass = enterFromClass,\n    appearActiveClass = enterActiveClass,\n    appearToClass = enterToClass,\n    leaveFromClass = `${name}-leave-from`,\n    leaveActiveClass = `${name}-leave-active`,\n    leaveToClass = `${name}-leave-to`\n  } = rawProps;\n  const durations = normalizeDuration(duration);\n  const enterDuration = durations && durations[0];\n  const leaveDuration = durations && durations[1];\n  const {\n    onBeforeEnter,\n    onEnter,\n    onEnterCancelled,\n    onLeave,\n    onLeaveCancelled,\n    onBeforeAppear = onBeforeEnter,\n    onAppear = onEnter,\n    onAppearCancelled = onEnterCancelled\n  } = baseProps;\n  const finishEnter = (el, isAppear, done, isCancelled) => {\n    el._enterCancelled = isCancelled;\n    removeTransitionClass(el, isAppear ? appearToClass : enterToClass);\n    removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);\n    done && done();\n  };\n  const finishLeave = (el, done) => {\n    el._isLeaving = false;\n    removeTransitionClass(el, leaveFromClass);\n    removeTransitionClass(el, leaveToClass);\n    removeTransitionClass(el, leaveActiveClass);\n    done && done();\n  };\n  const makeEnterHook = (isAppear) => {\n    return (el, done) => {\n      const hook = isAppear ? onAppear : onEnter;\n      const resolve2 = () => finishEnter(el, isAppear, done);\n      callHook2(hook, [el, resolve2]);\n      nextFrame(() => {\n        removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass);\n        addTransitionClass(el, isAppear ? appearToClass : enterToClass);\n        if (!hasExplicitCallback(hook)) {\n          whenTransitionEnds(el, type, enterDuration, resolve2);\n        }\n      });\n    };\n  };\n  return extend(baseProps, {\n    onBeforeEnter(el) {\n      callHook2(onBeforeEnter, [el]);\n      addTransitionClass(el, enterFromClass);\n      addTransitionClass(el, enterActiveClass);\n    },\n    onBeforeAppear(el) {\n      callHook2(onBeforeAppear, [el]);\n      addTransitionClass(el, appearFromClass);\n      addTransitionClass(el, appearActiveClass);\n    },\n    onEnter: makeEnterHook(false),\n    onAppear: makeEnterHook(true),\n    onLeave(el, done) {\n      el._isLeaving = true;\n      const resolve2 = () => finishLeave(el, done);\n      addTransitionClass(el, leaveFromClass);\n      if (!el._enterCancelled) {\n        forceReflow();\n        addTransitionClass(el, leaveActiveClass);\n      } else {\n        addTransitionClass(el, leaveActiveClass);\n        forceReflow();\n      }\n      nextFrame(() => {\n        if (!el._isLeaving) {\n          return;\n        }\n        removeTransitionClass(el, leaveFromClass);\n        addTransitionClass(el, leaveToClass);\n        if (!hasExplicitCallback(onLeave)) {\n          whenTransitionEnds(el, type, leaveDuration, resolve2);\n        }\n      });\n      callHook2(onLeave, [el, resolve2]);\n    },\n    onEnterCancelled(el) {\n      finishEnter(el, false, void 0, true);\n      callHook2(onEnterCancelled, [el]);\n    },\n    onAppearCancelled(el) {\n      finishEnter(el, true, void 0, true);\n      callHook2(onAppearCancelled, [el]);\n    },\n    onLeaveCancelled(el) {\n      finishLeave(el);\n      callHook2(onLeaveCancelled, [el]);\n    }\n  });\n}\nfunction normalizeDuration(duration) {\n  if (duration == null) {\n    return null;\n  } else if (isObject(duration)) {\n    return [NumberOf(duration.enter), NumberOf(duration.leave)];\n  } else {\n    const n = NumberOf(duration);\n    return [n, n];\n  }\n}\nfunction NumberOf(val) {\n  const res = toNumber(val);\n  if (true) {\n    assertNumber(res, \"<transition> explicit duration\");\n  }\n  return res;\n}\nfunction addTransitionClass(el, cls) {\n  cls.split(/\\s+/).forEach((c) => c && el.classList.add(c));\n  (el[vtcKey] || (el[vtcKey] = /* @__PURE__ */ new Set())).add(cls);\n}\nfunction removeTransitionClass(el, cls) {\n  cls.split(/\\s+/).forEach((c) => c && el.classList.remove(c));\n  const _vtc = el[vtcKey];\n  if (_vtc) {\n    _vtc.delete(cls);\n    if (!_vtc.size) {\n      el[vtcKey] = void 0;\n    }\n  }\n}\nfunction nextFrame(cb) {\n  requestAnimationFrame(() => {\n    requestAnimationFrame(cb);\n  });\n}\nvar endId = 0;\nfunction whenTransitionEnds(el, expectedType, explicitTimeout, resolve2) {\n  const id = el._endId = ++endId;\n  const resolveIfNotStale = () => {\n    if (id === el._endId) {\n      resolve2();\n    }\n  };\n  if (explicitTimeout != null) {\n    return setTimeout(resolveIfNotStale, explicitTimeout);\n  }\n  const { type, timeout, propCount } = getTransitionInfo(el, expectedType);\n  if (!type) {\n    return resolve2();\n  }\n  const endEvent = type + \"end\";\n  let ended = 0;\n  const end = () => {\n    el.removeEventListener(endEvent, onEnd);\n    resolveIfNotStale();\n  };\n  const onEnd = (e) => {\n    if (e.target === el && ++ended >= propCount) {\n      end();\n    }\n  };\n  setTimeout(() => {\n    if (ended < propCount) {\n      end();\n    }\n  }, timeout + 1);\n  el.addEventListener(endEvent, onEnd);\n}\nfunction getTransitionInfo(el, expectedType) {\n  const styles = window.getComputedStyle(el);\n  const getStyleProperties = (key) => (styles[key] || \"\").split(\", \");\n  const transitionDelays = getStyleProperties(`${TRANSITION}Delay`);\n  const transitionDurations = getStyleProperties(`${TRANSITION}Duration`);\n  const transitionTimeout = getTimeout(transitionDelays, transitionDurations);\n  const animationDelays = getStyleProperties(`${ANIMATION}Delay`);\n  const animationDurations = getStyleProperties(`${ANIMATION}Duration`);\n  const animationTimeout = getTimeout(animationDelays, animationDurations);\n  let type = null;\n  let timeout = 0;\n  let propCount = 0;\n  if (expectedType === TRANSITION) {\n    if (transitionTimeout > 0) {\n      type = TRANSITION;\n      timeout = transitionTimeout;\n      propCount = transitionDurations.length;\n    }\n  } else if (expectedType === ANIMATION) {\n    if (animationTimeout > 0) {\n      type = ANIMATION;\n      timeout = animationTimeout;\n      propCount = animationDurations.length;\n    }\n  } else {\n    timeout = Math.max(transitionTimeout, animationTimeout);\n    type = timeout > 0 ? transitionTimeout > animationTimeout ? TRANSITION : ANIMATION : null;\n    propCount = type ? type === TRANSITION ? transitionDurations.length : animationDurations.length : 0;\n  }\n  const hasTransform = type === TRANSITION && /\\b(transform|all)(,|$)/.test(\n    getStyleProperties(`${TRANSITION}Property`).toString()\n  );\n  return {\n    type,\n    timeout,\n    propCount,\n    hasTransform\n  };\n}\nfunction getTimeout(delays, durations) {\n  while (delays.length < durations.length) {\n    delays = delays.concat(delays);\n  }\n  return Math.max(...durations.map((d, i) => toMs(d) + toMs(delays[i])));\n}\nfunction toMs(s) {\n  if (s === \"auto\") return 0;\n  return Number(s.slice(0, -1).replace(\",\", \".\")) * 1e3;\n}\nfunction forceReflow() {\n  return document.body.offsetHeight;\n}\nfunction patchClass(el, value, isSVG) {\n  const transitionClasses = el[vtcKey];\n  if (transitionClasses) {\n    value = (value ? [value, ...transitionClasses] : [...transitionClasses]).join(\" \");\n  }\n  if (value == null) {\n    el.removeAttribute(\"class\");\n  } else if (isSVG) {\n    el.setAttribute(\"class\", value);\n  } else {\n    el.className = value;\n  }\n}\nvar vShowOriginalDisplay = Symbol(\"_vod\");\nvar vShowHidden = Symbol(\"_vsh\");\nvar vShow = {\n  beforeMount(el, { value }, { transition }) {\n    el[vShowOriginalDisplay] = el.style.display === \"none\" ? \"\" : el.style.display;\n    if (transition && value) {\n      transition.beforeEnter(el);\n    } else {\n      setDisplay(el, value);\n    }\n  },\n  mounted(el, { value }, { transition }) {\n    if (transition && value) {\n      transition.enter(el);\n    }\n  },\n  updated(el, { value, oldValue }, { transition }) {\n    if (!value === !oldValue) return;\n    if (transition) {\n      if (value) {\n        transition.beforeEnter(el);\n        setDisplay(el, true);\n        transition.enter(el);\n      } else {\n        transition.leave(el, () => {\n          setDisplay(el, false);\n        });\n      }\n    } else {\n      setDisplay(el, value);\n    }\n  },\n  beforeUnmount(el, { value }) {\n    setDisplay(el, value);\n  }\n};\nif (true) {\n  vShow.name = \"show\";\n}\nfunction setDisplay(el, value) {\n  el.style.display = value ? el[vShowOriginalDisplay] : \"none\";\n  el[vShowHidden] = !value;\n}\nfunction initVShowForSSR() {\n  vShow.getSSRProps = ({ value }) => {\n    if (!value) {\n      return { style: { display: \"none\" } };\n    }\n  };\n}\nvar CSS_VAR_TEXT = Symbol(true ? \"CSS_VAR_TEXT\" : \"\");\nfunction useCssVars(getter) {\n  const instance = getCurrentInstance();\n  if (!instance) {\n    warn2(`useCssVars is called without current active component instance.`);\n    return;\n  }\n  const updateTeleports = instance.ut = (vars = getter(instance.proxy)) => {\n    Array.from(\n      document.querySelectorAll(`[data-v-owner=\"${instance.uid}\"]`)\n    ).forEach((node) => setVarsOnNode(node, vars));\n  };\n  if (true) {\n    instance.getCssVars = () => getter(instance.proxy);\n  }\n  const setVars = () => {\n    const vars = getter(instance.proxy);\n    if (instance.ce) {\n      setVarsOnNode(instance.ce, vars);\n    } else {\n      setVarsOnVNode(instance.subTree, vars);\n    }\n    updateTeleports(vars);\n  };\n  onBeforeUpdate(() => {\n    queuePostFlushCb(setVars);\n  });\n  onMounted(() => {\n    watch2(setVars, NOOP, { flush: \"post\" });\n    const ob = new MutationObserver(setVars);\n    ob.observe(instance.subTree.el.parentNode, { childList: true });\n    onUnmounted(() => ob.disconnect());\n  });\n}\nfunction setVarsOnVNode(vnode, vars) {\n  if (vnode.shapeFlag & 128) {\n    const suspense = vnode.suspense;\n    vnode = suspense.activeBranch;\n    if (suspense.pendingBranch && !suspense.isHydrating) {\n      suspense.effects.push(() => {\n        setVarsOnVNode(suspense.activeBranch, vars);\n      });\n    }\n  }\n  while (vnode.component) {\n    vnode = vnode.component.subTree;\n  }\n  if (vnode.shapeFlag & 1 && vnode.el) {\n    setVarsOnNode(vnode.el, vars);\n  } else if (vnode.type === Fragment) {\n    vnode.children.forEach((c) => setVarsOnVNode(c, vars));\n  } else if (vnode.type === Static) {\n    let { el, anchor } = vnode;\n    while (el) {\n      setVarsOnNode(el, vars);\n      if (el === anchor) break;\n      el = el.nextSibling;\n    }\n  }\n}\nfunction setVarsOnNode(el, vars) {\n  if (el.nodeType === 1) {\n    const style = el.style;\n    let cssText = \"\";\n    for (const key in vars) {\n      style.setProperty(`--${key}`, vars[key]);\n      cssText += `--${key}: ${vars[key]};`;\n    }\n    style[CSS_VAR_TEXT] = cssText;\n  }\n}\nvar displayRE = /(^|;)\\s*display\\s*:/;\nfunction patchStyle(el, prev, next) {\n  const style = el.style;\n  const isCssString = isString(next);\n  let hasControlledDisplay = false;\n  if (next && !isCssString) {\n    if (prev) {\n      if (!isString(prev)) {\n        for (const key in prev) {\n          if (next[key] == null) {\n            setStyle(style, key, \"\");\n          }\n        }\n      } else {\n        for (const prevStyle of prev.split(\";\")) {\n          const key = prevStyle.slice(0, prevStyle.indexOf(\":\")).trim();\n          if (next[key] == null) {\n            setStyle(style, key, \"\");\n          }\n        }\n      }\n    }\n    for (const key in next) {\n      if (key === \"display\") {\n        hasControlledDisplay = true;\n      }\n      setStyle(style, key, next[key]);\n    }\n  } else {\n    if (isCssString) {\n      if (prev !== next) {\n        const cssVarText = style[CSS_VAR_TEXT];\n        if (cssVarText) {\n          next += \";\" + cssVarText;\n        }\n        style.cssText = next;\n        hasControlledDisplay = displayRE.test(next);\n      }\n    } else if (prev) {\n      el.removeAttribute(\"style\");\n    }\n  }\n  if (vShowOriginalDisplay in el) {\n    el[vShowOriginalDisplay] = hasControlledDisplay ? style.display : \"\";\n    if (el[vShowHidden]) {\n      style.display = \"none\";\n    }\n  }\n}\nvar semicolonRE = /[^\\\\];\\s*$/;\nvar importantRE = /\\s*!important$/;\nfunction setStyle(style, name, val) {\n  if (isArray(val)) {\n    val.forEach((v) => setStyle(style, name, v));\n  } else {\n    if (val == null) val = \"\";\n    if (true) {\n      if (semicolonRE.test(val)) {\n        warn2(\n          `Unexpected semicolon at the end of '${name}' style value: '${val}'`\n        );\n      }\n    }\n    if (name.startsWith(\"--\")) {\n      style.setProperty(name, val);\n    } else {\n      const prefixed = autoPrefix(style, name);\n      if (importantRE.test(val)) {\n        style.setProperty(\n          hyphenate(prefixed),\n          val.replace(importantRE, \"\"),\n          \"important\"\n        );\n      } else {\n        style[prefixed] = val;\n      }\n    }\n  }\n}\nvar prefixes = [\"Webkit\", \"Moz\", \"ms\"];\nvar prefixCache = {};\nfunction autoPrefix(style, rawName) {\n  const cached = prefixCache[rawName];\n  if (cached) {\n    return cached;\n  }\n  let name = camelize(rawName);\n  if (name !== \"filter\" && name in style) {\n    return prefixCache[rawName] = name;\n  }\n  name = capitalize(name);\n  for (let i = 0; i < prefixes.length; i++) {\n    const prefixed = prefixes[i] + name;\n    if (prefixed in style) {\n      return prefixCache[rawName] = prefixed;\n    }\n  }\n  return rawName;\n}\nvar xlinkNS = \"http://www.w3.org/1999/xlink\";\nfunction patchAttr(el, key, value, isSVG, instance, isBoolean2 = isSpecialBooleanAttr(key)) {\n  if (isSVG && key.startsWith(\"xlink:\")) {\n    if (value == null) {\n      el.removeAttributeNS(xlinkNS, key.slice(6, key.length));\n    } else {\n      el.setAttributeNS(xlinkNS, key, value);\n    }\n  } else {\n    if (value == null || isBoolean2 && !includeBooleanAttr(value)) {\n      el.removeAttribute(key);\n    } else {\n      el.setAttribute(\n        key,\n        isBoolean2 ? \"\" : isSymbol(value) ? String(value) : value\n      );\n    }\n  }\n}\nfunction patchDOMProp(el, key, value, parentComponent, attrName) {\n  if (key === \"innerHTML\" || key === \"textContent\") {\n    if (value != null) {\n      el[key] = key === \"innerHTML\" ? unsafeToTrustedHTML(value) : value;\n    }\n    return;\n  }\n  const tag = el.tagName;\n  if (key === \"value\" && tag !== \"PROGRESS\" && // custom elements may use _value internally\n  !tag.includes(\"-\")) {\n    const oldValue = tag === \"OPTION\" ? el.getAttribute(\"value\") || \"\" : el.value;\n    const newValue = value == null ? (\n      // #11647: value should be set as empty string for null and undefined,\n      // but <input type=\"checkbox\"> should be set as 'on'.\n      el.type === \"checkbox\" ? \"on\" : \"\"\n    ) : String(value);\n    if (oldValue !== newValue || !(\"_value\" in el)) {\n      el.value = newValue;\n    }\n    if (value == null) {\n      el.removeAttribute(key);\n    }\n    el._value = value;\n    return;\n  }\n  let needRemove = false;\n  if (value === \"\" || value == null) {\n    const type = typeof el[key];\n    if (type === \"boolean\") {\n      value = includeBooleanAttr(value);\n    } else if (value == null && type === \"string\") {\n      value = \"\";\n      needRemove = true;\n    } else if (type === \"number\") {\n      value = 0;\n      needRemove = true;\n    }\n  }\n  try {\n    el[key] = value;\n  } catch (e) {\n    if (!needRemove) {\n      warn2(\n        `Failed setting prop \"${key}\" on <${tag.toLowerCase()}>: value ${value} is invalid.`,\n        e\n      );\n    }\n  }\n  needRemove && el.removeAttribute(attrName || key);\n}\nfunction addEventListener(el, event, handler, options) {\n  el.addEventListener(event, handler, options);\n}\nfunction removeEventListener(el, event, handler, options) {\n  el.removeEventListener(event, handler, options);\n}\nvar veiKey = Symbol(\"_vei\");\nfunction patchEvent(el, rawName, prevValue, nextValue, instance = null) {\n  const invokers = el[veiKey] || (el[veiKey] = {});\n  const existingInvoker = invokers[rawName];\n  if (nextValue && existingInvoker) {\n    existingInvoker.value = true ? sanitizeEventValue(nextValue, rawName) : nextValue;\n  } else {\n    const [name, options] = parseName(rawName);\n    if (nextValue) {\n      const invoker = invokers[rawName] = createInvoker(\n        true ? sanitizeEventValue(nextValue, rawName) : nextValue,\n        instance\n      );\n      addEventListener(el, name, invoker, options);\n    } else if (existingInvoker) {\n      removeEventListener(el, name, existingInvoker, options);\n      invokers[rawName] = void 0;\n    }\n  }\n}\nvar optionsModifierRE = /(?:Once|Passive|Capture)$/;\nfunction parseName(name) {\n  let options;\n  if (optionsModifierRE.test(name)) {\n    options = {};\n    let m;\n    while (m = name.match(optionsModifierRE)) {\n      name = name.slice(0, name.length - m[0].length);\n      options[m[0].toLowerCase()] = true;\n    }\n  }\n  const event = name[2] === \":\" ? name.slice(3) : hyphenate(name.slice(2));\n  return [event, options];\n}\nvar cachedNow = 0;\nvar p = Promise.resolve();\nvar getNow = () => cachedNow || (p.then(() => cachedNow = 0), cachedNow = Date.now());\nfunction createInvoker(initialValue, instance) {\n  const invoker = (e) => {\n    if (!e._vts) {\n      e._vts = Date.now();\n    } else if (e._vts <= invoker.attached) {\n      return;\n    }\n    callWithAsyncErrorHandling(\n      patchStopImmediatePropagation(e, invoker.value),\n      instance,\n      5,\n      [e]\n    );\n  };\n  invoker.value = initialValue;\n  invoker.attached = getNow();\n  return invoker;\n}\nfunction sanitizeEventValue(value, propName) {\n  if (isFunction(value) || isArray(value)) {\n    return value;\n  }\n  warn2(\n    `Wrong type passed as event handler to ${propName} - did you forget @ or : in front of your prop?\nExpected function or array of functions, received type ${typeof value}.`\n  );\n  return NOOP;\n}\nfunction patchStopImmediatePropagation(e, value) {\n  if (isArray(value)) {\n    const originalStop = e.stopImmediatePropagation;\n    e.stopImmediatePropagation = () => {\n      originalStop.call(e);\n      e._stopped = true;\n    };\n    return value.map(\n      (fn) => (e2) => !e2._stopped && fn && fn(e2)\n    );\n  } else {\n    return value;\n  }\n}\nvar isNativeOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && // lowercase letter\nkey.charCodeAt(2) > 96 && key.charCodeAt(2) < 123;\nvar patchProp = (el, key, prevValue, nextValue, namespace, parentComponent) => {\n  const isSVG = namespace === \"svg\";\n  if (key === \"class\") {\n    patchClass(el, nextValue, isSVG);\n  } else if (key === \"style\") {\n    patchStyle(el, prevValue, nextValue);\n  } else if (isOn(key)) {\n    if (!isModelListener(key)) {\n      patchEvent(el, key, prevValue, nextValue, parentComponent);\n    }\n  } else if (key[0] === \".\" ? (key = key.slice(1), true) : key[0] === \"^\" ? (key = key.slice(1), false) : shouldSetAsProp(el, key, nextValue, isSVG)) {\n    patchDOMProp(el, key, nextValue);\n    if (!el.tagName.includes(\"-\") && (key === \"value\" || key === \"checked\" || key === \"selected\")) {\n      patchAttr(el, key, nextValue, isSVG, parentComponent, key !== \"value\");\n    }\n  } else if (\n    // #11081 force set props for possible async custom element\n    el._isVueCE && (/[A-Z]/.test(key) || !isString(nextValue))\n  ) {\n    patchDOMProp(el, camelize(key), nextValue, parentComponent, key);\n  } else {\n    if (key === \"true-value\") {\n      el._trueValue = nextValue;\n    } else if (key === \"false-value\") {\n      el._falseValue = nextValue;\n    }\n    patchAttr(el, key, nextValue, isSVG);\n  }\n};\nfunction shouldSetAsProp(el, key, value, isSVG) {\n  if (isSVG) {\n    if (key === \"innerHTML\" || key === \"textContent\") {\n      return true;\n    }\n    if (key in el && isNativeOn(key) && isFunction(value)) {\n      return true;\n    }\n    return false;\n  }\n  if (key === \"spellcheck\" || key === \"draggable\" || key === \"translate\") {\n    return false;\n  }\n  if (key === \"form\") {\n    return false;\n  }\n  if (key === \"list\" && el.tagName === \"INPUT\") {\n    return false;\n  }\n  if (key === \"type\" && el.tagName === \"TEXTAREA\") {\n    return false;\n  }\n  if (key === \"width\" || key === \"height\") {\n    const tag = el.tagName;\n    if (tag === \"IMG\" || tag === \"VIDEO\" || tag === \"CANVAS\" || tag === \"SOURCE\") {\n      return false;\n    }\n  }\n  if (isNativeOn(key) && isString(value)) {\n    return false;\n  }\n  return key in el;\n}\nvar REMOVAL = {};\nfunction defineCustomElement(options, extraOptions, _createApp) {\n  const Comp = defineComponent(options, extraOptions);\n  if (isPlainObject(Comp)) extend(Comp, extraOptions);\n  class VueCustomElement extends VueElement {\n    constructor(initialProps) {\n      super(Comp, initialProps, _createApp);\n    }\n  }\n  VueCustomElement.def = Comp;\n  return VueCustomElement;\n}\nvar defineSSRCustomElement = (options, extraOptions) => {\n  return defineCustomElement(options, extraOptions, createSSRApp);\n};\nvar BaseClass = typeof HTMLElement !== \"undefined\" ? HTMLElement : class {\n};\nvar VueElement = class _VueElement extends BaseClass {\n  constructor(_def, _props = {}, _createApp = createApp) {\n    super();\n    this._def = _def;\n    this._props = _props;\n    this._createApp = _createApp;\n    this._isVueCE = true;\n    this._instance = null;\n    this._app = null;\n    this._nonce = this._def.nonce;\n    this._connected = false;\n    this._resolved = false;\n    this._numberProps = null;\n    this._styleChildren = /* @__PURE__ */ new WeakSet();\n    this._ob = null;\n    if (this.shadowRoot && _createApp !== createApp) {\n      this._root = this.shadowRoot;\n    } else {\n      if (this.shadowRoot) {\n        warn2(\n          `Custom element has pre-rendered declarative shadow root but is not defined as hydratable. Use \\`defineSSRCustomElement\\`.`\n        );\n      }\n      if (_def.shadowRoot !== false) {\n        this.attachShadow({ mode: \"open\" });\n        this._root = this.shadowRoot;\n      } else {\n        this._root = this;\n      }\n    }\n    if (!this._def.__asyncLoader) {\n      this._resolveProps(this._def);\n    }\n  }\n  connectedCallback() {\n    if (!this.isConnected) return;\n    if (!this.shadowRoot) {\n      this._parseSlots();\n    }\n    this._connected = true;\n    let parent = this;\n    while (parent = parent && (parent.parentNode || parent.host)) {\n      if (parent instanceof _VueElement) {\n        this._parent = parent;\n        break;\n      }\n    }\n    if (!this._instance) {\n      if (this._resolved) {\n        this._setParent();\n        this._update();\n      } else {\n        if (parent && parent._pendingResolve) {\n          this._pendingResolve = parent._pendingResolve.then(() => {\n            this._pendingResolve = void 0;\n            this._resolveDef();\n          });\n        } else {\n          this._resolveDef();\n        }\n      }\n    }\n  }\n  _setParent(parent = this._parent) {\n    if (parent) {\n      this._instance.parent = parent._instance;\n      this._instance.provides = parent._instance.provides;\n    }\n  }\n  disconnectedCallback() {\n    this._connected = false;\n    nextTick(() => {\n      if (!this._connected) {\n        if (this._ob) {\n          this._ob.disconnect();\n          this._ob = null;\n        }\n        this._app && this._app.unmount();\n        if (this._instance) this._instance.ce = void 0;\n        this._app = this._instance = null;\n      }\n    });\n  }\n  /**\n   * resolve inner component definition (handle possible async component)\n   */\n  _resolveDef() {\n    if (this._pendingResolve) {\n      return;\n    }\n    for (let i = 0; i < this.attributes.length; i++) {\n      this._setAttr(this.attributes[i].name);\n    }\n    this._ob = new MutationObserver((mutations) => {\n      for (const m of mutations) {\n        this._setAttr(m.attributeName);\n      }\n    });\n    this._ob.observe(this, { attributes: true });\n    const resolve2 = (def2, isAsync = false) => {\n      this._resolved = true;\n      this._pendingResolve = void 0;\n      const { props, styles } = def2;\n      let numberProps;\n      if (props && !isArray(props)) {\n        for (const key in props) {\n          const opt = props[key];\n          if (opt === Number || opt && opt.type === Number) {\n            if (key in this._props) {\n              this._props[key] = toNumber(this._props[key]);\n            }\n            (numberProps || (numberProps = /* @__PURE__ */ Object.create(null)))[camelize(key)] = true;\n          }\n        }\n      }\n      this._numberProps = numberProps;\n      if (isAsync) {\n        this._resolveProps(def2);\n      }\n      if (this.shadowRoot) {\n        this._applyStyles(styles);\n      } else if (styles) {\n        warn2(\n          \"Custom element style injection is not supported when using shadowRoot: false\"\n        );\n      }\n      this._mount(def2);\n    };\n    const asyncDef = this._def.__asyncLoader;\n    if (asyncDef) {\n      this._pendingResolve = asyncDef().then(\n        (def2) => resolve2(this._def = def2, true)\n      );\n    } else {\n      resolve2(this._def);\n    }\n  }\n  _mount(def2) {\n    if (!def2.name) {\n      def2.name = \"VueElement\";\n    }\n    this._app = this._createApp(def2);\n    if (def2.configureApp) {\n      def2.configureApp(this._app);\n    }\n    this._app._ceVNode = this._createVNode();\n    this._app.mount(this._root);\n    const exposed = this._instance && this._instance.exposed;\n    if (!exposed) return;\n    for (const key in exposed) {\n      if (!hasOwn(this, key)) {\n        Object.defineProperty(this, key, {\n          // unwrap ref to be consistent with public instance behavior\n          get: () => unref(exposed[key])\n        });\n      } else if (true) {\n        warn2(`Exposed property \"${key}\" already exists on custom element.`);\n      }\n    }\n  }\n  _resolveProps(def2) {\n    const { props } = def2;\n    const declaredPropKeys = isArray(props) ? props : Object.keys(props || {});\n    for (const key of Object.keys(this)) {\n      if (key[0] !== \"_\" && declaredPropKeys.includes(key)) {\n        this._setProp(key, this[key]);\n      }\n    }\n    for (const key of declaredPropKeys.map(camelize)) {\n      Object.defineProperty(this, key, {\n        get() {\n          return this._getProp(key);\n        },\n        set(val) {\n          this._setProp(key, val, true, true);\n        }\n      });\n    }\n  }\n  _setAttr(key) {\n    if (key.startsWith(\"data-v-\")) return;\n    const has = this.hasAttribute(key);\n    let value = has ? this.getAttribute(key) : REMOVAL;\n    const camelKey = camelize(key);\n    if (has && this._numberProps && this._numberProps[camelKey]) {\n      value = toNumber(value);\n    }\n    this._setProp(camelKey, value, false, true);\n  }\n  /**\n   * @internal\n   */\n  _getProp(key) {\n    return this._props[key];\n  }\n  /**\n   * @internal\n   */\n  _setProp(key, val, shouldReflect = true, shouldUpdate = false) {\n    if (val !== this._props[key]) {\n      if (val === REMOVAL) {\n        delete this._props[key];\n      } else {\n        this._props[key] = val;\n        if (key === \"key\" && this._app) {\n          this._app._ceVNode.key = val;\n        }\n      }\n      if (shouldUpdate && this._instance) {\n        this._update();\n      }\n      if (shouldReflect) {\n        const ob = this._ob;\n        ob && ob.disconnect();\n        if (val === true) {\n          this.setAttribute(hyphenate(key), \"\");\n        } else if (typeof val === \"string\" || typeof val === \"number\") {\n          this.setAttribute(hyphenate(key), val + \"\");\n        } else if (!val) {\n          this.removeAttribute(hyphenate(key));\n        }\n        ob && ob.observe(this, { attributes: true });\n      }\n    }\n  }\n  _update() {\n    render(this._createVNode(), this._root);\n  }\n  _createVNode() {\n    const baseProps = {};\n    if (!this.shadowRoot) {\n      baseProps.onVnodeMounted = baseProps.onVnodeUpdated = this._renderSlots.bind(this);\n    }\n    const vnode = createVNode(this._def, extend(baseProps, this._props));\n    if (!this._instance) {\n      vnode.ce = (instance) => {\n        this._instance = instance;\n        instance.ce = this;\n        instance.isCE = true;\n        if (true) {\n          instance.ceReload = (newStyles) => {\n            if (this._styles) {\n              this._styles.forEach((s) => this._root.removeChild(s));\n              this._styles.length = 0;\n            }\n            this._applyStyles(newStyles);\n            this._instance = null;\n            this._update();\n          };\n        }\n        const dispatch = (event, args) => {\n          this.dispatchEvent(\n            new CustomEvent(\n              event,\n              isPlainObject(args[0]) ? extend({ detail: args }, args[0]) : { detail: args }\n            )\n          );\n        };\n        instance.emit = (event, ...args) => {\n          dispatch(event, args);\n          if (hyphenate(event) !== event) {\n            dispatch(hyphenate(event), args);\n          }\n        };\n        this._setParent();\n      };\n    }\n    return vnode;\n  }\n  _applyStyles(styles, owner) {\n    if (!styles) return;\n    if (owner) {\n      if (owner === this._def || this._styleChildren.has(owner)) {\n        return;\n      }\n      this._styleChildren.add(owner);\n    }\n    const nonce = this._nonce;\n    for (let i = styles.length - 1; i >= 0; i--) {\n      const s = document.createElement(\"style\");\n      if (nonce) s.setAttribute(\"nonce\", nonce);\n      s.textContent = styles[i];\n      this.shadowRoot.prepend(s);\n      if (true) {\n        if (owner) {\n          if (owner.__hmrId) {\n            if (!this._childStyles) this._childStyles = /* @__PURE__ */ new Map();\n            let entry = this._childStyles.get(owner.__hmrId);\n            if (!entry) {\n              this._childStyles.set(owner.__hmrId, entry = []);\n            }\n            entry.push(s);\n          }\n        } else {\n          (this._styles || (this._styles = [])).push(s);\n        }\n      }\n    }\n  }\n  /**\n   * Only called when shadowRoot is false\n   */\n  _parseSlots() {\n    const slots = this._slots = {};\n    let n;\n    while (n = this.firstChild) {\n      const slotName = n.nodeType === 1 && n.getAttribute(\"slot\") || \"default\";\n      (slots[slotName] || (slots[slotName] = [])).push(n);\n      this.removeChild(n);\n    }\n  }\n  /**\n   * Only called when shadowRoot is false\n   */\n  _renderSlots() {\n    const outlets = (this._teleportTarget || this).querySelectorAll(\"slot\");\n    const scopeId = this._instance.type.__scopeId;\n    for (let i = 0; i < outlets.length; i++) {\n      const o = outlets[i];\n      const slotName = o.getAttribute(\"name\") || \"default\";\n      const content = this._slots[slotName];\n      const parent = o.parentNode;\n      if (content) {\n        for (const n of content) {\n          if (scopeId && n.nodeType === 1) {\n            const id = scopeId + \"-s\";\n            const walker = document.createTreeWalker(n, 1);\n            n.setAttribute(id, \"\");\n            let child;\n            while (child = walker.nextNode()) {\n              child.setAttribute(id, \"\");\n            }\n          }\n          parent.insertBefore(n, o);\n        }\n      } else {\n        while (o.firstChild) parent.insertBefore(o.firstChild, o);\n      }\n      parent.removeChild(o);\n    }\n  }\n  /**\n   * @internal\n   */\n  _injectChildStyle(comp) {\n    this._applyStyles(comp.styles, comp);\n  }\n  /**\n   * @internal\n   */\n  _removeChildStyle(comp) {\n    if (true) {\n      this._styleChildren.delete(comp);\n      if (this._childStyles && comp.__hmrId) {\n        const oldStyles = this._childStyles.get(comp.__hmrId);\n        if (oldStyles) {\n          oldStyles.forEach((s) => this._root.removeChild(s));\n          oldStyles.length = 0;\n        }\n      }\n    }\n  }\n};\nfunction useHost(caller) {\n  const instance = getCurrentInstance();\n  const el = instance && instance.ce;\n  if (el) {\n    return el;\n  } else if (true) {\n    if (!instance) {\n      warn2(\n        `${caller || \"useHost\"} called without an active component instance.`\n      );\n    } else {\n      warn2(\n        `${caller || \"useHost\"} can only be used in components defined via defineCustomElement.`\n      );\n    }\n  }\n  return null;\n}\nfunction useShadowRoot() {\n  const el = true ? useHost(\"useShadowRoot\") : useHost();\n  return el && el.shadowRoot;\n}\nfunction useCssModule(name = \"$style\") {\n  {\n    const instance = getCurrentInstance();\n    if (!instance) {\n      warn2(`useCssModule must be called inside setup()`);\n      return EMPTY_OBJ;\n    }\n    const modules = instance.type.__cssModules;\n    if (!modules) {\n      warn2(`Current instance does not have CSS modules injected.`);\n      return EMPTY_OBJ;\n    }\n    const mod = modules[name];\n    if (!mod) {\n      warn2(`Current instance does not have CSS module named \"${name}\".`);\n      return EMPTY_OBJ;\n    }\n    return mod;\n  }\n}\nvar positionMap = /* @__PURE__ */ new WeakMap();\nvar newPositionMap = /* @__PURE__ */ new WeakMap();\nvar moveCbKey = Symbol(\"_moveCb\");\nvar enterCbKey2 = Symbol(\"_enterCb\");\nvar decorate = (t) => {\n  delete t.props.mode;\n  return t;\n};\nvar TransitionGroupImpl = decorate({\n  name: \"TransitionGroup\",\n  props: extend({}, TransitionPropsValidators, {\n    tag: String,\n    moveClass: String\n  }),\n  setup(props, { slots }) {\n    const instance = getCurrentInstance();\n    const state = useTransitionState();\n    let prevChildren;\n    let children;\n    onUpdated(() => {\n      if (!prevChildren.length) {\n        return;\n      }\n      const moveClass = props.moveClass || `${props.name || \"v\"}-move`;\n      if (!hasCSSTransform(\n        prevChildren[0].el,\n        instance.vnode.el,\n        moveClass\n      )) {\n        return;\n      }\n      prevChildren.forEach(callPendingCbs);\n      prevChildren.forEach(recordPosition);\n      const movedChildren = prevChildren.filter(applyTranslation);\n      forceReflow();\n      movedChildren.forEach((c) => {\n        const el = c.el;\n        const style = el.style;\n        addTransitionClass(el, moveClass);\n        style.transform = style.webkitTransform = style.transitionDuration = \"\";\n        const cb = el[moveCbKey] = (e) => {\n          if (e && e.target !== el) {\n            return;\n          }\n          if (!e || /transform$/.test(e.propertyName)) {\n            el.removeEventListener(\"transitionend\", cb);\n            el[moveCbKey] = null;\n            removeTransitionClass(el, moveClass);\n          }\n        };\n        el.addEventListener(\"transitionend\", cb);\n      });\n    });\n    return () => {\n      const rawProps = toRaw(props);\n      const cssTransitionProps = resolveTransitionProps(rawProps);\n      let tag = rawProps.tag || Fragment;\n      prevChildren = [];\n      if (children) {\n        for (let i = 0; i < children.length; i++) {\n          const child = children[i];\n          if (child.el && child.el instanceof Element) {\n            prevChildren.push(child);\n            setTransitionHooks(\n              child,\n              resolveTransitionHooks(\n                child,\n                cssTransitionProps,\n                state,\n                instance\n              )\n            );\n            positionMap.set(\n              child,\n              child.el.getBoundingClientRect()\n            );\n          }\n        }\n      }\n      children = slots.default ? getTransitionRawChildren(slots.default()) : [];\n      for (let i = 0; i < children.length; i++) {\n        const child = children[i];\n        if (child.key != null) {\n          setTransitionHooks(\n            child,\n            resolveTransitionHooks(child, cssTransitionProps, state, instance)\n          );\n        } else if (child.type !== Text) {\n          warn2(`<TransitionGroup> children must be keyed.`);\n        }\n      }\n      return createVNode(tag, null, children);\n    };\n  }\n});\nvar TransitionGroup = TransitionGroupImpl;\nfunction callPendingCbs(c) {\n  const el = c.el;\n  if (el[moveCbKey]) {\n    el[moveCbKey]();\n  }\n  if (el[enterCbKey2]) {\n    el[enterCbKey2]();\n  }\n}\nfunction recordPosition(c) {\n  newPositionMap.set(c, c.el.getBoundingClientRect());\n}\nfunction applyTranslation(c) {\n  const oldPos = positionMap.get(c);\n  const newPos = newPositionMap.get(c);\n  const dx = oldPos.left - newPos.left;\n  const dy = oldPos.top - newPos.top;\n  if (dx || dy) {\n    const s = c.el.style;\n    s.transform = s.webkitTransform = `translate(${dx}px,${dy}px)`;\n    s.transitionDuration = \"0s\";\n    return c;\n  }\n}\nfunction hasCSSTransform(el, root, moveClass) {\n  const clone = el.cloneNode();\n  const _vtc = el[vtcKey];\n  if (_vtc) {\n    _vtc.forEach((cls) => {\n      cls.split(/\\s+/).forEach((c) => c && clone.classList.remove(c));\n    });\n  }\n  moveClass.split(/\\s+/).forEach((c) => c && clone.classList.add(c));\n  clone.style.display = \"none\";\n  const container = root.nodeType === 1 ? root : root.parentNode;\n  container.appendChild(clone);\n  const { hasTransform } = getTransitionInfo(clone);\n  container.removeChild(clone);\n  return hasTransform;\n}\nvar getModelAssigner = (vnode) => {\n  const fn = vnode.props[\"onUpdate:modelValue\"] || false;\n  return isArray(fn) ? (value) => invokeArrayFns(fn, value) : fn;\n};\nfunction onCompositionStart(e) {\n  e.target.composing = true;\n}\nfunction onCompositionEnd(e) {\n  const target = e.target;\n  if (target.composing) {\n    target.composing = false;\n    target.dispatchEvent(new Event(\"input\"));\n  }\n}\nvar assignKey = Symbol(\"_assign\");\nvar vModelText = {\n  created(el, { modifiers: { lazy, trim, number } }, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n    const castToNumber = number || vnode.props && vnode.props.type === \"number\";\n    addEventListener(el, lazy ? \"change\" : \"input\", (e) => {\n      if (e.target.composing) return;\n      let domValue = el.value;\n      if (trim) {\n        domValue = domValue.trim();\n      }\n      if (castToNumber) {\n        domValue = looseToNumber(domValue);\n      }\n      el[assignKey](domValue);\n    });\n    if (trim) {\n      addEventListener(el, \"change\", () => {\n        el.value = el.value.trim();\n      });\n    }\n    if (!lazy) {\n      addEventListener(el, \"compositionstart\", onCompositionStart);\n      addEventListener(el, \"compositionend\", onCompositionEnd);\n      addEventListener(el, \"change\", onCompositionEnd);\n    }\n  },\n  // set value on mounted so it's after min/max for type=\"range\"\n  mounted(el, { value }) {\n    el.value = value == null ? \"\" : value;\n  },\n  beforeUpdate(el, { value, oldValue, modifiers: { lazy, trim, number } }, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n    if (el.composing) return;\n    const elValue = (number || el.type === \"number\") && !/^0\\d/.test(el.value) ? looseToNumber(el.value) : el.value;\n    const newValue = value == null ? \"\" : value;\n    if (elValue === newValue) {\n      return;\n    }\n    if (document.activeElement === el && el.type !== \"range\") {\n      if (lazy && value === oldValue) {\n        return;\n      }\n      if (trim && el.value.trim() === newValue) {\n        return;\n      }\n    }\n    el.value = newValue;\n  }\n};\nvar vModelCheckbox = {\n  // #4096 array checkboxes need to be deep traversed\n  deep: true,\n  created(el, _, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n    addEventListener(el, \"change\", () => {\n      const modelValue = el._modelValue;\n      const elementValue = getValue(el);\n      const checked = el.checked;\n      const assign = el[assignKey];\n      if (isArray(modelValue)) {\n        const index = looseIndexOf(modelValue, elementValue);\n        const found = index !== -1;\n        if (checked && !found) {\n          assign(modelValue.concat(elementValue));\n        } else if (!checked && found) {\n          const filtered = [...modelValue];\n          filtered.splice(index, 1);\n          assign(filtered);\n        }\n      } else if (isSet(modelValue)) {\n        const cloned = new Set(modelValue);\n        if (checked) {\n          cloned.add(elementValue);\n        } else {\n          cloned.delete(elementValue);\n        }\n        assign(cloned);\n      } else {\n        assign(getCheckboxValue(el, checked));\n      }\n    });\n  },\n  // set initial checked on mount to wait for true-value/false-value\n  mounted: setChecked,\n  beforeUpdate(el, binding, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n    setChecked(el, binding, vnode);\n  }\n};\nfunction setChecked(el, { value, oldValue }, vnode) {\n  el._modelValue = value;\n  let checked;\n  if (isArray(value)) {\n    checked = looseIndexOf(value, vnode.props.value) > -1;\n  } else if (isSet(value)) {\n    checked = value.has(vnode.props.value);\n  } else {\n    if (value === oldValue) return;\n    checked = looseEqual(value, getCheckboxValue(el, true));\n  }\n  if (el.checked !== checked) {\n    el.checked = checked;\n  }\n}\nvar vModelRadio = {\n  created(el, { value }, vnode) {\n    el.checked = looseEqual(value, vnode.props.value);\n    el[assignKey] = getModelAssigner(vnode);\n    addEventListener(el, \"change\", () => {\n      el[assignKey](getValue(el));\n    });\n  },\n  beforeUpdate(el, { value, oldValue }, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n    if (value !== oldValue) {\n      el.checked = looseEqual(value, vnode.props.value);\n    }\n  }\n};\nvar vModelSelect = {\n  // <select multiple> value need to be deep traversed\n  deep: true,\n  created(el, { value, modifiers: { number } }, vnode) {\n    const isSetModel = isSet(value);\n    addEventListener(el, \"change\", () => {\n      const selectedVal = Array.prototype.filter.call(el.options, (o) => o.selected).map(\n        (o) => number ? looseToNumber(getValue(o)) : getValue(o)\n      );\n      el[assignKey](\n        el.multiple ? isSetModel ? new Set(selectedVal) : selectedVal : selectedVal[0]\n      );\n      el._assigning = true;\n      nextTick(() => {\n        el._assigning = false;\n      });\n    });\n    el[assignKey] = getModelAssigner(vnode);\n  },\n  // set value in mounted & updated because <select> relies on its children\n  // <option>s.\n  mounted(el, { value }) {\n    setSelected(el, value);\n  },\n  beforeUpdate(el, _binding, vnode) {\n    el[assignKey] = getModelAssigner(vnode);\n  },\n  updated(el, { value }) {\n    if (!el._assigning) {\n      setSelected(el, value);\n    }\n  }\n};\nfunction setSelected(el, value) {\n  const isMultiple = el.multiple;\n  const isArrayValue = isArray(value);\n  if (isMultiple && !isArrayValue && !isSet(value)) {\n    warn2(\n      `<select multiple v-model> expects an Array or Set value for its binding, but got ${Object.prototype.toString.call(value).slice(8, -1)}.`\n    );\n    return;\n  }\n  for (let i = 0, l = el.options.length; i < l; i++) {\n    const option = el.options[i];\n    const optionValue = getValue(option);\n    if (isMultiple) {\n      if (isArrayValue) {\n        const optionType = typeof optionValue;\n        if (optionType === \"string\" || optionType === \"number\") {\n          option.selected = value.some((v) => String(v) === String(optionValue));\n        } else {\n          option.selected = looseIndexOf(value, optionValue) > -1;\n        }\n      } else {\n        option.selected = value.has(optionValue);\n      }\n    } else if (looseEqual(getValue(option), value)) {\n      if (el.selectedIndex !== i) el.selectedIndex = i;\n      return;\n    }\n  }\n  if (!isMultiple && el.selectedIndex !== -1) {\n    el.selectedIndex = -1;\n  }\n}\nfunction getValue(el) {\n  return \"_value\" in el ? el._value : el.value;\n}\nfunction getCheckboxValue(el, checked) {\n  const key = checked ? \"_trueValue\" : \"_falseValue\";\n  return key in el ? el[key] : checked;\n}\nvar vModelDynamic = {\n  created(el, binding, vnode) {\n    callModelHook(el, binding, vnode, null, \"created\");\n  },\n  mounted(el, binding, vnode) {\n    callModelHook(el, binding, vnode, null, \"mounted\");\n  },\n  beforeUpdate(el, binding, vnode, prevVNode) {\n    callModelHook(el, binding, vnode, prevVNode, \"beforeUpdate\");\n  },\n  updated(el, binding, vnode, prevVNode) {\n    callModelHook(el, binding, vnode, prevVNode, \"updated\");\n  }\n};\nfunction resolveDynamicModel(tagName, type) {\n  switch (tagName) {\n    case \"SELECT\":\n      return vModelSelect;\n    case \"TEXTAREA\":\n      return vModelText;\n    default:\n      switch (type) {\n        case \"checkbox\":\n          return vModelCheckbox;\n        case \"radio\":\n          return vModelRadio;\n        default:\n          return vModelText;\n      }\n  }\n}\nfunction callModelHook(el, binding, vnode, prevVNode, hook) {\n  const modelToUse = resolveDynamicModel(\n    el.tagName,\n    vnode.props && vnode.props.type\n  );\n  const fn = modelToUse[hook];\n  fn && fn(el, binding, vnode, prevVNode);\n}\nfunction initVModelForSSR() {\n  vModelText.getSSRProps = ({ value }) => ({ value });\n  vModelRadio.getSSRProps = ({ value }, vnode) => {\n    if (vnode.props && looseEqual(vnode.props.value, value)) {\n      return { checked: true };\n    }\n  };\n  vModelCheckbox.getSSRProps = ({ value }, vnode) => {\n    if (isArray(value)) {\n      if (vnode.props && looseIndexOf(value, vnode.props.value) > -1) {\n        return { checked: true };\n      }\n    } else if (isSet(value)) {\n      if (vnode.props && value.has(vnode.props.value)) {\n        return { checked: true };\n      }\n    } else if (value) {\n      return { checked: true };\n    }\n  };\n  vModelDynamic.getSSRProps = (binding, vnode) => {\n    if (typeof vnode.type !== \"string\") {\n      return;\n    }\n    const modelToUse = resolveDynamicModel(\n      // resolveDynamicModel expects an uppercase tag name, but vnode.type is lowercase\n      vnode.type.toUpperCase(),\n      vnode.props && vnode.props.type\n    );\n    if (modelToUse.getSSRProps) {\n      return modelToUse.getSSRProps(binding, vnode);\n    }\n  };\n}\nvar systemModifiers = [\"ctrl\", \"shift\", \"alt\", \"meta\"];\nvar modifierGuards = {\n  stop: (e) => e.stopPropagation(),\n  prevent: (e) => e.preventDefault(),\n  self: (e) => e.target !== e.currentTarget,\n  ctrl: (e) => !e.ctrlKey,\n  shift: (e) => !e.shiftKey,\n  alt: (e) => !e.altKey,\n  meta: (e) => !e.metaKey,\n  left: (e) => \"button\" in e && e.button !== 0,\n  middle: (e) => \"button\" in e && e.button !== 1,\n  right: (e) => \"button\" in e && e.button !== 2,\n  exact: (e, modifiers) => systemModifiers.some((m) => e[`${m}Key`] && !modifiers.includes(m))\n};\nvar withModifiers = (fn, modifiers) => {\n  const cache = fn._withMods || (fn._withMods = {});\n  const cacheKey = modifiers.join(\".\");\n  return cache[cacheKey] || (cache[cacheKey] = (event, ...args) => {\n    for (let i = 0; i < modifiers.length; i++) {\n      const guard = modifierGuards[modifiers[i]];\n      if (guard && guard(event, modifiers)) return;\n    }\n    return fn(event, ...args);\n  });\n};\nvar keyNames = {\n  esc: \"escape\",\n  space: \" \",\n  up: \"arrow-up\",\n  left: \"arrow-left\",\n  right: \"arrow-right\",\n  down: \"arrow-down\",\n  delete: \"backspace\"\n};\nvar withKeys = (fn, modifiers) => {\n  const cache = fn._withKeys || (fn._withKeys = {});\n  const cacheKey = modifiers.join(\".\");\n  return cache[cacheKey] || (cache[cacheKey] = (event) => {\n    if (!(\"key\" in event)) {\n      return;\n    }\n    const eventKey = hyphenate(event.key);\n    if (modifiers.some(\n      (k) => k === eventKey || keyNames[k] === eventKey\n    )) {\n      return fn(event);\n    }\n  });\n};\nvar rendererOptions = extend({ patchProp }, nodeOps);\nvar renderer;\nvar enabledHydration = false;\nfunction ensureRenderer() {\n  return renderer || (renderer = createRenderer(rendererOptions));\n}\nfunction ensureHydrationRenderer() {\n  renderer = enabledHydration ? renderer : createHydrationRenderer(rendererOptions);\n  enabledHydration = true;\n  return renderer;\n}\nvar render = (...args) => {\n  ensureRenderer().render(...args);\n};\nvar hydrate = (...args) => {\n  ensureHydrationRenderer().hydrate(...args);\n};\nvar createApp = (...args) => {\n  const app = ensureRenderer().createApp(...args);\n  if (true) {\n    injectNativeTagCheck(app);\n    injectCompilerOptionsCheck(app);\n  }\n  const { mount } = app;\n  app.mount = (containerOrSelector) => {\n    const container = normalizeContainer(containerOrSelector);\n    if (!container) return;\n    const component = app._component;\n    if (!isFunction(component) && !component.render && !component.template) {\n      component.template = container.innerHTML;\n    }\n    if (container.nodeType === 1) {\n      container.textContent = \"\";\n    }\n    const proxy = mount(container, false, resolveRootNamespace(container));\n    if (container instanceof Element) {\n      container.removeAttribute(\"v-cloak\");\n      container.setAttribute(\"data-v-app\", \"\");\n    }\n    return proxy;\n  };\n  return app;\n};\nvar createSSRApp = (...args) => {\n  const app = ensureHydrationRenderer().createApp(...args);\n  if (true) {\n    injectNativeTagCheck(app);\n    injectCompilerOptionsCheck(app);\n  }\n  const { mount } = app;\n  app.mount = (containerOrSelector) => {\n    const container = normalizeContainer(containerOrSelector);\n    if (container) {\n      return mount(container, true, resolveRootNamespace(container));\n    }\n  };\n  return app;\n};\nfunction resolveRootNamespace(container) {\n  if (container instanceof SVGElement) {\n    return \"svg\";\n  }\n  if (typeof MathMLElement === \"function\" && container instanceof MathMLElement) {\n    return \"mathml\";\n  }\n}\nfunction injectNativeTagCheck(app) {\n  Object.defineProperty(app.config, \"isNativeTag\", {\n    value: (tag) => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),\n    writable: false\n  });\n}\nfunction injectCompilerOptionsCheck(app) {\n  if (isRuntimeOnly()) {\n    const isCustomElement = app.config.isCustomElement;\n    Object.defineProperty(app.config, \"isCustomElement\", {\n      get() {\n        return isCustomElement;\n      },\n      set() {\n        warn2(\n          `The \\`isCustomElement\\` config option is deprecated. Use \\`compilerOptions.isCustomElement\\` instead.`\n        );\n      }\n    });\n    const compilerOptions = app.config.compilerOptions;\n    const msg = `The \\`compilerOptions\\` config option is only respected when using a build of Vue.js that includes the runtime compiler (aka \"full build\"). Since you are using the runtime-only build, \\`compilerOptions\\` must be passed to \\`@vue/compiler-dom\\` in the build setup instead.\n- For vue-loader: pass it via vue-loader's \\`compilerOptions\\` loader option.\n- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader\n- For vite: pass it via @vitejs/plugin-vue options. See https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue#example-for-passing-options-to-vuecompiler-sfc`;\n    Object.defineProperty(app.config, \"compilerOptions\", {\n      get() {\n        warn2(msg);\n        return compilerOptions;\n      },\n      set() {\n        warn2(msg);\n      }\n    });\n  }\n}\nfunction normalizeContainer(container) {\n  if (isString(container)) {\n    const res = document.querySelector(container);\n    if (!res) {\n      warn2(\n        `Failed to mount app: mount target selector \"${container}\" returned null.`\n      );\n    }\n    return res;\n  }\n  if (window.ShadowRoot && container instanceof window.ShadowRoot && container.mode === \"closed\") {\n    warn2(\n      `mounting on a ShadowRoot with \\`{mode: \"closed\"}\\` may lead to unpredictable bugs`\n    );\n  }\n  return container;\n}\nvar ssrDirectiveInitialized = false;\nvar initDirectivesForSSR = () => {\n  if (!ssrDirectiveInitialized) {\n    ssrDirectiveInitialized = true;\n    initVModelForSSR();\n    initVShowForSSR();\n  }\n};\n\n// node_modules/.pnpm/vue@3.5.13/node_modules/vue/dist/vue.runtime.esm-bundler.js\nfunction initDev() {\n  {\n    initCustomFormatter();\n  }\n}\nif (true) {\n  initDev();\n}\nvar compile2 = () => {\n  if (true) {\n    warn2(\n      `Runtime compilation is not supported in this build of Vue. Configure your bundler to alias \"vue\" to \"vue/dist/vue.esm-bundler.js\".`\n    );\n  }\n};\n\nexport {\n  camelize,\n  capitalize,\n  toHandlerKey,\n  normalizeStyle,\n  normalizeClass,\n  normalizeProps,\n  toDisplayString,\n  EffectScope,\n  effectScope,\n  getCurrentScope,\n  onScopeDispose,\n  ReactiveEffect,\n  effect,\n  stop,\n  reactive,\n  shallowReactive,\n  readonly,\n  shallowReadonly,\n  isReactive,\n  isReadonly,\n  isShallow,\n  isProxy,\n  toRaw,\n  markRaw,\n  isRef2 as isRef,\n  ref,\n  shallowRef,\n  triggerRef,\n  unref,\n  toValue,\n  proxyRefs,\n  customRef,\n  toRefs,\n  toRef,\n  TrackOpTypes,\n  TriggerOpTypes,\n  getCurrentWatcher,\n  onWatcherCleanup,\n  assertNumber,\n  ErrorCodes,\n  callWithErrorHandling,\n  callWithAsyncErrorHandling,\n  handleError,\n  nextTick,\n  queuePostFlushCb,\n  pushScopeId,\n  popScopeId,\n  withScopeId,\n  withCtx,\n  withDirectives,\n  Teleport,\n  useTransitionState,\n  BaseTransitionPropsValidators,\n  BaseTransition,\n  resolveTransitionHooks,\n  setTransitionHooks,\n  getTransitionRawChildren,\n  defineComponent,\n  useId,\n  useTemplateRef,\n  hydrateOnIdle,\n  hydrateOnVisible,\n  hydrateOnMediaQuery,\n  hydrateOnInteraction,\n  defineAsyncComponent,\n  KeepAlive,\n  onActivated,\n  onDeactivated,\n  onBeforeMount,\n  onMounted,\n  onBeforeUpdate,\n  onUpdated,\n  onBeforeUnmount,\n  onUnmounted,\n  onServerPrefetch,\n  onRenderTriggered,\n  onRenderTracked,\n  onErrorCaptured,\n  resolveComponent,\n  resolveDynamicComponent,\n  resolveDirective,\n  renderList,\n  createSlots,\n  renderSlot,\n  toHandlers,\n  defineProps,\n  defineEmits,\n  defineExpose,\n  defineOptions,\n  defineSlots,\n  defineModel,\n  withDefaults,\n  useSlots,\n  useAttrs,\n  mergeDefaults,\n  mergeModels,\n  createPropsRestProxy,\n  withAsyncContext,\n  provide,\n  inject,\n  hasInjectionContext,\n  createRenderer,\n  createHydrationRenderer,\n  ssrContextKey,\n  useSSRContext,\n  watchEffect,\n  watchPostEffect,\n  watchSyncEffect,\n  watch2 as watch,\n  useModel,\n  Suspense,\n  Fragment,\n  Text,\n  Comment,\n  Static,\n  openBlock,\n  setBlockTracking,\n  createElementBlock,\n  createBlock,\n  isVNode,\n  transformVNodeArgs,\n  createBaseVNode,\n  createVNode,\n  guardReactiveProps,\n  cloneVNode,\n  createTextVNode,\n  createStaticVNode,\n  createCommentVNode,\n  mergeProps,\n  getCurrentInstance,\n  registerRuntimeCompiler,\n  isRuntimeOnly,\n  computed2 as computed,\n  h,\n  initCustomFormatter,\n  withMemo,\n  isMemoSame,\n  version,\n  warn2 as warn,\n  ErrorTypeStrings,\n  devtools,\n  setDevtoolsHook,\n  ssrUtils,\n  resolveFilter,\n  compatUtils,\n  DeprecationTypes,\n  Transition,\n  vShow,\n  useCssVars,\n  defineCustomElement,\n  defineSSRCustomElement,\n  VueElement,\n  useHost,\n  useShadowRoot,\n  useCssModule,\n  TransitionGroup,\n  vModelText,\n  vModelCheckbox,\n  vModelRadio,\n  vModelSelect,\n  vModelDynamic,\n  withModifiers,\n  withKeys,\n  render,\n  hydrate,\n  createApp,\n  createSSRApp,\n  initDirectivesForSSR,\n  compile2 as compile\n};\n/*! Bundled license information:\n\n@vue/shared/dist/shared.esm-bundler.js:\n  (**\n  * @vue/shared v3.5.13\n  * (c) 2018-present Yuxi (Evan) You and Vue contributors\n  * @license MIT\n  **)\n  (*! #__NO_SIDE_EFFECTS__ *)\n\n@vue/reactivity/dist/reactivity.esm-bundler.js:\n  (**\n  * @vue/reactivity v3.5.13\n  * (c) 2018-present Yuxi (Evan) You and Vue contributors\n  * @license MIT\n  **)\n\n@vue/runtime-core/dist/runtime-core.esm-bundler.js:\n  (**\n  * @vue/runtime-core v3.5.13\n  * (c) 2018-present Yuxi (Evan) You and Vue contributors\n  * @license MIT\n  **)\n  (*! #__NO_SIDE_EFFECTS__ *)\n\n@vue/runtime-dom/dist/runtime-dom.esm-bundler.js:\n  (**\n  * @vue/runtime-dom v3.5.13\n  * (c) 2018-present Yuxi (Evan) You and Vue contributors\n  * @license MIT\n  **)\n  (*! #__NO_SIDE_EFFECTS__ *)\n\nvue/dist/vue.runtime.esm-bundler.js:\n  (**\n  * vue v3.5.13\n  * (c) 2018-present Yuxi (Evan) You and Vue contributors\n  * @license MIT\n  **)\n*/\n//# sourceMappingURL=chunk-CQOUZRMK.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/chunk-KT7LHMJ2.js",
    "content": "import {\n  Fragment,\n  TransitionGroup,\n  computed,\n  customRef,\n  defineComponent,\n  effectScope,\n  getCurrentInstance,\n  getCurrentScope,\n  h,\n  hasInjectionContext,\n  inject,\n  isReactive,\n  isReadonly,\n  isRef,\n  markRaw,\n  nextTick,\n  onBeforeMount,\n  onBeforeUnmount,\n  onBeforeUpdate,\n  onMounted,\n  onScopeDispose,\n  onUnmounted,\n  onUpdated,\n  provide,\n  reactive,\n  readonly,\n  ref,\n  shallowReactive,\n  shallowRef,\n  toRaw,\n  toRef,\n  toRefs,\n  toValue,\n  unref,\n  watch,\n  watchEffect\n} from \"./chunk-CQOUZRMK.js\";\n\n// node_modules/.pnpm/@vueuse+shared@12.5.0/node_modules/@vueuse/shared/index.mjs\nfunction computedEager(fn, options) {\n  var _a;\n  const result = shallowRef();\n  watchEffect(() => {\n    result.value = fn();\n  }, {\n    ...options,\n    flush: (_a = options == null ? void 0 : options.flush) != null ? _a : \"sync\"\n  });\n  return readonly(result);\n}\nfunction computedWithControl(source, fn) {\n  let v = void 0;\n  let track;\n  let trigger;\n  const dirty = ref(true);\n  const update = () => {\n    dirty.value = true;\n    trigger();\n  };\n  watch(source, update, { flush: \"sync\" });\n  const get2 = typeof fn === \"function\" ? fn : fn.get;\n  const set2 = typeof fn === \"function\" ? void 0 : fn.set;\n  const result = customRef((_track, _trigger) => {\n    track = _track;\n    trigger = _trigger;\n    return {\n      get() {\n        if (dirty.value) {\n          v = get2(v);\n          dirty.value = false;\n        }\n        track();\n        return v;\n      },\n      set(v2) {\n        set2 == null ? void 0 : set2(v2);\n      }\n    };\n  });\n  if (Object.isExtensible(result))\n    result.trigger = update;\n  return result;\n}\nfunction tryOnScopeDispose(fn) {\n  if (getCurrentScope()) {\n    onScopeDispose(fn);\n    return true;\n  }\n  return false;\n}\nfunction createEventHook() {\n  const fns = /* @__PURE__ */ new Set();\n  const off = (fn) => {\n    fns.delete(fn);\n  };\n  const clear = () => {\n    fns.clear();\n  };\n  const on = (fn) => {\n    fns.add(fn);\n    const offFn = () => off(fn);\n    tryOnScopeDispose(offFn);\n    return {\n      off: offFn\n    };\n  };\n  const trigger = (...args) => {\n    return Promise.all(Array.from(fns).map((fn) => fn(...args)));\n  };\n  return {\n    on,\n    off,\n    trigger,\n    clear\n  };\n}\nfunction createGlobalState(stateFactory) {\n  let initialized = false;\n  let state;\n  const scope = effectScope(true);\n  return (...args) => {\n    if (!initialized) {\n      state = scope.run(() => stateFactory(...args));\n      initialized = true;\n    }\n    return state;\n  };\n}\nvar localProvidedStateMap = /* @__PURE__ */ new WeakMap();\nvar injectLocal = (...args) => {\n  var _a;\n  const key = args[0];\n  const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy;\n  if (instance == null && !hasInjectionContext())\n    throw new Error(\"injectLocal must be called in setup\");\n  if (instance && localProvidedStateMap.has(instance) && key in localProvidedStateMap.get(instance))\n    return localProvidedStateMap.get(instance)[key];\n  return inject(...args);\n};\nvar provideLocal = (key, value) => {\n  var _a;\n  const instance = (_a = getCurrentInstance()) == null ? void 0 : _a.proxy;\n  if (instance == null)\n    throw new Error(\"provideLocal must be called in setup\");\n  if (!localProvidedStateMap.has(instance))\n    localProvidedStateMap.set(instance, /* @__PURE__ */ Object.create(null));\n  const localProvidedState = localProvidedStateMap.get(instance);\n  localProvidedState[key] = value;\n  provide(key, value);\n};\nfunction createInjectionState(composable, options) {\n  const key = (options == null ? void 0 : options.injectionKey) || Symbol(composable.name || \"InjectionState\");\n  const defaultValue = options == null ? void 0 : options.defaultValue;\n  const useProvidingState = (...args) => {\n    const state = composable(...args);\n    provideLocal(key, state);\n    return state;\n  };\n  const useInjectedState = () => injectLocal(key, defaultValue);\n  return [useProvidingState, useInjectedState];\n}\nfunction createSharedComposable(composable) {\n  let subscribers = 0;\n  let state;\n  let scope;\n  const dispose = () => {\n    subscribers -= 1;\n    if (scope && subscribers <= 0) {\n      scope.stop();\n      state = void 0;\n      scope = void 0;\n    }\n  };\n  return (...args) => {\n    subscribers += 1;\n    if (!scope) {\n      scope = effectScope(true);\n      state = scope.run(() => composable(...args));\n    }\n    tryOnScopeDispose(dispose);\n    return state;\n  };\n}\nfunction extendRef(ref2, extend, { enumerable = false, unwrap = true } = {}) {\n  for (const [key, value] of Object.entries(extend)) {\n    if (key === \"value\")\n      continue;\n    if (isRef(value) && unwrap) {\n      Object.defineProperty(ref2, key, {\n        get() {\n          return value.value;\n        },\n        set(v) {\n          value.value = v;\n        },\n        enumerable\n      });\n    } else {\n      Object.defineProperty(ref2, key, { value, enumerable });\n    }\n  }\n  return ref2;\n}\nfunction get(obj, key) {\n  if (key == null)\n    return unref(obj);\n  return unref(obj)[key];\n}\nfunction isDefined(v) {\n  return unref(v) != null;\n}\nfunction makeDestructurable(obj, arr) {\n  if (typeof Symbol !== \"undefined\") {\n    const clone = { ...obj };\n    Object.defineProperty(clone, Symbol.iterator, {\n      enumerable: false,\n      value() {\n        let index = 0;\n        return {\n          next: () => ({\n            value: arr[index++],\n            done: index > arr.length\n          })\n        };\n      }\n    });\n    return clone;\n  } else {\n    return Object.assign([...arr], obj);\n  }\n}\nfunction reactify(fn, options) {\n  const unrefFn = (options == null ? void 0 : options.computedGetter) === false ? unref : toValue;\n  return function(...args) {\n    return computed(() => fn.apply(this, args.map((i) => unrefFn(i))));\n  };\n}\nfunction reactifyObject(obj, optionsOrKeys = {}) {\n  let keys2 = [];\n  let options;\n  if (Array.isArray(optionsOrKeys)) {\n    keys2 = optionsOrKeys;\n  } else {\n    options = optionsOrKeys;\n    const { includeOwnProperties = true } = optionsOrKeys;\n    keys2.push(...Object.keys(obj));\n    if (includeOwnProperties)\n      keys2.push(...Object.getOwnPropertyNames(obj));\n  }\n  return Object.fromEntries(\n    keys2.map((key) => {\n      const value = obj[key];\n      return [\n        key,\n        typeof value === \"function\" ? reactify(value.bind(obj), options) : value\n      ];\n    })\n  );\n}\nfunction toReactive(objectRef) {\n  if (!isRef(objectRef))\n    return reactive(objectRef);\n  const proxy = new Proxy({}, {\n    get(_, p, receiver) {\n      return unref(Reflect.get(objectRef.value, p, receiver));\n    },\n    set(_, p, value) {\n      if (isRef(objectRef.value[p]) && !isRef(value))\n        objectRef.value[p].value = value;\n      else\n        objectRef.value[p] = value;\n      return true;\n    },\n    deleteProperty(_, p) {\n      return Reflect.deleteProperty(objectRef.value, p);\n    },\n    has(_, p) {\n      return Reflect.has(objectRef.value, p);\n    },\n    ownKeys() {\n      return Object.keys(objectRef.value);\n    },\n    getOwnPropertyDescriptor() {\n      return {\n        enumerable: true,\n        configurable: true\n      };\n    }\n  });\n  return reactive(proxy);\n}\nfunction reactiveComputed(fn) {\n  return toReactive(computed(fn));\n}\nfunction reactiveOmit(obj, ...keys2) {\n  const flatKeys = keys2.flat();\n  const predicate = flatKeys[0];\n  return reactiveComputed(() => typeof predicate === \"function\" ? Object.fromEntries(Object.entries(toRefs(obj)).filter(([k, v]) => !predicate(toValue(v), k))) : Object.fromEntries(Object.entries(toRefs(obj)).filter((e) => !flatKeys.includes(e[0]))));\n}\nvar isClient = typeof window !== \"undefined\" && typeof document !== \"undefined\";\nvar isWorker = typeof WorkerGlobalScope !== \"undefined\" && globalThis instanceof WorkerGlobalScope;\nvar isDef = (val) => typeof val !== \"undefined\";\nvar notNullish = (val) => val != null;\nvar assert = (condition, ...infos) => {\n  if (!condition)\n    console.warn(...infos);\n};\nvar toString = Object.prototype.toString;\nvar isObject = (val) => toString.call(val) === \"[object Object]\";\nvar now = () => Date.now();\nvar timestamp = () => +Date.now();\nvar clamp = (n, min, max) => Math.min(max, Math.max(min, n));\nvar noop = () => {\n};\nvar rand = (min, max) => {\n  min = Math.ceil(min);\n  max = Math.floor(max);\n  return Math.floor(Math.random() * (max - min + 1)) + min;\n};\nvar hasOwn = (val, key) => Object.prototype.hasOwnProperty.call(val, key);\nvar isIOS = getIsIOS();\nfunction getIsIOS() {\n  var _a, _b;\n  return isClient && ((_a = window == null ? void 0 : window.navigator) == null ? void 0 : _a.userAgent) && (/iP(?:ad|hone|od)/.test(window.navigator.userAgent) || ((_b = window == null ? void 0 : window.navigator) == null ? void 0 : _b.maxTouchPoints) > 2 && /iPad|Macintosh/.test(window == null ? void 0 : window.navigator.userAgent));\n}\nfunction createFilterWrapper(filter, fn) {\n  function wrapper(...args) {\n    return new Promise((resolve, reject) => {\n      Promise.resolve(filter(() => fn.apply(this, args), { fn, thisArg: this, args })).then(resolve).catch(reject);\n    });\n  }\n  return wrapper;\n}\nvar bypassFilter = (invoke2) => {\n  return invoke2();\n};\nfunction debounceFilter(ms, options = {}) {\n  let timer;\n  let maxTimer;\n  let lastRejector = noop;\n  const _clearTimeout = (timer2) => {\n    clearTimeout(timer2);\n    lastRejector();\n    lastRejector = noop;\n  };\n  let lastInvoker;\n  const filter = (invoke2) => {\n    const duration = toValue(ms);\n    const maxDuration = toValue(options.maxWait);\n    if (timer)\n      _clearTimeout(timer);\n    if (duration <= 0 || maxDuration !== void 0 && maxDuration <= 0) {\n      if (maxTimer) {\n        _clearTimeout(maxTimer);\n        maxTimer = null;\n      }\n      return Promise.resolve(invoke2());\n    }\n    return new Promise((resolve, reject) => {\n      lastRejector = options.rejectOnCancel ? reject : resolve;\n      lastInvoker = invoke2;\n      if (maxDuration && !maxTimer) {\n        maxTimer = setTimeout(() => {\n          if (timer)\n            _clearTimeout(timer);\n          maxTimer = null;\n          resolve(lastInvoker());\n        }, maxDuration);\n      }\n      timer = setTimeout(() => {\n        if (maxTimer)\n          _clearTimeout(maxTimer);\n        maxTimer = null;\n        resolve(invoke2());\n      }, duration);\n    });\n  };\n  return filter;\n}\nfunction throttleFilter(...args) {\n  let lastExec = 0;\n  let timer;\n  let isLeading = true;\n  let lastRejector = noop;\n  let lastValue;\n  let ms;\n  let trailing;\n  let leading;\n  let rejectOnCancel;\n  if (!isRef(args[0]) && typeof args[0] === \"object\")\n    ({ delay: ms, trailing = true, leading = true, rejectOnCancel = false } = args[0]);\n  else\n    [ms, trailing = true, leading = true, rejectOnCancel = false] = args;\n  const clear = () => {\n    if (timer) {\n      clearTimeout(timer);\n      timer = void 0;\n      lastRejector();\n      lastRejector = noop;\n    }\n  };\n  const filter = (_invoke) => {\n    const duration = toValue(ms);\n    const elapsed = Date.now() - lastExec;\n    const invoke2 = () => {\n      return lastValue = _invoke();\n    };\n    clear();\n    if (duration <= 0) {\n      lastExec = Date.now();\n      return invoke2();\n    }\n    if (elapsed > duration && (leading || !isLeading)) {\n      lastExec = Date.now();\n      invoke2();\n    } else if (trailing) {\n      lastValue = new Promise((resolve, reject) => {\n        lastRejector = rejectOnCancel ? reject : resolve;\n        timer = setTimeout(() => {\n          lastExec = Date.now();\n          isLeading = true;\n          resolve(invoke2());\n          clear();\n        }, Math.max(0, duration - elapsed));\n      });\n    }\n    if (!leading && !timer)\n      timer = setTimeout(() => isLeading = true, duration);\n    isLeading = false;\n    return lastValue;\n  };\n  return filter;\n}\nfunction pausableFilter(extendFilter = bypassFilter) {\n  const isActive = ref(true);\n  function pause() {\n    isActive.value = false;\n  }\n  function resume() {\n    isActive.value = true;\n  }\n  const eventFilter = (...args) => {\n    if (isActive.value)\n      extendFilter(...args);\n  };\n  return { isActive: readonly(isActive), pause, resume, eventFilter };\n}\nfunction cacheStringFunction(fn) {\n  const cache = /* @__PURE__ */ Object.create(null);\n  return (str) => {\n    const hit = cache[str];\n    return hit || (cache[str] = fn(str));\n  };\n}\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, \"-$1\").toLowerCase());\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cacheStringFunction((str) => {\n  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : \"\");\n});\nfunction promiseTimeout(ms, throwOnTimeout = false, reason = \"Timeout\") {\n  return new Promise((resolve, reject) => {\n    if (throwOnTimeout)\n      setTimeout(() => reject(reason), ms);\n    else\n      setTimeout(resolve, ms);\n  });\n}\nfunction identity(arg) {\n  return arg;\n}\nfunction createSingletonPromise(fn) {\n  let _promise;\n  function wrapper() {\n    if (!_promise)\n      _promise = fn();\n    return _promise;\n  }\n  wrapper.reset = async () => {\n    const _prev = _promise;\n    _promise = void 0;\n    if (_prev)\n      await _prev;\n  };\n  return wrapper;\n}\nfunction invoke(fn) {\n  return fn();\n}\nfunction containsProp(obj, ...props) {\n  return props.some((k) => k in obj);\n}\nfunction increaseWithUnit(target, delta) {\n  var _a;\n  if (typeof target === \"number\")\n    return target + delta;\n  const value = ((_a = target.match(/^-?\\d+\\.?\\d*/)) == null ? void 0 : _a[0]) || \"\";\n  const unit = target.slice(value.length);\n  const result = Number.parseFloat(value) + delta;\n  if (Number.isNaN(result))\n    return target;\n  return result + unit;\n}\nfunction pxValue(px) {\n  return px.endsWith(\"rem\") ? Number.parseFloat(px) * 16 : Number.parseFloat(px);\n}\nfunction objectPick(obj, keys2, omitUndefined = false) {\n  return keys2.reduce((n, k) => {\n    if (k in obj) {\n      if (!omitUndefined || obj[k] !== void 0)\n        n[k] = obj[k];\n    }\n    return n;\n  }, {});\n}\nfunction objectOmit(obj, keys2, omitUndefined = false) {\n  return Object.fromEntries(Object.entries(obj).filter(([key, value]) => {\n    return (!omitUndefined || value !== void 0) && !keys2.includes(key);\n  }));\n}\nfunction objectEntries(obj) {\n  return Object.entries(obj);\n}\nfunction getLifeCycleTarget(target) {\n  return target || getCurrentInstance();\n}\nfunction toArray(value) {\n  return Array.isArray(value) ? value : [value];\n}\nfunction toRef2(...args) {\n  if (args.length !== 1)\n    return toRef(...args);\n  const r = args[0];\n  return typeof r === \"function\" ? readonly(customRef(() => ({ get: r, set: noop }))) : ref(r);\n}\nvar resolveRef = toRef2;\nfunction reactivePick(obj, ...keys2) {\n  const flatKeys = keys2.flat();\n  const predicate = flatKeys[0];\n  return reactiveComputed(() => typeof predicate === \"function\" ? Object.fromEntries(Object.entries(toRefs(obj)).filter(([k, v]) => predicate(toValue(v), k))) : Object.fromEntries(flatKeys.map((k) => [k, toRef2(obj, k)])));\n}\nfunction refAutoReset(defaultValue, afterMs = 1e4) {\n  return customRef((track, trigger) => {\n    let value = toValue(defaultValue);\n    let timer;\n    const resetAfter = () => setTimeout(() => {\n      value = toValue(defaultValue);\n      trigger();\n    }, toValue(afterMs));\n    tryOnScopeDispose(() => {\n      clearTimeout(timer);\n    });\n    return {\n      get() {\n        track();\n        return value;\n      },\n      set(newValue) {\n        value = newValue;\n        trigger();\n        clearTimeout(timer);\n        timer = resetAfter();\n      }\n    };\n  });\n}\nfunction useDebounceFn(fn, ms = 200, options = {}) {\n  return createFilterWrapper(\n    debounceFilter(ms, options),\n    fn\n  );\n}\nfunction refDebounced(value, ms = 200, options = {}) {\n  const debounced = ref(value.value);\n  const updater = useDebounceFn(() => {\n    debounced.value = value.value;\n  }, ms, options);\n  watch(value, () => updater());\n  return debounced;\n}\nfunction refDefault(source, defaultValue) {\n  return computed({\n    get() {\n      var _a;\n      return (_a = source.value) != null ? _a : defaultValue;\n    },\n    set(value) {\n      source.value = value;\n    }\n  });\n}\nfunction useThrottleFn(fn, ms = 200, trailing = false, leading = true, rejectOnCancel = false) {\n  return createFilterWrapper(\n    throttleFilter(ms, trailing, leading, rejectOnCancel),\n    fn\n  );\n}\nfunction refThrottled(value, delay = 200, trailing = true, leading = true) {\n  if (delay <= 0)\n    return value;\n  const throttled = ref(value.value);\n  const updater = useThrottleFn(() => {\n    throttled.value = value.value;\n  }, delay, trailing, leading);\n  watch(value, () => updater());\n  return throttled;\n}\nfunction refWithControl(initial, options = {}) {\n  let source = initial;\n  let track;\n  let trigger;\n  const ref2 = customRef((_track, _trigger) => {\n    track = _track;\n    trigger = _trigger;\n    return {\n      get() {\n        return get2();\n      },\n      set(v) {\n        set2(v);\n      }\n    };\n  });\n  function get2(tracking = true) {\n    if (tracking)\n      track();\n    return source;\n  }\n  function set2(value, triggering = true) {\n    var _a, _b;\n    if (value === source)\n      return;\n    const old = source;\n    if (((_a = options.onBeforeChange) == null ? void 0 : _a.call(options, value, old)) === false)\n      return;\n    source = value;\n    (_b = options.onChanged) == null ? void 0 : _b.call(options, value, old);\n    if (triggering)\n      trigger();\n  }\n  const untrackedGet = () => get2(false);\n  const silentSet = (v) => set2(v, false);\n  const peek = () => get2(false);\n  const lay = (v) => set2(v, false);\n  return extendRef(\n    ref2,\n    {\n      get: get2,\n      set: set2,\n      untrackedGet,\n      silentSet,\n      peek,\n      lay\n    },\n    { enumerable: true }\n  );\n}\nvar controlledRef = refWithControl;\nfunction set(...args) {\n  if (args.length === 2) {\n    const [ref2, value] = args;\n    ref2.value = value;\n  }\n  if (args.length === 3) {\n    const [target, key, value] = args;\n    target[key] = value;\n  }\n}\nfunction watchWithFilter(source, cb, options = {}) {\n  const {\n    eventFilter = bypassFilter,\n    ...watchOptions\n  } = options;\n  return watch(\n    source,\n    createFilterWrapper(\n      eventFilter,\n      cb\n    ),\n    watchOptions\n  );\n}\nfunction watchPausable(source, cb, options = {}) {\n  const {\n    eventFilter: filter,\n    ...watchOptions\n  } = options;\n  const { eventFilter, pause, resume, isActive } = pausableFilter(filter);\n  const stop = watchWithFilter(\n    source,\n    cb,\n    {\n      ...watchOptions,\n      eventFilter\n    }\n  );\n  return { stop, pause, resume, isActive };\n}\nfunction syncRef(left, right, ...[options]) {\n  const {\n    flush = \"sync\",\n    deep = false,\n    immediate = true,\n    direction = \"both\",\n    transform = {}\n  } = options || {};\n  const watchers = [];\n  const transformLTR = \"ltr\" in transform && transform.ltr || ((v) => v);\n  const transformRTL = \"rtl\" in transform && transform.rtl || ((v) => v);\n  if (direction === \"both\" || direction === \"ltr\") {\n    watchers.push(watchPausable(\n      left,\n      (newValue) => {\n        watchers.forEach((w) => w.pause());\n        right.value = transformLTR(newValue);\n        watchers.forEach((w) => w.resume());\n      },\n      { flush, deep, immediate }\n    ));\n  }\n  if (direction === \"both\" || direction === \"rtl\") {\n    watchers.push(watchPausable(\n      right,\n      (newValue) => {\n        watchers.forEach((w) => w.pause());\n        left.value = transformRTL(newValue);\n        watchers.forEach((w) => w.resume());\n      },\n      { flush, deep, immediate }\n    ));\n  }\n  const stop = () => {\n    watchers.forEach((w) => w.stop());\n  };\n  return stop;\n}\nfunction syncRefs(source, targets, options = {}) {\n  const {\n    flush = \"sync\",\n    deep = false,\n    immediate = true\n  } = options;\n  targets = toArray(targets);\n  return watch(\n    source,\n    (newValue) => targets.forEach((target) => target.value = newValue),\n    { flush, deep, immediate }\n  );\n}\nfunction toRefs2(objectRef, options = {}) {\n  if (!isRef(objectRef))\n    return toRefs(objectRef);\n  const result = Array.isArray(objectRef.value) ? Array.from({ length: objectRef.value.length }) : {};\n  for (const key in objectRef.value) {\n    result[key] = customRef(() => ({\n      get() {\n        return objectRef.value[key];\n      },\n      set(v) {\n        var _a;\n        const replaceRef = (_a = toValue(options.replaceRef)) != null ? _a : true;\n        if (replaceRef) {\n          if (Array.isArray(objectRef.value)) {\n            const copy = [...objectRef.value];\n            copy[key] = v;\n            objectRef.value = copy;\n          } else {\n            const newObject = { ...objectRef.value, [key]: v };\n            Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value));\n            objectRef.value = newObject;\n          }\n        } else {\n          objectRef.value[key] = v;\n        }\n      }\n    }));\n  }\n  return result;\n}\nvar toValue2 = toValue;\nvar resolveUnref = toValue;\nfunction tryOnBeforeMount(fn, sync = true, target) {\n  const instance = getLifeCycleTarget(target);\n  if (instance)\n    onBeforeMount(fn, target);\n  else if (sync)\n    fn();\n  else\n    nextTick(fn);\n}\nfunction tryOnBeforeUnmount(fn, target) {\n  const instance = getLifeCycleTarget(target);\n  if (instance)\n    onBeforeUnmount(fn, target);\n}\nfunction tryOnMounted(fn, sync = true, target) {\n  const instance = getLifeCycleTarget();\n  if (instance)\n    onMounted(fn, target);\n  else if (sync)\n    fn();\n  else\n    nextTick(fn);\n}\nfunction tryOnUnmounted(fn, target) {\n  const instance = getLifeCycleTarget(target);\n  if (instance)\n    onUnmounted(fn, target);\n}\nfunction createUntil(r, isNot = false) {\n  function toMatch(condition, { flush = \"sync\", deep = false, timeout, throwOnTimeout } = {}) {\n    let stop = null;\n    const watcher = new Promise((resolve) => {\n      stop = watch(\n        r,\n        (v) => {\n          if (condition(v) !== isNot) {\n            if (stop)\n              stop();\n            else\n              nextTick(() => stop == null ? void 0 : stop());\n            resolve(v);\n          }\n        },\n        {\n          flush,\n          deep,\n          immediate: true\n        }\n      );\n    });\n    const promises = [watcher];\n    if (timeout != null) {\n      promises.push(\n        promiseTimeout(timeout, throwOnTimeout).then(() => toValue(r)).finally(() => stop == null ? void 0 : stop())\n      );\n    }\n    return Promise.race(promises);\n  }\n  function toBe(value, options) {\n    if (!isRef(value))\n      return toMatch((v) => v === value, options);\n    const { flush = \"sync\", deep = false, timeout, throwOnTimeout } = options != null ? options : {};\n    let stop = null;\n    const watcher = new Promise((resolve) => {\n      stop = watch(\n        [r, value],\n        ([v1, v2]) => {\n          if (isNot !== (v1 === v2)) {\n            if (stop)\n              stop();\n            else\n              nextTick(() => stop == null ? void 0 : stop());\n            resolve(v1);\n          }\n        },\n        {\n          flush,\n          deep,\n          immediate: true\n        }\n      );\n    });\n    const promises = [watcher];\n    if (timeout != null) {\n      promises.push(\n        promiseTimeout(timeout, throwOnTimeout).then(() => toValue(r)).finally(() => {\n          stop == null ? void 0 : stop();\n          return toValue(r);\n        })\n      );\n    }\n    return Promise.race(promises);\n  }\n  function toBeTruthy(options) {\n    return toMatch((v) => Boolean(v), options);\n  }\n  function toBeNull(options) {\n    return toBe(null, options);\n  }\n  function toBeUndefined(options) {\n    return toBe(void 0, options);\n  }\n  function toBeNaN(options) {\n    return toMatch(Number.isNaN, options);\n  }\n  function toContains(value, options) {\n    return toMatch((v) => {\n      const array = Array.from(v);\n      return array.includes(value) || array.includes(toValue(value));\n    }, options);\n  }\n  function changed(options) {\n    return changedTimes(1, options);\n  }\n  function changedTimes(n = 1, options) {\n    let count = -1;\n    return toMatch(() => {\n      count += 1;\n      return count >= n;\n    }, options);\n  }\n  if (Array.isArray(toValue(r))) {\n    const instance = {\n      toMatch,\n      toContains,\n      changed,\n      changedTimes,\n      get not() {\n        return createUntil(r, !isNot);\n      }\n    };\n    return instance;\n  } else {\n    const instance = {\n      toMatch,\n      toBe,\n      toBeTruthy,\n      toBeNull,\n      toBeNaN,\n      toBeUndefined,\n      changed,\n      changedTimes,\n      get not() {\n        return createUntil(r, !isNot);\n      }\n    };\n    return instance;\n  }\n}\nfunction until(r) {\n  return createUntil(r);\n}\nfunction defaultComparator(value, othVal) {\n  return value === othVal;\n}\nfunction useArrayDifference(...args) {\n  var _a, _b;\n  const list = args[0];\n  const values = args[1];\n  let compareFn = (_a = args[2]) != null ? _a : defaultComparator;\n  const {\n    symmetric = false\n  } = (_b = args[3]) != null ? _b : {};\n  if (typeof compareFn === \"string\") {\n    const key = compareFn;\n    compareFn = (value, othVal) => value[key] === othVal[key];\n  }\n  const diff1 = computed(() => toValue(list).filter((x) => toValue(values).findIndex((y) => compareFn(x, y)) === -1));\n  if (symmetric) {\n    const diff2 = computed(() => toValue(values).filter((x) => toValue(list).findIndex((y) => compareFn(x, y)) === -1));\n    return computed(() => symmetric ? [...toValue(diff1), ...toValue(diff2)] : toValue(diff1));\n  } else {\n    return diff1;\n  }\n}\nfunction useArrayEvery(list, fn) {\n  return computed(() => toValue(list).every((element, index, array) => fn(toValue(element), index, array)));\n}\nfunction useArrayFilter(list, fn) {\n  return computed(() => toValue(list).map((i) => toValue(i)).filter(fn));\n}\nfunction useArrayFind(list, fn) {\n  return computed(() => toValue(\n    toValue(list).find((element, index, array) => fn(toValue(element), index, array))\n  ));\n}\nfunction useArrayFindIndex(list, fn) {\n  return computed(() => toValue(list).findIndex((element, index, array) => fn(toValue(element), index, array)));\n}\nfunction findLast(arr, cb) {\n  let index = arr.length;\n  while (index-- > 0) {\n    if (cb(arr[index], index, arr))\n      return arr[index];\n  }\n  return void 0;\n}\nfunction useArrayFindLast(list, fn) {\n  return computed(() => toValue(\n    !Array.prototype.findLast ? findLast(toValue(list), (element, index, array) => fn(toValue(element), index, array)) : toValue(list).findLast((element, index, array) => fn(toValue(element), index, array))\n  ));\n}\nfunction isArrayIncludesOptions(obj) {\n  return isObject(obj) && containsProp(obj, \"formIndex\", \"comparator\");\n}\nfunction useArrayIncludes(...args) {\n  var _a;\n  const list = args[0];\n  const value = args[1];\n  let comparator = args[2];\n  let formIndex = 0;\n  if (isArrayIncludesOptions(comparator)) {\n    formIndex = (_a = comparator.fromIndex) != null ? _a : 0;\n    comparator = comparator.comparator;\n  }\n  if (typeof comparator === \"string\") {\n    const key = comparator;\n    comparator = (element, value2) => element[key] === toValue(value2);\n  }\n  comparator = comparator != null ? comparator : (element, value2) => element === toValue(value2);\n  return computed(() => toValue(list).slice(formIndex).some((element, index, array) => comparator(\n    toValue(element),\n    toValue(value),\n    index,\n    toValue(array)\n  )));\n}\nfunction useArrayJoin(list, separator) {\n  return computed(() => toValue(list).map((i) => toValue(i)).join(toValue(separator)));\n}\nfunction useArrayMap(list, fn) {\n  return computed(() => toValue(list).map((i) => toValue(i)).map(fn));\n}\nfunction useArrayReduce(list, reducer, ...args) {\n  const reduceCallback = (sum, value, index) => reducer(toValue(sum), toValue(value), index);\n  return computed(() => {\n    const resolved = toValue(list);\n    return args.length ? resolved.reduce(reduceCallback, typeof args[0] === \"function\" ? toValue(args[0]()) : toValue(args[0])) : resolved.reduce(reduceCallback);\n  });\n}\nfunction useArraySome(list, fn) {\n  return computed(() => toValue(list).some((element, index, array) => fn(toValue(element), index, array)));\n}\nfunction uniq(array) {\n  return Array.from(new Set(array));\n}\nfunction uniqueElementsBy(array, fn) {\n  return array.reduce((acc, v) => {\n    if (!acc.some((x) => fn(v, x, array)))\n      acc.push(v);\n    return acc;\n  }, []);\n}\nfunction useArrayUnique(list, compareFn) {\n  return computed(() => {\n    const resolvedList = toValue(list).map((element) => toValue(element));\n    return compareFn ? uniqueElementsBy(resolvedList, compareFn) : uniq(resolvedList);\n  });\n}\nfunction useCounter(initialValue = 0, options = {}) {\n  let _initialValue = unref(initialValue);\n  const count = ref(initialValue);\n  const {\n    max = Number.POSITIVE_INFINITY,\n    min = Number.NEGATIVE_INFINITY\n  } = options;\n  const inc = (delta = 1) => count.value = Math.max(Math.min(max, count.value + delta), min);\n  const dec = (delta = 1) => count.value = Math.min(Math.max(min, count.value - delta), max);\n  const get2 = () => count.value;\n  const set2 = (val) => count.value = Math.max(min, Math.min(max, val));\n  const reset = (val = _initialValue) => {\n    _initialValue = val;\n    return set2(val);\n  };\n  return { count, inc, dec, get: get2, set: set2, reset };\n}\nvar REGEX_PARSE = /^(\\d{4})[-/]?(\\d{1,2})?[-/]?(\\d{0,2})[T\\s]*(\\d{1,2})?:?(\\d{1,2})?:?(\\d{1,2})?[.:]?(\\d+)?$/i;\nvar REGEX_FORMAT = /[YMDHhms]o|\\[([^\\]]+)\\]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a{1,2}|A{1,2}|m{1,2}|s{1,2}|Z{1,2}|SSS/g;\nfunction defaultMeridiem(hours, minutes, isLowercase, hasPeriod) {\n  let m = hours < 12 ? \"AM\" : \"PM\";\n  if (hasPeriod)\n    m = m.split(\"\").reduce((acc, curr) => acc += `${curr}.`, \"\");\n  return isLowercase ? m.toLowerCase() : m;\n}\nfunction formatOrdinal(num) {\n  const suffixes = [\"th\", \"st\", \"nd\", \"rd\"];\n  const v = num % 100;\n  return num + (suffixes[(v - 20) % 10] || suffixes[v] || suffixes[0]);\n}\nfunction formatDate(date, formatStr, options = {}) {\n  var _a;\n  const years = date.getFullYear();\n  const month = date.getMonth();\n  const days = date.getDate();\n  const hours = date.getHours();\n  const minutes = date.getMinutes();\n  const seconds = date.getSeconds();\n  const milliseconds = date.getMilliseconds();\n  const day = date.getDay();\n  const meridiem = (_a = options.customMeridiem) != null ? _a : defaultMeridiem;\n  const matches = {\n    Yo: () => formatOrdinal(years),\n    YY: () => String(years).slice(-2),\n    YYYY: () => years,\n    M: () => month + 1,\n    Mo: () => formatOrdinal(month + 1),\n    MM: () => `${month + 1}`.padStart(2, \"0\"),\n    MMM: () => date.toLocaleDateString(toValue(options.locales), { month: \"short\" }),\n    MMMM: () => date.toLocaleDateString(toValue(options.locales), { month: \"long\" }),\n    D: () => String(days),\n    Do: () => formatOrdinal(days),\n    DD: () => `${days}`.padStart(2, \"0\"),\n    H: () => String(hours),\n    Ho: () => formatOrdinal(hours),\n    HH: () => `${hours}`.padStart(2, \"0\"),\n    h: () => `${hours % 12 || 12}`.padStart(1, \"0\"),\n    ho: () => formatOrdinal(hours % 12 || 12),\n    hh: () => `${hours % 12 || 12}`.padStart(2, \"0\"),\n    m: () => String(minutes),\n    mo: () => formatOrdinal(minutes),\n    mm: () => `${minutes}`.padStart(2, \"0\"),\n    s: () => String(seconds),\n    so: () => formatOrdinal(seconds),\n    ss: () => `${seconds}`.padStart(2, \"0\"),\n    SSS: () => `${milliseconds}`.padStart(3, \"0\"),\n    d: () => day,\n    dd: () => date.toLocaleDateString(toValue(options.locales), { weekday: \"narrow\" }),\n    ddd: () => date.toLocaleDateString(toValue(options.locales), { weekday: \"short\" }),\n    dddd: () => date.toLocaleDateString(toValue(options.locales), { weekday: \"long\" }),\n    A: () => meridiem(hours, minutes),\n    AA: () => meridiem(hours, minutes, false, true),\n    a: () => meridiem(hours, minutes, true),\n    aa: () => meridiem(hours, minutes, true, true)\n  };\n  return formatStr.replace(REGEX_FORMAT, (match, $1) => {\n    var _a2, _b;\n    return (_b = $1 != null ? $1 : (_a2 = matches[match]) == null ? void 0 : _a2.call(matches)) != null ? _b : match;\n  });\n}\nfunction normalizeDate(date) {\n  if (date === null)\n    return new Date(Number.NaN);\n  if (date === void 0)\n    return /* @__PURE__ */ new Date();\n  if (date instanceof Date)\n    return new Date(date);\n  if (typeof date === \"string\" && !/Z$/i.test(date)) {\n    const d = date.match(REGEX_PARSE);\n    if (d) {\n      const m = d[2] - 1 || 0;\n      const ms = (d[7] || \"0\").substring(0, 3);\n      return new Date(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms);\n    }\n  }\n  return new Date(date);\n}\nfunction useDateFormat(date, formatStr = \"HH:mm:ss\", options = {}) {\n  return computed(() => formatDate(normalizeDate(toValue(date)), toValue(formatStr), options));\n}\nfunction useIntervalFn(cb, interval = 1e3, options = {}) {\n  const {\n    immediate = true,\n    immediateCallback = false\n  } = options;\n  let timer = null;\n  const isActive = ref(false);\n  function clean() {\n    if (timer) {\n      clearInterval(timer);\n      timer = null;\n    }\n  }\n  function pause() {\n    isActive.value = false;\n    clean();\n  }\n  function resume() {\n    const intervalValue = toValue(interval);\n    if (intervalValue <= 0)\n      return;\n    isActive.value = true;\n    if (immediateCallback)\n      cb();\n    clean();\n    if (isActive.value)\n      timer = setInterval(cb, intervalValue);\n  }\n  if (immediate && isClient)\n    resume();\n  if (isRef(interval) || typeof interval === \"function\") {\n    const stopWatch = watch(interval, () => {\n      if (isActive.value && isClient)\n        resume();\n    });\n    tryOnScopeDispose(stopWatch);\n  }\n  tryOnScopeDispose(pause);\n  return {\n    isActive,\n    pause,\n    resume\n  };\n}\nfunction useInterval(interval = 1e3, options = {}) {\n  const {\n    controls: exposeControls = false,\n    immediate = true,\n    callback\n  } = options;\n  const counter = ref(0);\n  const update = () => counter.value += 1;\n  const reset = () => {\n    counter.value = 0;\n  };\n  const controls = useIntervalFn(\n    callback ? () => {\n      update();\n      callback(counter.value);\n    } : update,\n    interval,\n    { immediate }\n  );\n  if (exposeControls) {\n    return {\n      counter,\n      reset,\n      ...controls\n    };\n  } else {\n    return counter;\n  }\n}\nfunction useLastChanged(source, options = {}) {\n  var _a;\n  const ms = ref((_a = options.initialValue) != null ? _a : null);\n  watch(\n    source,\n    () => ms.value = timestamp(),\n    options\n  );\n  return ms;\n}\nfunction useTimeoutFn(cb, interval, options = {}) {\n  const {\n    immediate = true\n  } = options;\n  const isPending = ref(false);\n  let timer = null;\n  function clear() {\n    if (timer) {\n      clearTimeout(timer);\n      timer = null;\n    }\n  }\n  function stop() {\n    isPending.value = false;\n    clear();\n  }\n  function start(...args) {\n    clear();\n    isPending.value = true;\n    timer = setTimeout(() => {\n      isPending.value = false;\n      timer = null;\n      cb(...args);\n    }, toValue(interval));\n  }\n  if (immediate) {\n    isPending.value = true;\n    if (isClient)\n      start();\n  }\n  tryOnScopeDispose(stop);\n  return {\n    isPending: readonly(isPending),\n    start,\n    stop\n  };\n}\nfunction useTimeout(interval = 1e3, options = {}) {\n  const {\n    controls: exposeControls = false,\n    callback\n  } = options;\n  const controls = useTimeoutFn(\n    callback != null ? callback : noop,\n    interval,\n    options\n  );\n  const ready = computed(() => !controls.isPending.value);\n  if (exposeControls) {\n    return {\n      ready,\n      ...controls\n    };\n  } else {\n    return ready;\n  }\n}\nfunction useToNumber(value, options = {}) {\n  const {\n    method = \"parseFloat\",\n    radix,\n    nanToZero\n  } = options;\n  return computed(() => {\n    let resolved = toValue(value);\n    if (typeof method === \"function\")\n      resolved = method(resolved);\n    else if (typeof resolved === \"string\")\n      resolved = Number[method](resolved, radix);\n    if (nanToZero && Number.isNaN(resolved))\n      resolved = 0;\n    return resolved;\n  });\n}\nfunction useToString(value) {\n  return computed(() => `${toValue(value)}`);\n}\nfunction useToggle(initialValue = false, options = {}) {\n  const {\n    truthyValue = true,\n    falsyValue = false\n  } = options;\n  const valueIsRef = isRef(initialValue);\n  const _value = ref(initialValue);\n  function toggle(value) {\n    if (arguments.length) {\n      _value.value = value;\n      return _value.value;\n    } else {\n      const truthy = toValue(truthyValue);\n      _value.value = _value.value === truthy ? toValue(falsyValue) : truthy;\n      return _value.value;\n    }\n  }\n  if (valueIsRef)\n    return toggle;\n  else\n    return [_value, toggle];\n}\nfunction watchArray(source, cb, options) {\n  let oldList = (options == null ? void 0 : options.immediate) ? [] : [...source instanceof Function ? source() : Array.isArray(source) ? source : toValue(source)];\n  return watch(source, (newList, _, onCleanup) => {\n    const oldListRemains = Array.from({ length: oldList.length });\n    const added = [];\n    for (const obj of newList) {\n      let found = false;\n      for (let i = 0; i < oldList.length; i++) {\n        if (!oldListRemains[i] && obj === oldList[i]) {\n          oldListRemains[i] = true;\n          found = true;\n          break;\n        }\n      }\n      if (!found)\n        added.push(obj);\n    }\n    const removed = oldList.filter((_2, i) => !oldListRemains[i]);\n    cb(newList, oldList, added, removed, onCleanup);\n    oldList = [...newList];\n  }, options);\n}\nfunction watchAtMost(source, cb, options) {\n  const {\n    count,\n    ...watchOptions\n  } = options;\n  const current = ref(0);\n  const stop = watchWithFilter(\n    source,\n    (...args) => {\n      current.value += 1;\n      if (current.value >= toValue(count))\n        nextTick(() => stop());\n      cb(...args);\n    },\n    watchOptions\n  );\n  return { count: current, stop };\n}\nfunction watchDebounced(source, cb, options = {}) {\n  const {\n    debounce = 0,\n    maxWait = void 0,\n    ...watchOptions\n  } = options;\n  return watchWithFilter(\n    source,\n    cb,\n    {\n      ...watchOptions,\n      eventFilter: debounceFilter(debounce, { maxWait })\n    }\n  );\n}\nfunction watchDeep(source, cb, options) {\n  return watch(\n    source,\n    cb,\n    {\n      ...options,\n      deep: true\n    }\n  );\n}\nfunction watchIgnorable(source, cb, options = {}) {\n  const {\n    eventFilter = bypassFilter,\n    ...watchOptions\n  } = options;\n  const filteredCb = createFilterWrapper(\n    eventFilter,\n    cb\n  );\n  let ignoreUpdates;\n  let ignorePrevAsyncUpdates;\n  let stop;\n  if (watchOptions.flush === \"sync\") {\n    const ignore = ref(false);\n    ignorePrevAsyncUpdates = () => {\n    };\n    ignoreUpdates = (updater) => {\n      ignore.value = true;\n      updater();\n      ignore.value = false;\n    };\n    stop = watch(\n      source,\n      (...args) => {\n        if (!ignore.value)\n          filteredCb(...args);\n      },\n      watchOptions\n    );\n  } else {\n    const disposables = [];\n    const ignoreCounter = ref(0);\n    const syncCounter = ref(0);\n    ignorePrevAsyncUpdates = () => {\n      ignoreCounter.value = syncCounter.value;\n    };\n    disposables.push(\n      watch(\n        source,\n        () => {\n          syncCounter.value++;\n        },\n        { ...watchOptions, flush: \"sync\" }\n      )\n    );\n    ignoreUpdates = (updater) => {\n      const syncCounterPrev = syncCounter.value;\n      updater();\n      ignoreCounter.value += syncCounter.value - syncCounterPrev;\n    };\n    disposables.push(\n      watch(\n        source,\n        (...args) => {\n          const ignore = ignoreCounter.value > 0 && ignoreCounter.value === syncCounter.value;\n          ignoreCounter.value = 0;\n          syncCounter.value = 0;\n          if (ignore)\n            return;\n          filteredCb(...args);\n        },\n        watchOptions\n      )\n    );\n    stop = () => {\n      disposables.forEach((fn) => fn());\n    };\n  }\n  return { stop, ignoreUpdates, ignorePrevAsyncUpdates };\n}\nfunction watchImmediate(source, cb, options) {\n  return watch(\n    source,\n    cb,\n    {\n      ...options,\n      immediate: true\n    }\n  );\n}\nfunction watchOnce(source, cb, options) {\n  const stop = watch(source, (...args) => {\n    nextTick(() => stop());\n    return cb(...args);\n  }, options);\n  return stop;\n}\nfunction watchThrottled(source, cb, options = {}) {\n  const {\n    throttle = 0,\n    trailing = true,\n    leading = true,\n    ...watchOptions\n  } = options;\n  return watchWithFilter(\n    source,\n    cb,\n    {\n      ...watchOptions,\n      eventFilter: throttleFilter(throttle, trailing, leading)\n    }\n  );\n}\nfunction watchTriggerable(source, cb, options = {}) {\n  let cleanupFn;\n  function onEffect() {\n    if (!cleanupFn)\n      return;\n    const fn = cleanupFn;\n    cleanupFn = void 0;\n    fn();\n  }\n  function onCleanup(callback) {\n    cleanupFn = callback;\n  }\n  const _cb = (value, oldValue) => {\n    onEffect();\n    return cb(value, oldValue, onCleanup);\n  };\n  const res = watchIgnorable(source, _cb, options);\n  const { ignoreUpdates } = res;\n  const trigger = () => {\n    let res2;\n    ignoreUpdates(() => {\n      res2 = _cb(getWatchSources(source), getOldValue(source));\n    });\n    return res2;\n  };\n  return {\n    ...res,\n    trigger\n  };\n}\nfunction getWatchSources(sources) {\n  if (isReactive(sources))\n    return sources;\n  if (Array.isArray(sources))\n    return sources.map((item) => toValue(item));\n  return toValue(sources);\n}\nfunction getOldValue(source) {\n  return Array.isArray(source) ? source.map(() => void 0) : void 0;\n}\nfunction whenever(source, cb, options) {\n  const stop = watch(\n    source,\n    (v, ov, onInvalidate) => {\n      if (v) {\n        if (options == null ? void 0 : options.once)\n          nextTick(() => stop());\n        cb(v, ov, onInvalidate);\n      }\n    },\n    {\n      ...options,\n      once: false\n    }\n  );\n  return stop;\n}\n\n// node_modules/.pnpm/@vueuse+core@12.5.0/node_modules/@vueuse/core/index.mjs\nfunction computedAsync(evaluationCallback, initialState, optionsOrRef) {\n  let options;\n  if (isRef(optionsOrRef)) {\n    options = {\n      evaluating: optionsOrRef\n    };\n  } else {\n    options = optionsOrRef || {};\n  }\n  const {\n    lazy = false,\n    evaluating = void 0,\n    shallow = true,\n    onError = noop\n  } = options;\n  const started = ref(!lazy);\n  const current = shallow ? shallowRef(initialState) : ref(initialState);\n  let counter = 0;\n  watchEffect(async (onInvalidate) => {\n    if (!started.value)\n      return;\n    counter++;\n    const counterAtBeginning = counter;\n    let hasFinished = false;\n    if (evaluating) {\n      Promise.resolve().then(() => {\n        evaluating.value = true;\n      });\n    }\n    try {\n      const result = await evaluationCallback((cancelCallback) => {\n        onInvalidate(() => {\n          if (evaluating)\n            evaluating.value = false;\n          if (!hasFinished)\n            cancelCallback();\n        });\n      });\n      if (counterAtBeginning === counter)\n        current.value = result;\n    } catch (e) {\n      onError(e);\n    } finally {\n      if (evaluating && counterAtBeginning === counter)\n        evaluating.value = false;\n      hasFinished = true;\n    }\n  });\n  if (lazy) {\n    return computed(() => {\n      started.value = true;\n      return current.value;\n    });\n  } else {\n    return current;\n  }\n}\nfunction computedInject(key, options, defaultSource, treatDefaultAsFactory) {\n  let source = inject(key);\n  if (defaultSource)\n    source = inject(key, defaultSource);\n  if (treatDefaultAsFactory)\n    source = inject(key, defaultSource, treatDefaultAsFactory);\n  if (typeof options === \"function\") {\n    return computed((ctx) => options(source, ctx));\n  } else {\n    return computed({\n      get: (ctx) => options.get(source, ctx),\n      set: options.set\n    });\n  }\n}\nfunction createReusableTemplate(options = {}) {\n  const {\n    inheritAttrs = true\n  } = options;\n  const render = shallowRef();\n  const define = defineComponent({\n    setup(_, { slots }) {\n      return () => {\n        render.value = slots.default;\n      };\n    }\n  });\n  const reuse = defineComponent({\n    inheritAttrs,\n    setup(_, { attrs, slots }) {\n      return () => {\n        var _a;\n        if (!render.value && true)\n          throw new Error(\"[VueUse] Failed to find the definition of reusable template\");\n        const vnode = (_a = render.value) == null ? void 0 : _a.call(render, { ...keysToCamelKebabCase(attrs), $slots: slots });\n        return inheritAttrs && (vnode == null ? void 0 : vnode.length) === 1 ? vnode[0] : vnode;\n      };\n    }\n  });\n  return makeDestructurable(\n    { define, reuse },\n    [define, reuse]\n  );\n}\nfunction keysToCamelKebabCase(obj) {\n  const newObj = {};\n  for (const key in obj)\n    newObj[camelize(key)] = obj[key];\n  return newObj;\n}\nfunction createTemplatePromise(options = {}) {\n  let index = 0;\n  const instances = ref([]);\n  function create(...args) {\n    const props = shallowReactive({\n      key: index++,\n      args,\n      promise: void 0,\n      resolve: () => {\n      },\n      reject: () => {\n      },\n      isResolving: false,\n      options\n    });\n    instances.value.push(props);\n    props.promise = new Promise((_resolve, _reject) => {\n      props.resolve = (v) => {\n        props.isResolving = true;\n        return _resolve(v);\n      };\n      props.reject = _reject;\n    }).finally(() => {\n      props.promise = void 0;\n      const index2 = instances.value.indexOf(props);\n      if (index2 !== -1)\n        instances.value.splice(index2, 1);\n    });\n    return props.promise;\n  }\n  function start(...args) {\n    if (options.singleton && instances.value.length > 0)\n      return instances.value[0].promise;\n    return create(...args);\n  }\n  const component = defineComponent((_, { slots }) => {\n    const renderList = () => instances.value.map((props) => {\n      var _a;\n      return h(Fragment, { key: props.key }, (_a = slots.default) == null ? void 0 : _a.call(slots, props));\n    });\n    if (options.transition)\n      return () => h(TransitionGroup, options.transition, renderList);\n    return renderList;\n  });\n  component.start = start;\n  return component;\n}\nfunction createUnrefFn(fn) {\n  return function(...args) {\n    return fn.apply(this, args.map((i) => toValue(i)));\n  };\n}\nvar defaultWindow = isClient ? window : void 0;\nvar defaultDocument = isClient ? window.document : void 0;\nvar defaultNavigator = isClient ? window.navigator : void 0;\nvar defaultLocation = isClient ? window.location : void 0;\nfunction unrefElement(elRef) {\n  var _a;\n  const plain = toValue(elRef);\n  return (_a = plain == null ? void 0 : plain.$el) != null ? _a : plain;\n}\nfunction useEventListener(...args) {\n  const cleanups = [];\n  const cleanup = () => {\n    cleanups.forEach((fn) => fn());\n    cleanups.length = 0;\n  };\n  const register = (el, event, listener, options) => {\n    el.addEventListener(event, listener, options);\n    return () => el.removeEventListener(event, listener, options);\n  };\n  const firstParamTargets = computed(() => {\n    const test = toArray(toValue(args[0])).filter((e) => e != null);\n    return test.every((e) => typeof e !== \"string\") ? test : void 0;\n  });\n  const stopWatch = watchImmediate(\n    () => {\n      var _a, _b;\n      return [\n        (_b = (_a = firstParamTargets.value) == null ? void 0 : _a.map((e) => unrefElement(e))) != null ? _b : [defaultWindow].filter((e) => e != null),\n        toArray(toValue(firstParamTargets.value ? args[1] : args[0])),\n        toArray(unref(firstParamTargets.value ? args[2] : args[1])),\n        // @ts-expect-error - TypeScript gets the correct types, but somehow still complains\n        toValue(firstParamTargets.value ? args[3] : args[2])\n      ];\n    },\n    ([raw_targets, raw_events, raw_listeners, raw_options]) => {\n      cleanup();\n      if (!(raw_targets == null ? void 0 : raw_targets.length) || !(raw_events == null ? void 0 : raw_events.length) || !(raw_listeners == null ? void 0 : raw_listeners.length))\n        return;\n      const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;\n      cleanups.push(\n        ...raw_targets.flatMap(\n          (el) => raw_events.flatMap(\n            (event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))\n          )\n        )\n      );\n    },\n    { flush: \"post\" }\n  );\n  const stop = () => {\n    stopWatch();\n    cleanup();\n  };\n  tryOnScopeDispose(cleanup);\n  return stop;\n}\nvar _iOSWorkaround = false;\nfunction onClickOutside(target, handler, options = {}) {\n  const { window: window2 = defaultWindow, ignore = [], capture = true, detectIframe = false } = options;\n  if (!window2)\n    return noop;\n  if (isIOS && !_iOSWorkaround) {\n    _iOSWorkaround = true;\n    const listenerOptions = { passive: true };\n    Array.from(window2.document.body.children).forEach((el) => useEventListener(el, \"click\", noop, listenerOptions));\n    useEventListener(window2.document.documentElement, \"click\", noop, listenerOptions);\n  }\n  let shouldListen = true;\n  const shouldIgnore = (event) => {\n    return toValue(ignore).some((target2) => {\n      if (typeof target2 === \"string\") {\n        return Array.from(window2.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el));\n      } else {\n        const el = unrefElement(target2);\n        return el && (event.target === el || event.composedPath().includes(el));\n      }\n    });\n  };\n  function hasMultipleRoots(target2) {\n    const vm = toValue(target2);\n    return vm && vm.$.subTree.shapeFlag === 16;\n  }\n  function checkMultipleRoots(target2, event) {\n    const vm = toValue(target2);\n    const children = vm.$.subTree && vm.$.subTree.children;\n    if (children == null || !Array.isArray(children))\n      return false;\n    return children.some((child) => child.el === event.target || event.composedPath().includes(child.el));\n  }\n  const listener = (event) => {\n    const el = unrefElement(target);\n    if (event.target == null)\n      return;\n    if (!(el instanceof Element) && hasMultipleRoots(target) && checkMultipleRoots(target, event))\n      return;\n    if (!el || el === event.target || event.composedPath().includes(el))\n      return;\n    if (event.detail === 0)\n      shouldListen = !shouldIgnore(event);\n    if (!shouldListen) {\n      shouldListen = true;\n      return;\n    }\n    handler(event);\n  };\n  let isProcessingClick = false;\n  const cleanup = [\n    useEventListener(window2, \"click\", (event) => {\n      if (!isProcessingClick) {\n        isProcessingClick = true;\n        setTimeout(() => {\n          isProcessingClick = false;\n        }, 0);\n        listener(event);\n      }\n    }, { passive: true, capture }),\n    useEventListener(window2, \"pointerdown\", (e) => {\n      const el = unrefElement(target);\n      shouldListen = !shouldIgnore(e) && !!(el && !e.composedPath().includes(el));\n    }, { passive: true }),\n    detectIframe && useEventListener(window2, \"blur\", (event) => {\n      setTimeout(() => {\n        var _a;\n        const el = unrefElement(target);\n        if (((_a = window2.document.activeElement) == null ? void 0 : _a.tagName) === \"IFRAME\" && !(el == null ? void 0 : el.contains(window2.document.activeElement))) {\n          handler(event);\n        }\n      }, 0);\n    }, { passive: true })\n  ].filter(Boolean);\n  const stop = () => cleanup.forEach((fn) => fn());\n  return stop;\n}\nfunction useMounted() {\n  const isMounted = ref(false);\n  const instance = getCurrentInstance();\n  if (instance) {\n    onMounted(() => {\n      isMounted.value = true;\n    }, instance);\n  }\n  return isMounted;\n}\nfunction useSupported(callback) {\n  const isMounted = useMounted();\n  return computed(() => {\n    isMounted.value;\n    return Boolean(callback());\n  });\n}\nfunction useMutationObserver(target, callback, options = {}) {\n  const { window: window2 = defaultWindow, ...mutationOptions } = options;\n  let observer;\n  const isSupported = useSupported(() => window2 && \"MutationObserver\" in window2);\n  const cleanup = () => {\n    if (observer) {\n      observer.disconnect();\n      observer = void 0;\n    }\n  };\n  const targets = computed(() => {\n    const value = toValue(target);\n    const items = toArray(value).map(unrefElement).filter(notNullish);\n    return new Set(items);\n  });\n  const stopWatch = watch(\n    () => targets.value,\n    (targets2) => {\n      cleanup();\n      if (isSupported.value && targets2.size) {\n        observer = new MutationObserver(callback);\n        targets2.forEach((el) => observer.observe(el, mutationOptions));\n      }\n    },\n    { immediate: true, flush: \"post\" }\n  );\n  const takeRecords = () => {\n    return observer == null ? void 0 : observer.takeRecords();\n  };\n  const stop = () => {\n    stopWatch();\n    cleanup();\n  };\n  tryOnScopeDispose(stop);\n  return {\n    isSupported,\n    stop,\n    takeRecords\n  };\n}\nfunction onElementRemoval(target, callback, options = {}) {\n  const {\n    window: window2 = defaultWindow,\n    document: document2 = window2 == null ? void 0 : window2.document,\n    flush = \"sync\"\n  } = options;\n  if (!window2 || !document2)\n    return noop;\n  let stopFn;\n  const cleanupAndUpdate = (fn) => {\n    stopFn == null ? void 0 : stopFn();\n    stopFn = fn;\n  };\n  const stopWatch = watchEffect(() => {\n    const el = unrefElement(target);\n    if (el) {\n      const { stop } = useMutationObserver(\n        document2,\n        (mutationsList) => {\n          const targetRemoved = mutationsList.map((mutation) => [...mutation.removedNodes]).flat().some((node) => node === el || node.contains(el));\n          if (targetRemoved) {\n            callback(mutationsList);\n          }\n        },\n        {\n          window: window2,\n          childList: true,\n          subtree: true\n        }\n      );\n      cleanupAndUpdate(stop);\n    }\n  }, { flush });\n  const stopHandle = () => {\n    stopWatch();\n    cleanupAndUpdate();\n  };\n  tryOnScopeDispose(stopHandle);\n  return stopHandle;\n}\nfunction createKeyPredicate(keyFilter) {\n  if (typeof keyFilter === \"function\")\n    return keyFilter;\n  else if (typeof keyFilter === \"string\")\n    return (event) => event.key === keyFilter;\n  else if (Array.isArray(keyFilter))\n    return (event) => keyFilter.includes(event.key);\n  return () => true;\n}\nfunction onKeyStroke(...args) {\n  let key;\n  let handler;\n  let options = {};\n  if (args.length === 3) {\n    key = args[0];\n    handler = args[1];\n    options = args[2];\n  } else if (args.length === 2) {\n    if (typeof args[1] === \"object\") {\n      key = true;\n      handler = args[0];\n      options = args[1];\n    } else {\n      key = args[0];\n      handler = args[1];\n    }\n  } else {\n    key = true;\n    handler = args[0];\n  }\n  const {\n    target = defaultWindow,\n    eventName = \"keydown\",\n    passive = false,\n    dedupe = false\n  } = options;\n  const predicate = createKeyPredicate(key);\n  const listener = (e) => {\n    if (e.repeat && toValue(dedupe))\n      return;\n    if (predicate(e))\n      handler(e);\n  };\n  return useEventListener(target, eventName, listener, passive);\n}\nfunction onKeyDown(key, handler, options = {}) {\n  return onKeyStroke(key, handler, { ...options, eventName: \"keydown\" });\n}\nfunction onKeyPressed(key, handler, options = {}) {\n  return onKeyStroke(key, handler, { ...options, eventName: \"keypress\" });\n}\nfunction onKeyUp(key, handler, options = {}) {\n  return onKeyStroke(key, handler, { ...options, eventName: \"keyup\" });\n}\nvar DEFAULT_DELAY = 500;\nvar DEFAULT_THRESHOLD = 10;\nfunction onLongPress(target, handler, options) {\n  var _a, _b;\n  const elementRef = computed(() => unrefElement(target));\n  let timeout;\n  let posStart;\n  let startTimestamp;\n  let hasLongPressed = false;\n  function clear() {\n    if (timeout) {\n      clearTimeout(timeout);\n      timeout = void 0;\n    }\n    posStart = void 0;\n    startTimestamp = void 0;\n    hasLongPressed = false;\n  }\n  function onRelease(ev) {\n    var _a2, _b2, _c;\n    const [_startTimestamp, _posStart, _hasLongPressed] = [startTimestamp, posStart, hasLongPressed];\n    clear();\n    if (!(options == null ? void 0 : options.onMouseUp) || !_posStart || !_startTimestamp)\n      return;\n    if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n      return;\n    if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n      ev.preventDefault();\n    if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n      ev.stopPropagation();\n    const dx = ev.x - _posStart.x;\n    const dy = ev.y - _posStart.y;\n    const distance = Math.sqrt(dx * dx + dy * dy);\n    options.onMouseUp(ev.timeStamp - _startTimestamp, distance, _hasLongPressed);\n  }\n  function onDown(ev) {\n    var _a2, _b2, _c, _d;\n    if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n      return;\n    clear();\n    if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n      ev.preventDefault();\n    if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n      ev.stopPropagation();\n    posStart = {\n      x: ev.x,\n      y: ev.y\n    };\n    startTimestamp = ev.timeStamp;\n    timeout = setTimeout(\n      () => {\n        hasLongPressed = true;\n        handler(ev);\n      },\n      (_d = options == null ? void 0 : options.delay) != null ? _d : DEFAULT_DELAY\n    );\n  }\n  function onMove(ev) {\n    var _a2, _b2, _c, _d;\n    if (((_a2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _a2.self) && ev.target !== elementRef.value)\n      return;\n    if (!posStart || (options == null ? void 0 : options.distanceThreshold) === false)\n      return;\n    if ((_b2 = options == null ? void 0 : options.modifiers) == null ? void 0 : _b2.prevent)\n      ev.preventDefault();\n    if ((_c = options == null ? void 0 : options.modifiers) == null ? void 0 : _c.stop)\n      ev.stopPropagation();\n    const dx = ev.x - posStart.x;\n    const dy = ev.y - posStart.y;\n    const distance = Math.sqrt(dx * dx + dy * dy);\n    if (distance >= ((_d = options == null ? void 0 : options.distanceThreshold) != null ? _d : DEFAULT_THRESHOLD))\n      clear();\n  }\n  const listenerOptions = {\n    capture: (_a = options == null ? void 0 : options.modifiers) == null ? void 0 : _a.capture,\n    once: (_b = options == null ? void 0 : options.modifiers) == null ? void 0 : _b.once\n  };\n  const cleanup = [\n    useEventListener(elementRef, \"pointerdown\", onDown, listenerOptions),\n    useEventListener(elementRef, \"pointermove\", onMove, listenerOptions),\n    useEventListener(elementRef, [\"pointerup\", \"pointerleave\"], onRelease, listenerOptions)\n  ];\n  const stop = () => cleanup.forEach((fn) => fn());\n  return stop;\n}\nfunction isFocusedElementEditable() {\n  const { activeElement, body } = document;\n  if (!activeElement)\n    return false;\n  if (activeElement === body)\n    return false;\n  switch (activeElement.tagName) {\n    case \"INPUT\":\n    case \"TEXTAREA\":\n      return true;\n  }\n  return activeElement.hasAttribute(\"contenteditable\");\n}\nfunction isTypedCharValid({\n  keyCode,\n  metaKey,\n  ctrlKey,\n  altKey\n}) {\n  if (metaKey || ctrlKey || altKey)\n    return false;\n  if (keyCode >= 48 && keyCode <= 57)\n    return true;\n  if (keyCode >= 65 && keyCode <= 90)\n    return true;\n  if (keyCode >= 97 && keyCode <= 122)\n    return true;\n  return false;\n}\nfunction onStartTyping(callback, options = {}) {\n  const { document: document2 = defaultDocument } = options;\n  const keydown = (event) => {\n    if (!isFocusedElementEditable() && isTypedCharValid(event)) {\n      callback(event);\n    }\n  };\n  if (document2)\n    useEventListener(document2, \"keydown\", keydown, { passive: true });\n}\nfunction templateRef(key, initialValue = null) {\n  const instance = getCurrentInstance();\n  let _trigger = () => {\n  };\n  const element = customRef((track, trigger) => {\n    _trigger = trigger;\n    return {\n      get() {\n        var _a, _b;\n        track();\n        return (_b = (_a = instance == null ? void 0 : instance.proxy) == null ? void 0 : _a.$refs[key]) != null ? _b : initialValue;\n      },\n      set() {\n      }\n    };\n  });\n  tryOnMounted(_trigger);\n  onUpdated(_trigger);\n  return element;\n}\nfunction useActiveElement(options = {}) {\n  var _a;\n  const {\n    window: window2 = defaultWindow,\n    deep = true,\n    triggerOnRemoval = false\n  } = options;\n  const document2 = (_a = options.document) != null ? _a : window2 == null ? void 0 : window2.document;\n  const getDeepActiveElement = () => {\n    var _a2;\n    let element = document2 == null ? void 0 : document2.activeElement;\n    if (deep) {\n      while (element == null ? void 0 : element.shadowRoot)\n        element = (_a2 = element == null ? void 0 : element.shadowRoot) == null ? void 0 : _a2.activeElement;\n    }\n    return element;\n  };\n  const activeElement = ref();\n  const trigger = () => {\n    activeElement.value = getDeepActiveElement();\n  };\n  if (window2) {\n    const listenerOptions = {\n      capture: true,\n      passive: true\n    };\n    useEventListener(\n      window2,\n      \"blur\",\n      (event) => {\n        if (event.relatedTarget !== null)\n          return;\n        trigger();\n      },\n      listenerOptions\n    );\n    useEventListener(\n      window2,\n      \"focus\",\n      trigger,\n      listenerOptions\n    );\n  }\n  if (triggerOnRemoval) {\n    onElementRemoval(activeElement, trigger, { document: document2 });\n  }\n  trigger();\n  return activeElement;\n}\nfunction useRafFn(fn, options = {}) {\n  const {\n    immediate = true,\n    fpsLimit = void 0,\n    window: window2 = defaultWindow\n  } = options;\n  const isActive = ref(false);\n  const intervalLimit = computed(() => {\n    return fpsLimit ? 1e3 / toValue(fpsLimit) : null;\n  });\n  let previousFrameTimestamp = 0;\n  let rafId = null;\n  function loop(timestamp2) {\n    if (!isActive.value || !window2)\n      return;\n    if (!previousFrameTimestamp)\n      previousFrameTimestamp = timestamp2;\n    const delta = timestamp2 - previousFrameTimestamp;\n    if (intervalLimit.value && delta < intervalLimit.value) {\n      rafId = window2.requestAnimationFrame(loop);\n      return;\n    }\n    previousFrameTimestamp = timestamp2;\n    fn({ delta, timestamp: timestamp2 });\n    rafId = window2.requestAnimationFrame(loop);\n  }\n  function resume() {\n    if (!isActive.value && window2) {\n      isActive.value = true;\n      previousFrameTimestamp = 0;\n      rafId = window2.requestAnimationFrame(loop);\n    }\n  }\n  function pause() {\n    isActive.value = false;\n    if (rafId != null && window2) {\n      window2.cancelAnimationFrame(rafId);\n      rafId = null;\n    }\n  }\n  if (immediate)\n    resume();\n  tryOnScopeDispose(pause);\n  return {\n    isActive: readonly(isActive),\n    pause,\n    resume\n  };\n}\nfunction useAnimate(target, keyframes, options) {\n  let config;\n  let animateOptions;\n  if (isObject(options)) {\n    config = options;\n    animateOptions = objectOmit(options, [\"window\", \"immediate\", \"commitStyles\", \"persist\", \"onReady\", \"onError\"]);\n  } else {\n    config = { duration: options };\n    animateOptions = options;\n  }\n  const {\n    window: window2 = defaultWindow,\n    immediate = true,\n    commitStyles,\n    persist,\n    playbackRate: _playbackRate = 1,\n    onReady,\n    onError = (e) => {\n      console.error(e);\n    }\n  } = config;\n  const isSupported = useSupported(() => window2 && HTMLElement && \"animate\" in HTMLElement.prototype);\n  const animate = shallowRef(void 0);\n  const store = shallowReactive({\n    startTime: null,\n    currentTime: null,\n    timeline: null,\n    playbackRate: _playbackRate,\n    pending: false,\n    playState: immediate ? \"idle\" : \"paused\",\n    replaceState: \"active\"\n  });\n  const pending = computed(() => store.pending);\n  const playState = computed(() => store.playState);\n  const replaceState = computed(() => store.replaceState);\n  const startTime = computed({\n    get() {\n      return store.startTime;\n    },\n    set(value) {\n      store.startTime = value;\n      if (animate.value)\n        animate.value.startTime = value;\n    }\n  });\n  const currentTime = computed({\n    get() {\n      return store.currentTime;\n    },\n    set(value) {\n      store.currentTime = value;\n      if (animate.value) {\n        animate.value.currentTime = value;\n        syncResume();\n      }\n    }\n  });\n  const timeline = computed({\n    get() {\n      return store.timeline;\n    },\n    set(value) {\n      store.timeline = value;\n      if (animate.value)\n        animate.value.timeline = value;\n    }\n  });\n  const playbackRate = computed({\n    get() {\n      return store.playbackRate;\n    },\n    set(value) {\n      store.playbackRate = value;\n      if (animate.value)\n        animate.value.playbackRate = value;\n    }\n  });\n  const play = () => {\n    if (animate.value) {\n      try {\n        animate.value.play();\n        syncResume();\n      } catch (e) {\n        syncPause();\n        onError(e);\n      }\n    } else {\n      update();\n    }\n  };\n  const pause = () => {\n    var _a;\n    try {\n      (_a = animate.value) == null ? void 0 : _a.pause();\n      syncPause();\n    } catch (e) {\n      onError(e);\n    }\n  };\n  const reverse = () => {\n    var _a;\n    if (!animate.value)\n      update();\n    try {\n      (_a = animate.value) == null ? void 0 : _a.reverse();\n      syncResume();\n    } catch (e) {\n      syncPause();\n      onError(e);\n    }\n  };\n  const finish = () => {\n    var _a;\n    try {\n      (_a = animate.value) == null ? void 0 : _a.finish();\n      syncPause();\n    } catch (e) {\n      onError(e);\n    }\n  };\n  const cancel = () => {\n    var _a;\n    try {\n      (_a = animate.value) == null ? void 0 : _a.cancel();\n      syncPause();\n    } catch (e) {\n      onError(e);\n    }\n  };\n  watch(() => unrefElement(target), (el) => {\n    if (el)\n      update();\n  });\n  watch(() => keyframes, (value) => {\n    if (animate.value)\n      update();\n    if (!unrefElement(target) && animate.value) {\n      animate.value.effect = new KeyframeEffect(\n        unrefElement(target),\n        toValue(value),\n        animateOptions\n      );\n    }\n  }, { deep: true });\n  tryOnMounted(() => update(true), false);\n  tryOnScopeDispose(cancel);\n  function update(init) {\n    const el = unrefElement(target);\n    if (!isSupported.value || !el)\n      return;\n    if (!animate.value)\n      animate.value = el.animate(toValue(keyframes), animateOptions);\n    if (persist)\n      animate.value.persist();\n    if (_playbackRate !== 1)\n      animate.value.playbackRate = _playbackRate;\n    if (init && !immediate)\n      animate.value.pause();\n    else\n      syncResume();\n    onReady == null ? void 0 : onReady(animate.value);\n  }\n  const listenerOptions = { passive: true };\n  useEventListener(animate, [\"cancel\", \"finish\", \"remove\"], syncPause, listenerOptions);\n  useEventListener(animate, \"finish\", () => {\n    var _a;\n    if (commitStyles)\n      (_a = animate.value) == null ? void 0 : _a.commitStyles();\n  }, listenerOptions);\n  const { resume: resumeRef, pause: pauseRef } = useRafFn(() => {\n    if (!animate.value)\n      return;\n    store.pending = animate.value.pending;\n    store.playState = animate.value.playState;\n    store.replaceState = animate.value.replaceState;\n    store.startTime = animate.value.startTime;\n    store.currentTime = animate.value.currentTime;\n    store.timeline = animate.value.timeline;\n    store.playbackRate = animate.value.playbackRate;\n  }, { immediate: false });\n  function syncResume() {\n    if (isSupported.value)\n      resumeRef();\n  }\n  function syncPause() {\n    if (isSupported.value && window2)\n      window2.requestAnimationFrame(pauseRef);\n  }\n  return {\n    isSupported,\n    animate,\n    // actions\n    play,\n    pause,\n    reverse,\n    finish,\n    cancel,\n    // state\n    pending,\n    playState,\n    replaceState,\n    startTime,\n    currentTime,\n    timeline,\n    playbackRate\n  };\n}\nfunction useAsyncQueue(tasks, options) {\n  const {\n    interrupt = true,\n    onError = noop,\n    onFinished = noop,\n    signal\n  } = options || {};\n  const promiseState = {\n    aborted: \"aborted\",\n    fulfilled: \"fulfilled\",\n    pending: \"pending\",\n    rejected: \"rejected\"\n  };\n  const initialResult = Array.from(Array.from({ length: tasks.length }), () => ({ state: promiseState.pending, data: null }));\n  const result = reactive(initialResult);\n  const activeIndex = ref(-1);\n  if (!tasks || tasks.length === 0) {\n    onFinished();\n    return {\n      activeIndex,\n      result\n    };\n  }\n  function updateResult(state, res) {\n    activeIndex.value++;\n    result[activeIndex.value].data = res;\n    result[activeIndex.value].state = state;\n  }\n  tasks.reduce((prev, curr) => {\n    return prev.then((prevRes) => {\n      var _a;\n      if (signal == null ? void 0 : signal.aborted) {\n        updateResult(promiseState.aborted, new Error(\"aborted\"));\n        return;\n      }\n      if (((_a = result[activeIndex.value]) == null ? void 0 : _a.state) === promiseState.rejected && interrupt) {\n        onFinished();\n        return;\n      }\n      const done = curr(prevRes).then((currentRes) => {\n        updateResult(promiseState.fulfilled, currentRes);\n        if (activeIndex.value === tasks.length - 1)\n          onFinished();\n        return currentRes;\n      });\n      if (!signal)\n        return done;\n      return Promise.race([done, whenAborted(signal)]);\n    }).catch((e) => {\n      if (signal == null ? void 0 : signal.aborted) {\n        updateResult(promiseState.aborted, e);\n        return e;\n      }\n      updateResult(promiseState.rejected, e);\n      onError();\n      return e;\n    });\n  }, Promise.resolve());\n  return {\n    activeIndex,\n    result\n  };\n}\nfunction whenAborted(signal) {\n  return new Promise((resolve, reject) => {\n    const error = new Error(\"aborted\");\n    if (signal.aborted)\n      reject(error);\n    else\n      signal.addEventListener(\"abort\", () => reject(error), { once: true });\n  });\n}\nfunction useAsyncState(promise, initialState, options) {\n  const {\n    immediate = true,\n    delay = 0,\n    onError = noop,\n    onSuccess = noop,\n    resetOnExecute = true,\n    shallow = true,\n    throwError\n  } = options != null ? options : {};\n  const state = shallow ? shallowRef(initialState) : ref(initialState);\n  const isReady = ref(false);\n  const isLoading = ref(false);\n  const error = shallowRef(void 0);\n  async function execute(delay2 = 0, ...args) {\n    if (resetOnExecute)\n      state.value = initialState;\n    error.value = void 0;\n    isReady.value = false;\n    isLoading.value = true;\n    if (delay2 > 0)\n      await promiseTimeout(delay2);\n    const _promise = typeof promise === \"function\" ? promise(...args) : promise;\n    try {\n      const data = await _promise;\n      state.value = data;\n      isReady.value = true;\n      onSuccess(data);\n    } catch (e) {\n      error.value = e;\n      onError(e);\n      if (throwError)\n        throw e;\n    } finally {\n      isLoading.value = false;\n    }\n    return state.value;\n  }\n  if (immediate) {\n    execute(delay);\n  }\n  const shell = {\n    state,\n    isReady,\n    isLoading,\n    error,\n    execute\n  };\n  function waitUntilIsLoaded() {\n    return new Promise((resolve, reject) => {\n      until(isLoading).toBe(false).then(() => resolve(shell)).catch(reject);\n    });\n  }\n  return {\n    ...shell,\n    then(onFulfilled, onRejected) {\n      return waitUntilIsLoaded().then(onFulfilled, onRejected);\n    }\n  };\n}\nvar defaults = {\n  array: (v) => JSON.stringify(v),\n  object: (v) => JSON.stringify(v),\n  set: (v) => JSON.stringify(Array.from(v)),\n  map: (v) => JSON.stringify(Object.fromEntries(v)),\n  null: () => \"\"\n};\nfunction getDefaultSerialization(target) {\n  if (!target)\n    return defaults.null;\n  if (target instanceof Map)\n    return defaults.map;\n  else if (target instanceof Set)\n    return defaults.set;\n  else if (Array.isArray(target))\n    return defaults.array;\n  else\n    return defaults.object;\n}\nfunction useBase64(target, options) {\n  const base64 = ref(\"\");\n  const promise = ref();\n  function execute() {\n    if (!isClient)\n      return;\n    promise.value = new Promise((resolve, reject) => {\n      try {\n        const _target = toValue(target);\n        if (_target == null) {\n          resolve(\"\");\n        } else if (typeof _target === \"string\") {\n          resolve(blobToBase64(new Blob([_target], { type: \"text/plain\" })));\n        } else if (_target instanceof Blob) {\n          resolve(blobToBase64(_target));\n        } else if (_target instanceof ArrayBuffer) {\n          resolve(window.btoa(String.fromCharCode(...new Uint8Array(_target))));\n        } else if (_target instanceof HTMLCanvasElement) {\n          resolve(_target.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality));\n        } else if (_target instanceof HTMLImageElement) {\n          const img = _target.cloneNode(false);\n          img.crossOrigin = \"Anonymous\";\n          imgLoaded(img).then(() => {\n            const canvas = document.createElement(\"canvas\");\n            const ctx = canvas.getContext(\"2d\");\n            canvas.width = img.width;\n            canvas.height = img.height;\n            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);\n            resolve(canvas.toDataURL(options == null ? void 0 : options.type, options == null ? void 0 : options.quality));\n          }).catch(reject);\n        } else if (typeof _target === \"object\") {\n          const _serializeFn = (options == null ? void 0 : options.serializer) || getDefaultSerialization(_target);\n          const serialized = _serializeFn(_target);\n          return resolve(blobToBase64(new Blob([serialized], { type: \"application/json\" })));\n        } else {\n          reject(new Error(\"target is unsupported types\"));\n        }\n      } catch (error) {\n        reject(error);\n      }\n    });\n    promise.value.then((res) => {\n      base64.value = (options == null ? void 0 : options.dataUrl) === false ? res.replace(/^data:.*?;base64,/, \"\") : res;\n    });\n    return promise.value;\n  }\n  if (isRef(target) || typeof target === \"function\")\n    watch(target, execute, { immediate: true });\n  else\n    execute();\n  return {\n    base64,\n    promise,\n    execute\n  };\n}\nfunction imgLoaded(img) {\n  return new Promise((resolve, reject) => {\n    if (!img.complete) {\n      img.onload = () => {\n        resolve();\n      };\n      img.onerror = reject;\n    } else {\n      resolve();\n    }\n  });\n}\nfunction blobToBase64(blob) {\n  return new Promise((resolve, reject) => {\n    const fr = new FileReader();\n    fr.onload = (e) => {\n      resolve(e.target.result);\n    };\n    fr.onerror = reject;\n    fr.readAsDataURL(blob);\n  });\n}\nfunction useBattery(options = {}) {\n  const { navigator: navigator2 = defaultNavigator } = options;\n  const events2 = [\"chargingchange\", \"chargingtimechange\", \"dischargingtimechange\", \"levelchange\"];\n  const isSupported = useSupported(() => navigator2 && \"getBattery\" in navigator2 && typeof navigator2.getBattery === \"function\");\n  const charging = ref(false);\n  const chargingTime = ref(0);\n  const dischargingTime = ref(0);\n  const level = ref(1);\n  let battery;\n  function updateBatteryInfo() {\n    charging.value = this.charging;\n    chargingTime.value = this.chargingTime || 0;\n    dischargingTime.value = this.dischargingTime || 0;\n    level.value = this.level;\n  }\n  if (isSupported.value) {\n    navigator2.getBattery().then((_battery) => {\n      battery = _battery;\n      updateBatteryInfo.call(battery);\n      useEventListener(battery, events2, updateBatteryInfo, { passive: true });\n    });\n  }\n  return {\n    isSupported,\n    charging,\n    chargingTime,\n    dischargingTime,\n    level\n  };\n}\nfunction useBluetooth(options) {\n  let {\n    acceptAllDevices = false\n  } = options || {};\n  const {\n    filters = void 0,\n    optionalServices = void 0,\n    navigator: navigator2 = defaultNavigator\n  } = options || {};\n  const isSupported = useSupported(() => navigator2 && \"bluetooth\" in navigator2);\n  const device = shallowRef();\n  const error = shallowRef(null);\n  watch(device, () => {\n    connectToBluetoothGATTServer();\n  });\n  async function requestDevice() {\n    if (!isSupported.value)\n      return;\n    error.value = null;\n    if (filters && filters.length > 0)\n      acceptAllDevices = false;\n    try {\n      device.value = await (navigator2 == null ? void 0 : navigator2.bluetooth.requestDevice({\n        acceptAllDevices,\n        filters,\n        optionalServices\n      }));\n    } catch (err) {\n      error.value = err;\n    }\n  }\n  const server = shallowRef();\n  const isConnected = shallowRef(false);\n  function reset() {\n    isConnected.value = false;\n    device.value = void 0;\n    server.value = void 0;\n  }\n  async function connectToBluetoothGATTServer() {\n    error.value = null;\n    if (device.value && device.value.gatt) {\n      useEventListener(device, \"gattserverdisconnected\", reset, { passive: true });\n      try {\n        server.value = await device.value.gatt.connect();\n        isConnected.value = server.value.connected;\n      } catch (err) {\n        error.value = err;\n      }\n    }\n  }\n  tryOnMounted(() => {\n    var _a;\n    if (device.value)\n      (_a = device.value.gatt) == null ? void 0 : _a.connect();\n  });\n  tryOnScopeDispose(() => {\n    var _a;\n    if (device.value)\n      (_a = device.value.gatt) == null ? void 0 : _a.disconnect();\n  });\n  return {\n    isSupported,\n    isConnected: readonly(isConnected),\n    // Device:\n    device,\n    requestDevice,\n    // Server:\n    server,\n    // Errors:\n    error\n  };\n}\nvar ssrWidthSymbol = Symbol(\"vueuse-ssr-width\");\nfunction useSSRWidth() {\n  const ssrWidth = hasInjectionContext() ? injectLocal(ssrWidthSymbol, null) : null;\n  return typeof ssrWidth === \"number\" ? ssrWidth : void 0;\n}\nfunction provideSSRWidth(width, app) {\n  if (app !== void 0) {\n    app.provide(ssrWidthSymbol, width);\n  } else {\n    provideLocal(ssrWidthSymbol, width);\n  }\n}\nfunction useMediaQuery(query, options = {}) {\n  const { window: window2 = defaultWindow, ssrWidth = useSSRWidth() } = options;\n  const isSupported = useSupported(() => window2 && \"matchMedia\" in window2 && typeof window2.matchMedia === \"function\");\n  const ssrSupport = ref(typeof ssrWidth === \"number\");\n  const mediaQuery = shallowRef();\n  const matches = ref(false);\n  const handler = (event) => {\n    matches.value = event.matches;\n  };\n  watchEffect(() => {\n    if (ssrSupport.value) {\n      ssrSupport.value = !isSupported.value;\n      const queryStrings = toValue(query).split(\",\");\n      matches.value = queryStrings.some((queryString) => {\n        const not = queryString.includes(\"not all\");\n        const minWidth = queryString.match(/\\(\\s*min-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n        const maxWidth = queryString.match(/\\(\\s*max-width:\\s*(-?\\d+(?:\\.\\d*)?[a-z]+\\s*)\\)/);\n        let res = Boolean(minWidth || maxWidth);\n        if (minWidth && res) {\n          res = ssrWidth >= pxValue(minWidth[1]);\n        }\n        if (maxWidth && res) {\n          res = ssrWidth <= pxValue(maxWidth[1]);\n        }\n        return not ? !res : res;\n      });\n      return;\n    }\n    if (!isSupported.value)\n      return;\n    mediaQuery.value = window2.matchMedia(toValue(query));\n    matches.value = mediaQuery.value.matches;\n  });\n  useEventListener(mediaQuery, \"change\", handler, { passive: true });\n  return computed(() => matches.value);\n}\nvar breakpointsTailwind = {\n  \"sm\": 640,\n  \"md\": 768,\n  \"lg\": 1024,\n  \"xl\": 1280,\n  \"2xl\": 1536\n};\nvar breakpointsBootstrapV5 = {\n  xs: 0,\n  sm: 576,\n  md: 768,\n  lg: 992,\n  xl: 1200,\n  xxl: 1400\n};\nvar breakpointsVuetifyV2 = {\n  xs: 0,\n  sm: 600,\n  md: 960,\n  lg: 1264,\n  xl: 1904\n};\nvar breakpointsVuetifyV3 = {\n  xs: 0,\n  sm: 600,\n  md: 960,\n  lg: 1280,\n  xl: 1920,\n  xxl: 2560\n};\nvar breakpointsVuetify = breakpointsVuetifyV2;\nvar breakpointsAntDesign = {\n  xs: 480,\n  sm: 576,\n  md: 768,\n  lg: 992,\n  xl: 1200,\n  xxl: 1600\n};\nvar breakpointsQuasar = {\n  xs: 0,\n  sm: 600,\n  md: 1024,\n  lg: 1440,\n  xl: 1920\n};\nvar breakpointsSematic = {\n  mobileS: 320,\n  mobileM: 375,\n  mobileL: 425,\n  tablet: 768,\n  laptop: 1024,\n  laptopL: 1440,\n  desktop4K: 2560\n};\nvar breakpointsMasterCss = {\n  \"3xs\": 360,\n  \"2xs\": 480,\n  \"xs\": 600,\n  \"sm\": 768,\n  \"md\": 1024,\n  \"lg\": 1280,\n  \"xl\": 1440,\n  \"2xl\": 1600,\n  \"3xl\": 1920,\n  \"4xl\": 2560\n};\nvar breakpointsPrimeFlex = {\n  sm: 576,\n  md: 768,\n  lg: 992,\n  xl: 1200\n};\nvar breakpointsElement = {\n  xs: 0,\n  sm: 768,\n  md: 992,\n  lg: 1200,\n  xl: 1920\n};\nfunction useBreakpoints(breakpoints, options = {}) {\n  function getValue2(k, delta) {\n    let v = toValue(breakpoints[toValue(k)]);\n    if (delta != null)\n      v = increaseWithUnit(v, delta);\n    if (typeof v === \"number\")\n      v = `${v}px`;\n    return v;\n  }\n  const { window: window2 = defaultWindow, strategy = \"min-width\", ssrWidth = useSSRWidth() } = options;\n  const ssrSupport = typeof ssrWidth === \"number\";\n  const mounted = ssrSupport ? ref(false) : { value: true };\n  if (ssrSupport) {\n    tryOnMounted(() => mounted.value = !!window2);\n  }\n  function match(query, size) {\n    if (!mounted.value && ssrSupport) {\n      return query === \"min\" ? ssrWidth >= pxValue(size) : ssrWidth <= pxValue(size);\n    }\n    if (!window2)\n      return false;\n    return window2.matchMedia(`(${query}-width: ${size})`).matches;\n  }\n  const greaterOrEqual = (k) => {\n    return useMediaQuery(() => `(min-width: ${getValue2(k)})`, options);\n  };\n  const smallerOrEqual = (k) => {\n    return useMediaQuery(() => `(max-width: ${getValue2(k)})`, options);\n  };\n  const shortcutMethods = Object.keys(breakpoints).reduce((shortcuts, k) => {\n    Object.defineProperty(shortcuts, k, {\n      get: () => strategy === \"min-width\" ? greaterOrEqual(k) : smallerOrEqual(k),\n      enumerable: true,\n      configurable: true\n    });\n    return shortcuts;\n  }, {});\n  function current() {\n    const points = Object.keys(breakpoints).map((k) => [k, shortcutMethods[k], pxValue(getValue2(k))]).sort((a, b) => a[2] - b[2]);\n    return computed(() => points.filter(([, v]) => v.value).map(([k]) => k));\n  }\n  return Object.assign(shortcutMethods, {\n    greaterOrEqual,\n    smallerOrEqual,\n    greater(k) {\n      return useMediaQuery(() => `(min-width: ${getValue2(k, 0.1)})`, options);\n    },\n    smaller(k) {\n      return useMediaQuery(() => `(max-width: ${getValue2(k, -0.1)})`, options);\n    },\n    between(a, b) {\n      return useMediaQuery(() => `(min-width: ${getValue2(a)}) and (max-width: ${getValue2(b, -0.1)})`, options);\n    },\n    isGreater(k) {\n      return match(\"min\", getValue2(k, 0.1));\n    },\n    isGreaterOrEqual(k) {\n      return match(\"min\", getValue2(k));\n    },\n    isSmaller(k) {\n      return match(\"max\", getValue2(k, -0.1));\n    },\n    isSmallerOrEqual(k) {\n      return match(\"max\", getValue2(k));\n    },\n    isInBetween(a, b) {\n      return match(\"min\", getValue2(a)) && match(\"max\", getValue2(b, -0.1));\n    },\n    current,\n    active() {\n      const bps = current();\n      return computed(() => bps.value.length === 0 ? \"\" : bps.value.at(strategy === \"min-width\" ? -1 : 0));\n    }\n  });\n}\nfunction useBroadcastChannel(options) {\n  const {\n    name,\n    window: window2 = defaultWindow\n  } = options;\n  const isSupported = useSupported(() => window2 && \"BroadcastChannel\" in window2);\n  const isClosed = ref(false);\n  const channel = ref();\n  const data = ref();\n  const error = shallowRef(null);\n  const post = (data2) => {\n    if (channel.value)\n      channel.value.postMessage(data2);\n  };\n  const close = () => {\n    if (channel.value)\n      channel.value.close();\n    isClosed.value = true;\n  };\n  if (isSupported.value) {\n    tryOnMounted(() => {\n      error.value = null;\n      channel.value = new BroadcastChannel(name);\n      const listenerOptions = {\n        passive: true\n      };\n      useEventListener(channel, \"message\", (e) => {\n        data.value = e.data;\n      }, listenerOptions);\n      useEventListener(channel, \"messageerror\", (e) => {\n        error.value = e;\n      }, listenerOptions);\n      useEventListener(channel, \"close\", () => {\n        isClosed.value = true;\n      }, listenerOptions);\n    });\n  }\n  tryOnScopeDispose(() => {\n    close();\n  });\n  return {\n    isSupported,\n    channel,\n    data,\n    post,\n    close,\n    error,\n    isClosed\n  };\n}\nvar WRITABLE_PROPERTIES = [\n  \"hash\",\n  \"host\",\n  \"hostname\",\n  \"href\",\n  \"pathname\",\n  \"port\",\n  \"protocol\",\n  \"search\"\n];\nfunction useBrowserLocation(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const refs = Object.fromEntries(\n    WRITABLE_PROPERTIES.map((key) => [key, ref()])\n  );\n  for (const [key, ref2] of objectEntries(refs)) {\n    watch(ref2, (value) => {\n      if (!(window2 == null ? void 0 : window2.location) || window2.location[key] === value)\n        return;\n      window2.location[key] = value;\n    });\n  }\n  const buildState = (trigger) => {\n    var _a;\n    const { state: state2, length } = (window2 == null ? void 0 : window2.history) || {};\n    const { origin } = (window2 == null ? void 0 : window2.location) || {};\n    for (const key of WRITABLE_PROPERTIES)\n      refs[key].value = (_a = window2 == null ? void 0 : window2.location) == null ? void 0 : _a[key];\n    return reactive({\n      trigger,\n      state: state2,\n      length,\n      origin,\n      ...refs\n    });\n  };\n  const state = ref(buildState(\"load\"));\n  if (window2) {\n    const listenerOptions = { passive: true };\n    useEventListener(window2, \"popstate\", () => state.value = buildState(\"popstate\"), listenerOptions);\n    useEventListener(window2, \"hashchange\", () => state.value = buildState(\"hashchange\"), listenerOptions);\n  }\n  return state;\n}\nfunction useCached(refValue, comparator = (a, b) => a === b, watchOptions) {\n  const cachedValue = ref(refValue.value);\n  watch(() => refValue.value, (value) => {\n    if (!comparator(value, cachedValue.value))\n      cachedValue.value = value;\n  }, watchOptions);\n  return cachedValue;\n}\nfunction usePermission(permissionDesc, options = {}) {\n  const {\n    controls = false,\n    navigator: navigator2 = defaultNavigator\n  } = options;\n  const isSupported = useSupported(() => navigator2 && \"permissions\" in navigator2);\n  const permissionStatus = shallowRef();\n  const desc = typeof permissionDesc === \"string\" ? { name: permissionDesc } : permissionDesc;\n  const state = shallowRef();\n  const update = () => {\n    var _a, _b;\n    state.value = (_b = (_a = permissionStatus.value) == null ? void 0 : _a.state) != null ? _b : \"prompt\";\n  };\n  useEventListener(permissionStatus, \"change\", update, { passive: true });\n  const query = createSingletonPromise(async () => {\n    if (!isSupported.value)\n      return;\n    if (!permissionStatus.value) {\n      try {\n        permissionStatus.value = await navigator2.permissions.query(desc);\n      } catch (e) {\n        permissionStatus.value = void 0;\n      } finally {\n        update();\n      }\n    }\n    if (controls)\n      return toRaw(permissionStatus.value);\n  });\n  query();\n  if (controls) {\n    return {\n      state,\n      isSupported,\n      query\n    };\n  } else {\n    return state;\n  }\n}\nfunction useClipboard(options = {}) {\n  const {\n    navigator: navigator2 = defaultNavigator,\n    read = false,\n    source,\n    copiedDuring = 1500,\n    legacy = false\n  } = options;\n  const isClipboardApiSupported = useSupported(() => navigator2 && \"clipboard\" in navigator2);\n  const permissionRead = usePermission(\"clipboard-read\");\n  const permissionWrite = usePermission(\"clipboard-write\");\n  const isSupported = computed(() => isClipboardApiSupported.value || legacy);\n  const text = ref(\"\");\n  const copied = ref(false);\n  const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false });\n  function updateText() {\n    let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionRead.value));\n    if (!useLegacy) {\n      try {\n        navigator2.clipboard.readText().then((value) => {\n          text.value = value;\n        });\n      } catch (e) {\n        useLegacy = true;\n      }\n    }\n    if (useLegacy) {\n      text.value = legacyRead();\n    }\n  }\n  if (isSupported.value && read)\n    useEventListener([\"copy\", \"cut\"], updateText, { passive: true });\n  async function copy(value = toValue(source)) {\n    if (isSupported.value && value != null) {\n      let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionWrite.value));\n      if (!useLegacy) {\n        try {\n          await navigator2.clipboard.writeText(value);\n        } catch (e) {\n          useLegacy = true;\n        }\n      }\n      if (useLegacy)\n        legacyCopy(value);\n      text.value = value;\n      copied.value = true;\n      timeout.start();\n    }\n  }\n  function legacyCopy(value) {\n    const ta = document.createElement(\"textarea\");\n    ta.value = value != null ? value : \"\";\n    ta.style.position = \"absolute\";\n    ta.style.opacity = \"0\";\n    document.body.appendChild(ta);\n    ta.select();\n    document.execCommand(\"copy\");\n    ta.remove();\n  }\n  function legacyRead() {\n    var _a, _b, _c;\n    return (_c = (_b = (_a = document == null ? void 0 : document.getSelection) == null ? void 0 : _a.call(document)) == null ? void 0 : _b.toString()) != null ? _c : \"\";\n  }\n  function isAllowed(status) {\n    return status === \"granted\" || status === \"prompt\";\n  }\n  return {\n    isSupported,\n    text,\n    copied,\n    copy\n  };\n}\nfunction useClipboardItems(options = {}) {\n  const {\n    navigator: navigator2 = defaultNavigator,\n    read = false,\n    source,\n    copiedDuring = 1500\n  } = options;\n  const isSupported = useSupported(() => navigator2 && \"clipboard\" in navigator2);\n  const content = ref([]);\n  const copied = ref(false);\n  const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false });\n  function updateContent() {\n    if (isSupported.value) {\n      navigator2.clipboard.read().then((items) => {\n        content.value = items;\n      });\n    }\n  }\n  if (isSupported.value && read)\n    useEventListener([\"copy\", \"cut\"], updateContent, { passive: true });\n  async function copy(value = toValue(source)) {\n    if (isSupported.value && value != null) {\n      await navigator2.clipboard.write(value);\n      content.value = value;\n      copied.value = true;\n      timeout.start();\n    }\n  }\n  return {\n    isSupported,\n    content,\n    copied,\n    copy\n  };\n}\nfunction cloneFnJSON(source) {\n  return JSON.parse(JSON.stringify(source));\n}\nfunction useCloned(source, options = {}) {\n  const cloned = ref({});\n  const isModified = ref(false);\n  let _lastSync = false;\n  const {\n    manual,\n    clone = cloneFnJSON,\n    // watch options\n    deep = true,\n    immediate = true\n  } = options;\n  watch(cloned, () => {\n    if (_lastSync) {\n      _lastSync = false;\n      return;\n    }\n    isModified.value = true;\n  }, {\n    deep: true,\n    flush: \"sync\"\n  });\n  function sync() {\n    _lastSync = true;\n    isModified.value = false;\n    cloned.value = clone(toValue(source));\n  }\n  if (!manual && (isRef(source) || typeof source === \"function\")) {\n    watch(source, sync, {\n      ...options,\n      deep,\n      immediate\n    });\n  } else {\n    sync();\n  }\n  return { cloned, isModified, sync };\n}\nvar _global = typeof globalThis !== \"undefined\" ? globalThis : typeof window !== \"undefined\" ? window : typeof global !== \"undefined\" ? global : typeof self !== \"undefined\" ? self : {};\nvar globalKey = \"__vueuse_ssr_handlers__\";\nvar handlers = getHandlers();\nfunction getHandlers() {\n  if (!(globalKey in _global))\n    _global[globalKey] = _global[globalKey] || {};\n  return _global[globalKey];\n}\nfunction getSSRHandler(key, fallback) {\n  return handlers[key] || fallback;\n}\nfunction setSSRHandler(key, fn) {\n  handlers[key] = fn;\n}\nfunction usePreferredDark(options) {\n  return useMediaQuery(\"(prefers-color-scheme: dark)\", options);\n}\nfunction guessSerializerType(rawInit) {\n  return rawInit == null ? \"any\" : rawInit instanceof Set ? \"set\" : rawInit instanceof Map ? \"map\" : rawInit instanceof Date ? \"date\" : typeof rawInit === \"boolean\" ? \"boolean\" : typeof rawInit === \"string\" ? \"string\" : typeof rawInit === \"object\" ? \"object\" : !Number.isNaN(rawInit) ? \"number\" : \"any\";\n}\nvar StorageSerializers = {\n  boolean: {\n    read: (v) => v === \"true\",\n    write: (v) => String(v)\n  },\n  object: {\n    read: (v) => JSON.parse(v),\n    write: (v) => JSON.stringify(v)\n  },\n  number: {\n    read: (v) => Number.parseFloat(v),\n    write: (v) => String(v)\n  },\n  any: {\n    read: (v) => v,\n    write: (v) => String(v)\n  },\n  string: {\n    read: (v) => v,\n    write: (v) => String(v)\n  },\n  map: {\n    read: (v) => new Map(JSON.parse(v)),\n    write: (v) => JSON.stringify(Array.from(v.entries()))\n  },\n  set: {\n    read: (v) => new Set(JSON.parse(v)),\n    write: (v) => JSON.stringify(Array.from(v))\n  },\n  date: {\n    read: (v) => new Date(v),\n    write: (v) => v.toISOString()\n  }\n};\nvar customStorageEventName = \"vueuse-storage\";\nfunction useStorage(key, defaults2, storage, options = {}) {\n  var _a;\n  const {\n    flush = \"pre\",\n    deep = true,\n    listenToStorageChanges = true,\n    writeDefaults = true,\n    mergeDefaults = false,\n    shallow,\n    window: window2 = defaultWindow,\n    eventFilter,\n    onError = (e) => {\n      console.error(e);\n    },\n    initOnMounted\n  } = options;\n  const data = (shallow ? shallowRef : ref)(typeof defaults2 === \"function\" ? defaults2() : defaults2);\n  const keyComputed = computed(() => toValue(key));\n  if (!storage) {\n    try {\n      storage = getSSRHandler(\"getDefaultStorage\", () => {\n        var _a2;\n        return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage;\n      })();\n    } catch (e) {\n      onError(e);\n    }\n  }\n  if (!storage)\n    return data;\n  const rawInit = toValue(defaults2);\n  const type = guessSerializerType(rawInit);\n  const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];\n  const { pause: pauseWatch, resume: resumeWatch } = watchPausable(\n    data,\n    () => write(data.value),\n    { flush, deep, eventFilter }\n  );\n  watch(keyComputed, () => update(), { flush });\n  if (window2 && listenToStorageChanges) {\n    tryOnMounted(() => {\n      if (storage instanceof Storage)\n        useEventListener(window2, \"storage\", update, { passive: true });\n      else\n        useEventListener(window2, customStorageEventName, updateFromCustomEvent);\n      if (initOnMounted)\n        update();\n    });\n  }\n  if (!initOnMounted)\n    update();\n  function dispatchWriteEvent(oldValue, newValue) {\n    if (window2) {\n      const payload = {\n        key: keyComputed.value,\n        oldValue,\n        newValue,\n        storageArea: storage\n      };\n      window2.dispatchEvent(storage instanceof Storage ? new StorageEvent(\"storage\", payload) : new CustomEvent(customStorageEventName, {\n        detail: payload\n      }));\n    }\n  }\n  function write(v) {\n    try {\n      const oldValue = storage.getItem(keyComputed.value);\n      if (v == null) {\n        dispatchWriteEvent(oldValue, null);\n        storage.removeItem(keyComputed.value);\n      } else {\n        const serialized = serializer.write(v);\n        if (oldValue !== serialized) {\n          storage.setItem(keyComputed.value, serialized);\n          dispatchWriteEvent(oldValue, serialized);\n        }\n      }\n    } catch (e) {\n      onError(e);\n    }\n  }\n  function read(event) {\n    const rawValue = event ? event.newValue : storage.getItem(keyComputed.value);\n    if (rawValue == null) {\n      if (writeDefaults && rawInit != null)\n        storage.setItem(keyComputed.value, serializer.write(rawInit));\n      return rawInit;\n    } else if (!event && mergeDefaults) {\n      const value = serializer.read(rawValue);\n      if (typeof mergeDefaults === \"function\")\n        return mergeDefaults(value, rawInit);\n      else if (type === \"object\" && !Array.isArray(value))\n        return { ...rawInit, ...value };\n      return value;\n    } else if (typeof rawValue !== \"string\") {\n      return rawValue;\n    } else {\n      return serializer.read(rawValue);\n    }\n  }\n  function update(event) {\n    if (event && event.storageArea !== storage)\n      return;\n    if (event && event.key == null) {\n      data.value = rawInit;\n      return;\n    }\n    if (event && event.key !== keyComputed.value)\n      return;\n    pauseWatch();\n    try {\n      if ((event == null ? void 0 : event.newValue) !== serializer.write(data.value))\n        data.value = read(event);\n    } catch (e) {\n      onError(e);\n    } finally {\n      if (event)\n        nextTick(resumeWatch);\n      else\n        resumeWatch();\n    }\n  }\n  function updateFromCustomEvent(event) {\n    update(event.detail);\n  }\n  return data;\n}\nvar CSS_DISABLE_TRANS = \"*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}\";\nfunction useColorMode(options = {}) {\n  const {\n    selector = \"html\",\n    attribute = \"class\",\n    initialValue = \"auto\",\n    window: window2 = defaultWindow,\n    storage,\n    storageKey = \"vueuse-color-scheme\",\n    listenToStorageChanges = true,\n    storageRef,\n    emitAuto,\n    disableTransition = true\n  } = options;\n  const modes = {\n    auto: \"\",\n    light: \"light\",\n    dark: \"dark\",\n    ...options.modes || {}\n  };\n  const preferredDark = usePreferredDark({ window: window2 });\n  const system = computed(() => preferredDark.value ? \"dark\" : \"light\");\n  const store = storageRef || (storageKey == null ? toRef2(initialValue) : useStorage(storageKey, initialValue, storage, { window: window2, listenToStorageChanges }));\n  const state = computed(() => store.value === \"auto\" ? system.value : store.value);\n  const updateHTMLAttrs = getSSRHandler(\n    \"updateHTMLAttrs\",\n    (selector2, attribute2, value) => {\n      const el = typeof selector2 === \"string\" ? window2 == null ? void 0 : window2.document.querySelector(selector2) : unrefElement(selector2);\n      if (!el)\n        return;\n      const classesToAdd = /* @__PURE__ */ new Set();\n      const classesToRemove = /* @__PURE__ */ new Set();\n      let attributeToChange = null;\n      if (attribute2 === \"class\") {\n        const current = value.split(/\\s/g);\n        Object.values(modes).flatMap((i) => (i || \"\").split(/\\s/g)).filter(Boolean).forEach((v) => {\n          if (current.includes(v))\n            classesToAdd.add(v);\n          else\n            classesToRemove.add(v);\n        });\n      } else {\n        attributeToChange = { key: attribute2, value };\n      }\n      if (classesToAdd.size === 0 && classesToRemove.size === 0 && attributeToChange === null)\n        return;\n      let style;\n      if (disableTransition) {\n        style = window2.document.createElement(\"style\");\n        style.appendChild(document.createTextNode(CSS_DISABLE_TRANS));\n        window2.document.head.appendChild(style);\n      }\n      for (const c of classesToAdd) {\n        el.classList.add(c);\n      }\n      for (const c of classesToRemove) {\n        el.classList.remove(c);\n      }\n      if (attributeToChange) {\n        el.setAttribute(attributeToChange.key, attributeToChange.value);\n      }\n      if (disableTransition) {\n        window2.getComputedStyle(style).opacity;\n        document.head.removeChild(style);\n      }\n    }\n  );\n  function defaultOnChanged(mode) {\n    var _a;\n    updateHTMLAttrs(selector, attribute, (_a = modes[mode]) != null ? _a : mode);\n  }\n  function onChanged(mode) {\n    if (options.onChanged)\n      options.onChanged(mode, defaultOnChanged);\n    else\n      defaultOnChanged(mode);\n  }\n  watch(state, onChanged, { flush: \"post\", immediate: true });\n  tryOnMounted(() => onChanged(state.value));\n  const auto = computed({\n    get() {\n      return emitAuto ? store.value : state.value;\n    },\n    set(v) {\n      store.value = v;\n    }\n  });\n  return Object.assign(auto, { store, system, state });\n}\nfunction useConfirmDialog(revealed = ref(false)) {\n  const confirmHook = createEventHook();\n  const cancelHook = createEventHook();\n  const revealHook = createEventHook();\n  let _resolve = noop;\n  const reveal = (data) => {\n    revealHook.trigger(data);\n    revealed.value = true;\n    return new Promise((resolve) => {\n      _resolve = resolve;\n    });\n  };\n  const confirm = (data) => {\n    revealed.value = false;\n    confirmHook.trigger(data);\n    _resolve({ data, isCanceled: false });\n  };\n  const cancel = (data) => {\n    revealed.value = false;\n    cancelHook.trigger(data);\n    _resolve({ data, isCanceled: true });\n  };\n  return {\n    isRevealed: computed(() => revealed.value),\n    reveal,\n    confirm,\n    cancel,\n    onReveal: revealHook.on,\n    onConfirm: confirmHook.on,\n    onCancel: cancelHook.on\n  };\n}\nfunction useCountdown(initialCountdown, options) {\n  var _a, _b;\n  const remaining = ref(toValue(initialCountdown));\n  const intervalController = useIntervalFn(() => {\n    var _a2, _b2;\n    const value = remaining.value - 1;\n    remaining.value = value < 0 ? 0 : value;\n    (_a2 = options == null ? void 0 : options.onTick) == null ? void 0 : _a2.call(options);\n    if (remaining.value <= 0) {\n      intervalController.pause();\n      (_b2 = options == null ? void 0 : options.onComplete) == null ? void 0 : _b2.call(options);\n    }\n  }, (_a = options == null ? void 0 : options.interval) != null ? _a : 1e3, { immediate: (_b = options == null ? void 0 : options.immediate) != null ? _b : false });\n  const reset = () => {\n    remaining.value = toValue(initialCountdown);\n  };\n  const stop = () => {\n    intervalController.pause();\n    reset();\n  };\n  const resume = () => {\n    if (!intervalController.isActive.value) {\n      if (remaining.value > 0) {\n        intervalController.resume();\n      }\n    }\n  };\n  const start = () => {\n    reset();\n    intervalController.resume();\n  };\n  return {\n    remaining,\n    reset,\n    stop,\n    start,\n    pause: intervalController.pause,\n    resume,\n    isActive: intervalController.isActive\n  };\n}\nfunction useCssVar(prop, target, options = {}) {\n  const { window: window2 = defaultWindow, initialValue, observe = false } = options;\n  const variable = ref(initialValue);\n  const elRef = computed(() => {\n    var _a;\n    return unrefElement(target) || ((_a = window2 == null ? void 0 : window2.document) == null ? void 0 : _a.documentElement);\n  });\n  function updateCssVar() {\n    var _a;\n    const key = toValue(prop);\n    const el = toValue(elRef);\n    if (el && window2 && key) {\n      const value = (_a = window2.getComputedStyle(el).getPropertyValue(key)) == null ? void 0 : _a.trim();\n      variable.value = value || initialValue;\n    }\n  }\n  if (observe) {\n    useMutationObserver(elRef, updateCssVar, {\n      attributeFilter: [\"style\", \"class\"],\n      window: window2\n    });\n  }\n  watch(\n    [elRef, () => toValue(prop)],\n    (_, old) => {\n      if (old[0] && old[1])\n        old[0].style.removeProperty(old[1]);\n      updateCssVar();\n    },\n    { immediate: true }\n  );\n  watch(\n    variable,\n    (val) => {\n      var _a;\n      const raw_prop = toValue(prop);\n      if (((_a = elRef.value) == null ? void 0 : _a.style) && raw_prop) {\n        if (val == null)\n          elRef.value.style.removeProperty(raw_prop);\n        else\n          elRef.value.style.setProperty(raw_prop, val);\n      }\n    }\n  );\n  return variable;\n}\nfunction useCurrentElement(rootComponent) {\n  const vm = getCurrentInstance();\n  const currentElement = computedWithControl(\n    () => null,\n    () => rootComponent ? unrefElement(rootComponent) : vm.proxy.$el\n  );\n  onUpdated(currentElement.trigger);\n  onMounted(currentElement.trigger);\n  return currentElement;\n}\nfunction useCycleList(list, options) {\n  const state = shallowRef(getInitialValue());\n  const listRef = toRef2(list);\n  const index = computed({\n    get() {\n      var _a;\n      const targetList = listRef.value;\n      let index2 = (options == null ? void 0 : options.getIndexOf) ? options.getIndexOf(state.value, targetList) : targetList.indexOf(state.value);\n      if (index2 < 0)\n        index2 = (_a = options == null ? void 0 : options.fallbackIndex) != null ? _a : 0;\n      return index2;\n    },\n    set(v) {\n      set2(v);\n    }\n  });\n  function set2(i) {\n    const targetList = listRef.value;\n    const length = targetList.length;\n    const index2 = (i % length + length) % length;\n    const value = targetList[index2];\n    state.value = value;\n    return value;\n  }\n  function shift(delta = 1) {\n    return set2(index.value + delta);\n  }\n  function next(n = 1) {\n    return shift(n);\n  }\n  function prev(n = 1) {\n    return shift(-n);\n  }\n  function getInitialValue() {\n    var _a, _b;\n    return (_b = toValue((_a = options == null ? void 0 : options.initialValue) != null ? _a : toValue(list)[0])) != null ? _b : void 0;\n  }\n  watch(listRef, () => set2(index.value));\n  return {\n    state,\n    index,\n    next,\n    prev,\n    go: set2\n  };\n}\nfunction useDark(options = {}) {\n  const {\n    valueDark = \"dark\",\n    valueLight = \"\"\n  } = options;\n  const mode = useColorMode({\n    ...options,\n    onChanged: (mode2, defaultHandler) => {\n      var _a;\n      if (options.onChanged)\n        (_a = options.onChanged) == null ? void 0 : _a.call(options, mode2 === \"dark\", defaultHandler, mode2);\n      else\n        defaultHandler(mode2);\n    },\n    modes: {\n      dark: valueDark,\n      light: valueLight\n    }\n  });\n  const system = computed(() => mode.system.value);\n  const isDark = computed({\n    get() {\n      return mode.value === \"dark\";\n    },\n    set(v) {\n      const modeVal = v ? \"dark\" : \"light\";\n      if (system.value === modeVal)\n        mode.value = \"auto\";\n      else\n        mode.value = modeVal;\n    }\n  });\n  return isDark;\n}\nfunction fnBypass(v) {\n  return v;\n}\nfunction fnSetSource(source, value) {\n  return source.value = value;\n}\nfunction defaultDump(clone) {\n  return clone ? typeof clone === \"function\" ? clone : cloneFnJSON : fnBypass;\n}\nfunction defaultParse(clone) {\n  return clone ? typeof clone === \"function\" ? clone : cloneFnJSON : fnBypass;\n}\nfunction useManualRefHistory(source, options = {}) {\n  const {\n    clone = false,\n    dump = defaultDump(clone),\n    parse = defaultParse(clone),\n    setSource = fnSetSource\n  } = options;\n  function _createHistoryRecord() {\n    return markRaw({\n      snapshot: dump(source.value),\n      timestamp: timestamp()\n    });\n  }\n  const last = ref(_createHistoryRecord());\n  const undoStack = ref([]);\n  const redoStack = ref([]);\n  const _setSource = (record) => {\n    setSource(source, parse(record.snapshot));\n    last.value = record;\n  };\n  const commit = () => {\n    undoStack.value.unshift(last.value);\n    last.value = _createHistoryRecord();\n    if (options.capacity && undoStack.value.length > options.capacity)\n      undoStack.value.splice(options.capacity, Number.POSITIVE_INFINITY);\n    if (redoStack.value.length)\n      redoStack.value.splice(0, redoStack.value.length);\n  };\n  const clear = () => {\n    undoStack.value.splice(0, undoStack.value.length);\n    redoStack.value.splice(0, redoStack.value.length);\n  };\n  const undo = () => {\n    const state = undoStack.value.shift();\n    if (state) {\n      redoStack.value.unshift(last.value);\n      _setSource(state);\n    }\n  };\n  const redo = () => {\n    const state = redoStack.value.shift();\n    if (state) {\n      undoStack.value.unshift(last.value);\n      _setSource(state);\n    }\n  };\n  const reset = () => {\n    _setSource(last.value);\n  };\n  const history = computed(() => [last.value, ...undoStack.value]);\n  const canUndo = computed(() => undoStack.value.length > 0);\n  const canRedo = computed(() => redoStack.value.length > 0);\n  return {\n    source,\n    undoStack,\n    redoStack,\n    last,\n    history,\n    canUndo,\n    canRedo,\n    clear,\n    commit,\n    reset,\n    undo,\n    redo\n  };\n}\nfunction useRefHistory(source, options = {}) {\n  const {\n    deep = false,\n    flush = \"pre\",\n    eventFilter\n  } = options;\n  const {\n    eventFilter: composedFilter,\n    pause,\n    resume: resumeTracking,\n    isActive: isTracking\n  } = pausableFilter(eventFilter);\n  const {\n    ignoreUpdates,\n    ignorePrevAsyncUpdates,\n    stop\n  } = watchIgnorable(\n    source,\n    commit,\n    { deep, flush, eventFilter: composedFilter }\n  );\n  function setSource(source2, value) {\n    ignorePrevAsyncUpdates();\n    ignoreUpdates(() => {\n      source2.value = value;\n    });\n  }\n  const manualHistory = useManualRefHistory(source, { ...options, clone: options.clone || deep, setSource });\n  const { clear, commit: manualCommit } = manualHistory;\n  function commit() {\n    ignorePrevAsyncUpdates();\n    manualCommit();\n  }\n  function resume(commitNow) {\n    resumeTracking();\n    if (commitNow)\n      commit();\n  }\n  function batch(fn) {\n    let canceled = false;\n    const cancel = () => canceled = true;\n    ignoreUpdates(() => {\n      fn(cancel);\n    });\n    if (!canceled)\n      commit();\n  }\n  function dispose() {\n    stop();\n    clear();\n  }\n  return {\n    ...manualHistory,\n    isTracking,\n    pause,\n    resume,\n    commit,\n    batch,\n    dispose\n  };\n}\nfunction useDebouncedRefHistory(source, options = {}) {\n  const filter = options.debounce ? debounceFilter(options.debounce) : void 0;\n  const history = useRefHistory(source, { ...options, eventFilter: filter });\n  return {\n    ...history\n  };\n}\nfunction useDeviceMotion(options = {}) {\n  const {\n    window: window2 = defaultWindow,\n    requestPermissions = false,\n    eventFilter = bypassFilter\n  } = options;\n  const isSupported = useSupported(() => typeof DeviceMotionEvent !== \"undefined\");\n  const requirePermissions = useSupported(() => isSupported.value && \"requestPermission\" in DeviceMotionEvent && typeof DeviceMotionEvent.requestPermission === \"function\");\n  const permissionGranted = ref(false);\n  const acceleration = ref({ x: null, y: null, z: null });\n  const rotationRate = ref({ alpha: null, beta: null, gamma: null });\n  const interval = ref(0);\n  const accelerationIncludingGravity = ref({\n    x: null,\n    y: null,\n    z: null\n  });\n  function init() {\n    if (window2) {\n      const onDeviceMotion = createFilterWrapper(\n        eventFilter,\n        (event) => {\n          var _a, _b, _c, _d, _e, _f, _g, _h, _i;\n          acceleration.value = {\n            x: ((_a = event.acceleration) == null ? void 0 : _a.x) || null,\n            y: ((_b = event.acceleration) == null ? void 0 : _b.y) || null,\n            z: ((_c = event.acceleration) == null ? void 0 : _c.z) || null\n          };\n          accelerationIncludingGravity.value = {\n            x: ((_d = event.accelerationIncludingGravity) == null ? void 0 : _d.x) || null,\n            y: ((_e = event.accelerationIncludingGravity) == null ? void 0 : _e.y) || null,\n            z: ((_f = event.accelerationIncludingGravity) == null ? void 0 : _f.z) || null\n          };\n          rotationRate.value = {\n            alpha: ((_g = event.rotationRate) == null ? void 0 : _g.alpha) || null,\n            beta: ((_h = event.rotationRate) == null ? void 0 : _h.beta) || null,\n            gamma: ((_i = event.rotationRate) == null ? void 0 : _i.gamma) || null\n          };\n          interval.value = event.interval;\n        }\n      );\n      useEventListener(window2, \"devicemotion\", onDeviceMotion, { passive: true });\n    }\n  }\n  const ensurePermissions = async () => {\n    if (!requirePermissions.value)\n      permissionGranted.value = true;\n    if (permissionGranted.value)\n      return;\n    if (requirePermissions.value) {\n      const requestPermission = DeviceMotionEvent.requestPermission;\n      try {\n        const response = await requestPermission();\n        if (response === \"granted\") {\n          permissionGranted.value = true;\n          init();\n        }\n      } catch (error) {\n        console.error(error);\n      }\n    }\n  };\n  if (isSupported.value) {\n    if (requestPermissions && requirePermissions.value) {\n      ensurePermissions().then(() => init());\n    } else {\n      init();\n    }\n  }\n  return {\n    acceleration,\n    accelerationIncludingGravity,\n    rotationRate,\n    interval,\n    isSupported,\n    requirePermissions,\n    ensurePermissions,\n    permissionGranted\n  };\n}\nfunction useDeviceOrientation(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const isSupported = useSupported(() => window2 && \"DeviceOrientationEvent\" in window2);\n  const isAbsolute = ref(false);\n  const alpha = ref(null);\n  const beta = ref(null);\n  const gamma = ref(null);\n  if (window2 && isSupported.value) {\n    useEventListener(window2, \"deviceorientation\", (event) => {\n      isAbsolute.value = event.absolute;\n      alpha.value = event.alpha;\n      beta.value = event.beta;\n      gamma.value = event.gamma;\n    }, { passive: true });\n  }\n  return {\n    isSupported,\n    isAbsolute,\n    alpha,\n    beta,\n    gamma\n  };\n}\nfunction useDevicePixelRatio(options = {}) {\n  const {\n    window: window2 = defaultWindow\n  } = options;\n  const pixelRatio = ref(1);\n  const query = useMediaQuery(() => `(resolution: ${pixelRatio.value}dppx)`, options);\n  let stop = noop;\n  if (window2) {\n    stop = watchImmediate(query, () => pixelRatio.value = window2.devicePixelRatio);\n  }\n  return {\n    pixelRatio: readonly(pixelRatio),\n    stop\n  };\n}\nfunction useDevicesList(options = {}) {\n  const {\n    navigator: navigator2 = defaultNavigator,\n    requestPermissions = false,\n    constraints = { audio: true, video: true },\n    onUpdated: onUpdated2\n  } = options;\n  const devices = ref([]);\n  const videoInputs = computed(() => devices.value.filter((i) => i.kind === \"videoinput\"));\n  const audioInputs = computed(() => devices.value.filter((i) => i.kind === \"audioinput\"));\n  const audioOutputs = computed(() => devices.value.filter((i) => i.kind === \"audiooutput\"));\n  const isSupported = useSupported(() => navigator2 && navigator2.mediaDevices && navigator2.mediaDevices.enumerateDevices);\n  const permissionGranted = ref(false);\n  let stream;\n  async function update() {\n    if (!isSupported.value)\n      return;\n    devices.value = await navigator2.mediaDevices.enumerateDevices();\n    onUpdated2 == null ? void 0 : onUpdated2(devices.value);\n    if (stream) {\n      stream.getTracks().forEach((t) => t.stop());\n      stream = null;\n    }\n  }\n  async function ensurePermissions() {\n    if (!isSupported.value)\n      return false;\n    if (permissionGranted.value)\n      return true;\n    const { state, query } = usePermission(\"camera\", { controls: true });\n    await query();\n    if (state.value !== \"granted\") {\n      let granted = true;\n      try {\n        stream = await navigator2.mediaDevices.getUserMedia(constraints);\n      } catch (e) {\n        stream = null;\n        granted = false;\n      }\n      update();\n      permissionGranted.value = granted;\n    } else {\n      permissionGranted.value = true;\n    }\n    return permissionGranted.value;\n  }\n  if (isSupported.value) {\n    if (requestPermissions)\n      ensurePermissions();\n    useEventListener(navigator2.mediaDevices, \"devicechange\", update, { passive: true });\n    update();\n  }\n  return {\n    devices,\n    ensurePermissions,\n    permissionGranted,\n    videoInputs,\n    audioInputs,\n    audioOutputs,\n    isSupported\n  };\n}\nfunction useDisplayMedia(options = {}) {\n  var _a;\n  const enabled = ref((_a = options.enabled) != null ? _a : false);\n  const video = options.video;\n  const audio = options.audio;\n  const { navigator: navigator2 = defaultNavigator } = options;\n  const isSupported = useSupported(() => {\n    var _a2;\n    return (_a2 = navigator2 == null ? void 0 : navigator2.mediaDevices) == null ? void 0 : _a2.getDisplayMedia;\n  });\n  const constraint = { audio, video };\n  const stream = shallowRef();\n  async function _start() {\n    var _a2;\n    if (!isSupported.value || stream.value)\n      return;\n    stream.value = await navigator2.mediaDevices.getDisplayMedia(constraint);\n    (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => useEventListener(t, \"ended\", stop, { passive: true }));\n    return stream.value;\n  }\n  async function _stop() {\n    var _a2;\n    (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop());\n    stream.value = void 0;\n  }\n  function stop() {\n    _stop();\n    enabled.value = false;\n  }\n  async function start() {\n    await _start();\n    if (stream.value)\n      enabled.value = true;\n    return stream.value;\n  }\n  watch(\n    enabled,\n    (v) => {\n      if (v)\n        _start();\n      else\n        _stop();\n    },\n    { immediate: true }\n  );\n  return {\n    isSupported,\n    stream,\n    start,\n    stop,\n    enabled\n  };\n}\nfunction useDocumentVisibility(options = {}) {\n  const { document: document2 = defaultDocument } = options;\n  if (!document2)\n    return ref(\"visible\");\n  const visibility = ref(document2.visibilityState);\n  useEventListener(document2, \"visibilitychange\", () => {\n    visibility.value = document2.visibilityState;\n  }, { passive: true });\n  return visibility;\n}\nfunction useDraggable(target, options = {}) {\n  var _a;\n  const {\n    pointerTypes,\n    preventDefault: preventDefault2,\n    stopPropagation,\n    exact,\n    onMove,\n    onEnd,\n    onStart,\n    initialValue,\n    axis = \"both\",\n    draggingElement = defaultWindow,\n    containerElement,\n    handle: draggingHandle = target,\n    buttons = [0]\n  } = options;\n  const position = ref(\n    (_a = toValue(initialValue)) != null ? _a : { x: 0, y: 0 }\n  );\n  const pressedDelta = ref();\n  const filterEvent = (e) => {\n    if (pointerTypes)\n      return pointerTypes.includes(e.pointerType);\n    return true;\n  };\n  const handleEvent = (e) => {\n    if (toValue(preventDefault2))\n      e.preventDefault();\n    if (toValue(stopPropagation))\n      e.stopPropagation();\n  };\n  const start = (e) => {\n    var _a2;\n    if (!toValue(buttons).includes(e.button))\n      return;\n    if (toValue(options.disabled) || !filterEvent(e))\n      return;\n    if (toValue(exact) && e.target !== toValue(target))\n      return;\n    const container = toValue(containerElement);\n    const containerRect = (_a2 = container == null ? void 0 : container.getBoundingClientRect) == null ? void 0 : _a2.call(container);\n    const targetRect = toValue(target).getBoundingClientRect();\n    const pos = {\n      x: e.clientX - (container ? targetRect.left - containerRect.left + container.scrollLeft : targetRect.left),\n      y: e.clientY - (container ? targetRect.top - containerRect.top + container.scrollTop : targetRect.top)\n    };\n    if ((onStart == null ? void 0 : onStart(pos, e)) === false)\n      return;\n    pressedDelta.value = pos;\n    handleEvent(e);\n  };\n  const move = (e) => {\n    if (toValue(options.disabled) || !filterEvent(e))\n      return;\n    if (!pressedDelta.value)\n      return;\n    const container = toValue(containerElement);\n    const targetRect = toValue(target).getBoundingClientRect();\n    let { x, y } = position.value;\n    if (axis === \"x\" || axis === \"both\") {\n      x = e.clientX - pressedDelta.value.x;\n      if (container)\n        x = Math.min(Math.max(0, x), container.scrollWidth - targetRect.width);\n    }\n    if (axis === \"y\" || axis === \"both\") {\n      y = e.clientY - pressedDelta.value.y;\n      if (container)\n        y = Math.min(Math.max(0, y), container.scrollHeight - targetRect.height);\n    }\n    position.value = {\n      x,\n      y\n    };\n    onMove == null ? void 0 : onMove(position.value, e);\n    handleEvent(e);\n  };\n  const end = (e) => {\n    if (toValue(options.disabled) || !filterEvent(e))\n      return;\n    if (!pressedDelta.value)\n      return;\n    pressedDelta.value = void 0;\n    onEnd == null ? void 0 : onEnd(position.value, e);\n    handleEvent(e);\n  };\n  if (isClient) {\n    const config = () => {\n      var _a2;\n      return {\n        capture: (_a2 = options.capture) != null ? _a2 : true,\n        passive: !toValue(preventDefault2)\n      };\n    };\n    useEventListener(draggingHandle, \"pointerdown\", start, config);\n    useEventListener(draggingElement, \"pointermove\", move, config);\n    useEventListener(draggingElement, \"pointerup\", end, config);\n  }\n  return {\n    ...toRefs2(position),\n    position,\n    isDragging: computed(() => !!pressedDelta.value),\n    style: computed(\n      () => `left:${position.value.x}px;top:${position.value.y}px;`\n    )\n  };\n}\nfunction useDropZone(target, options = {}) {\n  var _a, _b;\n  const isOverDropZone = ref(false);\n  const files = shallowRef(null);\n  let counter = 0;\n  let isValid = true;\n  if (isClient) {\n    const _options = typeof options === \"function\" ? { onDrop: options } : options;\n    const multiple = (_a = _options.multiple) != null ? _a : true;\n    const preventDefaultForUnhandled = (_b = _options.preventDefaultForUnhandled) != null ? _b : false;\n    const getFiles = (event) => {\n      var _a2, _b2;\n      const list = Array.from((_b2 = (_a2 = event.dataTransfer) == null ? void 0 : _a2.files) != null ? _b2 : []);\n      return list.length === 0 ? null : multiple ? list : [list[0]];\n    };\n    const checkDataTypes = (types) => {\n      const dataTypes = unref(_options.dataTypes);\n      if (typeof dataTypes === \"function\")\n        return dataTypes(types);\n      if (!(dataTypes == null ? void 0 : dataTypes.length))\n        return true;\n      if (types.length === 0)\n        return false;\n      return types.every(\n        (type) => dataTypes.some((allowedType) => type.includes(allowedType))\n      );\n    };\n    const checkValidity = (items) => {\n      const types = Array.from(items != null ? items : []).map((item) => item.type);\n      const dataTypesValid = checkDataTypes(types);\n      const multipleFilesValid = multiple || items.length <= 1;\n      return dataTypesValid && multipleFilesValid;\n    };\n    const isSafari = () => /^(?:(?!chrome|android).)*safari/i.test(navigator.userAgent) && !(\"chrome\" in window);\n    const handleDragEvent = (event, eventType) => {\n      var _a2, _b2, _c, _d, _e, _f;\n      const dataTransferItemList = (_a2 = event.dataTransfer) == null ? void 0 : _a2.items;\n      isValid = (_b2 = dataTransferItemList && checkValidity(dataTransferItemList)) != null ? _b2 : false;\n      if (preventDefaultForUnhandled) {\n        event.preventDefault();\n      }\n      if (!isSafari() && !isValid) {\n        if (event.dataTransfer) {\n          event.dataTransfer.dropEffect = \"none\";\n        }\n        return;\n      }\n      event.preventDefault();\n      if (event.dataTransfer) {\n        event.dataTransfer.dropEffect = \"copy\";\n      }\n      const currentFiles = getFiles(event);\n      switch (eventType) {\n        case \"enter\":\n          counter += 1;\n          isOverDropZone.value = true;\n          (_c = _options.onEnter) == null ? void 0 : _c.call(_options, null, event);\n          break;\n        case \"over\":\n          (_d = _options.onOver) == null ? void 0 : _d.call(_options, null, event);\n          break;\n        case \"leave\":\n          counter -= 1;\n          if (counter === 0)\n            isOverDropZone.value = false;\n          (_e = _options.onLeave) == null ? void 0 : _e.call(_options, null, event);\n          break;\n        case \"drop\":\n          counter = 0;\n          isOverDropZone.value = false;\n          if (isValid) {\n            files.value = currentFiles;\n            (_f = _options.onDrop) == null ? void 0 : _f.call(_options, currentFiles, event);\n          }\n          break;\n      }\n    };\n    useEventListener(target, \"dragenter\", (event) => handleDragEvent(event, \"enter\"));\n    useEventListener(target, \"dragover\", (event) => handleDragEvent(event, \"over\"));\n    useEventListener(target, \"dragleave\", (event) => handleDragEvent(event, \"leave\"));\n    useEventListener(target, \"drop\", (event) => handleDragEvent(event, \"drop\"));\n  }\n  return {\n    files,\n    isOverDropZone\n  };\n}\nfunction useResizeObserver(target, callback, options = {}) {\n  const { window: window2 = defaultWindow, ...observerOptions } = options;\n  let observer;\n  const isSupported = useSupported(() => window2 && \"ResizeObserver\" in window2);\n  const cleanup = () => {\n    if (observer) {\n      observer.disconnect();\n      observer = void 0;\n    }\n  };\n  const targets = computed(() => {\n    const _targets = toValue(target);\n    return Array.isArray(_targets) ? _targets.map((el) => unrefElement(el)) : [unrefElement(_targets)];\n  });\n  const stopWatch = watch(\n    targets,\n    (els) => {\n      cleanup();\n      if (isSupported.value && window2) {\n        observer = new ResizeObserver(callback);\n        for (const _el of els) {\n          if (_el)\n            observer.observe(_el, observerOptions);\n        }\n      }\n    },\n    { immediate: true, flush: \"post\" }\n  );\n  const stop = () => {\n    cleanup();\n    stopWatch();\n  };\n  tryOnScopeDispose(stop);\n  return {\n    isSupported,\n    stop\n  };\n}\nfunction useElementBounding(target, options = {}) {\n  const {\n    reset = true,\n    windowResize = true,\n    windowScroll = true,\n    immediate = true,\n    updateTiming = \"sync\"\n  } = options;\n  const height = ref(0);\n  const bottom = ref(0);\n  const left = ref(0);\n  const right = ref(0);\n  const top = ref(0);\n  const width = ref(0);\n  const x = ref(0);\n  const y = ref(0);\n  function recalculate() {\n    const el = unrefElement(target);\n    if (!el) {\n      if (reset) {\n        height.value = 0;\n        bottom.value = 0;\n        left.value = 0;\n        right.value = 0;\n        top.value = 0;\n        width.value = 0;\n        x.value = 0;\n        y.value = 0;\n      }\n      return;\n    }\n    const rect = el.getBoundingClientRect();\n    height.value = rect.height;\n    bottom.value = rect.bottom;\n    left.value = rect.left;\n    right.value = rect.right;\n    top.value = rect.top;\n    width.value = rect.width;\n    x.value = rect.x;\n    y.value = rect.y;\n  }\n  function update() {\n    if (updateTiming === \"sync\")\n      recalculate();\n    else if (updateTiming === \"next-frame\")\n      requestAnimationFrame(() => recalculate());\n  }\n  useResizeObserver(target, update);\n  watch(() => unrefElement(target), (ele) => !ele && update());\n  useMutationObserver(target, update, {\n    attributeFilter: [\"style\", \"class\"]\n  });\n  if (windowScroll)\n    useEventListener(\"scroll\", update, { capture: true, passive: true });\n  if (windowResize)\n    useEventListener(\"resize\", update, { passive: true });\n  tryOnMounted(() => {\n    if (immediate)\n      update();\n  });\n  return {\n    height,\n    bottom,\n    left,\n    right,\n    top,\n    width,\n    x,\n    y,\n    update\n  };\n}\nfunction useElementByPoint(options) {\n  const {\n    x,\n    y,\n    document: document2 = defaultDocument,\n    multiple,\n    interval = \"requestAnimationFrame\",\n    immediate = true\n  } = options;\n  const isSupported = useSupported(() => {\n    if (toValue(multiple))\n      return document2 && \"elementsFromPoint\" in document2;\n    return document2 && \"elementFromPoint\" in document2;\n  });\n  const element = ref(null);\n  const cb = () => {\n    var _a, _b;\n    element.value = toValue(multiple) ? (_a = document2 == null ? void 0 : document2.elementsFromPoint(toValue(x), toValue(y))) != null ? _a : [] : (_b = document2 == null ? void 0 : document2.elementFromPoint(toValue(x), toValue(y))) != null ? _b : null;\n  };\n  const controls = interval === \"requestAnimationFrame\" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate });\n  return {\n    isSupported,\n    element,\n    ...controls\n  };\n}\nfunction useElementHover(el, options = {}) {\n  const {\n    delayEnter = 0,\n    delayLeave = 0,\n    triggerOnRemoval = false,\n    window: window2 = defaultWindow\n  } = options;\n  const isHovered = ref(false);\n  let timer;\n  const toggle = (entering) => {\n    const delay = entering ? delayEnter : delayLeave;\n    if (timer) {\n      clearTimeout(timer);\n      timer = void 0;\n    }\n    if (delay)\n      timer = setTimeout(() => isHovered.value = entering, delay);\n    else\n      isHovered.value = entering;\n  };\n  if (!window2)\n    return isHovered;\n  useEventListener(el, \"mouseenter\", () => toggle(true), { passive: true });\n  useEventListener(el, \"mouseleave\", () => toggle(false), { passive: true });\n  if (triggerOnRemoval) {\n    onElementRemoval(\n      computed(() => unrefElement(el)),\n      () => toggle(false)\n    );\n  }\n  return isHovered;\n}\nfunction useElementSize(target, initialSize = { width: 0, height: 0 }, options = {}) {\n  const { window: window2 = defaultWindow, box = \"content-box\" } = options;\n  const isSVG = computed(() => {\n    var _a, _b;\n    return (_b = (_a = unrefElement(target)) == null ? void 0 : _a.namespaceURI) == null ? void 0 : _b.includes(\"svg\");\n  });\n  const width = ref(initialSize.width);\n  const height = ref(initialSize.height);\n  const { stop: stop1 } = useResizeObserver(\n    target,\n    ([entry]) => {\n      const boxSize = box === \"border-box\" ? entry.borderBoxSize : box === \"content-box\" ? entry.contentBoxSize : entry.devicePixelContentBoxSize;\n      if (window2 && isSVG.value) {\n        const $elem = unrefElement(target);\n        if ($elem) {\n          const rect = $elem.getBoundingClientRect();\n          width.value = rect.width;\n          height.value = rect.height;\n        }\n      } else {\n        if (boxSize) {\n          const formatBoxSize = toArray(boxSize);\n          width.value = formatBoxSize.reduce((acc, { inlineSize }) => acc + inlineSize, 0);\n          height.value = formatBoxSize.reduce((acc, { blockSize }) => acc + blockSize, 0);\n        } else {\n          width.value = entry.contentRect.width;\n          height.value = entry.contentRect.height;\n        }\n      }\n    },\n    options\n  );\n  tryOnMounted(() => {\n    const ele = unrefElement(target);\n    if (ele) {\n      width.value = \"offsetWidth\" in ele ? ele.offsetWidth : initialSize.width;\n      height.value = \"offsetHeight\" in ele ? ele.offsetHeight : initialSize.height;\n    }\n  });\n  const stop2 = watch(\n    () => unrefElement(target),\n    (ele) => {\n      width.value = ele ? initialSize.width : 0;\n      height.value = ele ? initialSize.height : 0;\n    }\n  );\n  function stop() {\n    stop1();\n    stop2();\n  }\n  return {\n    width,\n    height,\n    stop\n  };\n}\nfunction useIntersectionObserver(target, callback, options = {}) {\n  const {\n    root,\n    rootMargin = \"0px\",\n    threshold = 0,\n    window: window2 = defaultWindow,\n    immediate = true\n  } = options;\n  const isSupported = useSupported(() => window2 && \"IntersectionObserver\" in window2);\n  const targets = computed(() => {\n    const _target = toValue(target);\n    return toArray(_target).map(unrefElement).filter(notNullish);\n  });\n  let cleanup = noop;\n  const isActive = ref(immediate);\n  const stopWatch = isSupported.value ? watch(\n    () => [targets.value, unrefElement(root), isActive.value],\n    ([targets2, root2]) => {\n      cleanup();\n      if (!isActive.value)\n        return;\n      if (!targets2.length)\n        return;\n      const observer = new IntersectionObserver(\n        callback,\n        {\n          root: unrefElement(root2),\n          rootMargin,\n          threshold\n        }\n      );\n      targets2.forEach((el) => el && observer.observe(el));\n      cleanup = () => {\n        observer.disconnect();\n        cleanup = noop;\n      };\n    },\n    { immediate, flush: \"post\" }\n  ) : noop;\n  const stop = () => {\n    cleanup();\n    stopWatch();\n    isActive.value = false;\n  };\n  tryOnScopeDispose(stop);\n  return {\n    isSupported,\n    isActive,\n    pause() {\n      cleanup();\n      isActive.value = false;\n    },\n    resume() {\n      isActive.value = true;\n    },\n    stop\n  };\n}\nfunction useElementVisibility(element, options = {}) {\n  const {\n    window: window2 = defaultWindow,\n    scrollTarget,\n    threshold = 0,\n    rootMargin\n  } = options;\n  const elementIsVisible = ref(false);\n  useIntersectionObserver(\n    element,\n    (intersectionObserverEntries) => {\n      let isIntersecting = elementIsVisible.value;\n      let latestTime = 0;\n      for (const entry of intersectionObserverEntries) {\n        if (entry.time >= latestTime) {\n          latestTime = entry.time;\n          isIntersecting = entry.isIntersecting;\n        }\n      }\n      elementIsVisible.value = isIntersecting;\n    },\n    {\n      root: scrollTarget,\n      window: window2,\n      threshold,\n      rootMargin: toValue(rootMargin)\n    }\n  );\n  return elementIsVisible;\n}\nvar events = /* @__PURE__ */ new Map();\nfunction useEventBus(key) {\n  const scope = getCurrentScope();\n  function on(listener) {\n    var _a;\n    const listeners = events.get(key) || /* @__PURE__ */ new Set();\n    listeners.add(listener);\n    events.set(key, listeners);\n    const _off = () => off(listener);\n    (_a = scope == null ? void 0 : scope.cleanups) == null ? void 0 : _a.push(_off);\n    return _off;\n  }\n  function once(listener) {\n    function _listener(...args) {\n      off(_listener);\n      listener(...args);\n    }\n    return on(_listener);\n  }\n  function off(listener) {\n    const listeners = events.get(key);\n    if (!listeners)\n      return;\n    listeners.delete(listener);\n    if (!listeners.size)\n      reset();\n  }\n  function reset() {\n    events.delete(key);\n  }\n  function emit(event, payload) {\n    var _a;\n    (_a = events.get(key)) == null ? void 0 : _a.forEach((v) => v(event, payload));\n  }\n  return { on, once, off, emit, reset };\n}\nfunction resolveNestedOptions$1(options) {\n  if (options === true)\n    return {};\n  return options;\n}\nfunction useEventSource(url, events2 = [], options = {}) {\n  const event = ref(null);\n  const data = ref(null);\n  const status = ref(\"CONNECTING\");\n  const eventSource = ref(null);\n  const error = shallowRef(null);\n  const urlRef = toRef2(url);\n  const lastEventId = shallowRef(null);\n  let explicitlyClosed = false;\n  let retried = 0;\n  const {\n    withCredentials = false,\n    immediate = true,\n    autoConnect = true,\n    autoReconnect\n  } = options;\n  const close = () => {\n    if (isClient && eventSource.value) {\n      eventSource.value.close();\n      eventSource.value = null;\n      status.value = \"CLOSED\";\n      explicitlyClosed = true;\n    }\n  };\n  const _init = () => {\n    if (explicitlyClosed || typeof urlRef.value === \"undefined\")\n      return;\n    const es = new EventSource(urlRef.value, { withCredentials });\n    status.value = \"CONNECTING\";\n    eventSource.value = es;\n    es.onopen = () => {\n      status.value = \"OPEN\";\n      error.value = null;\n    };\n    es.onerror = (e) => {\n      status.value = \"CLOSED\";\n      error.value = e;\n      if (es.readyState === 2 && !explicitlyClosed && autoReconnect) {\n        es.close();\n        const {\n          retries = -1,\n          delay = 1e3,\n          onFailed\n        } = resolveNestedOptions$1(autoReconnect);\n        retried += 1;\n        if (typeof retries === \"number\" && (retries < 0 || retried < retries))\n          setTimeout(_init, delay);\n        else if (typeof retries === \"function\" && retries())\n          setTimeout(_init, delay);\n        else\n          onFailed == null ? void 0 : onFailed();\n      }\n    };\n    es.onmessage = (e) => {\n      event.value = null;\n      data.value = e.data;\n      lastEventId.value = e.lastEventId;\n    };\n    for (const event_name of events2) {\n      useEventListener(es, event_name, (e) => {\n        event.value = event_name;\n        data.value = e.data || null;\n      }, { passive: true });\n    }\n  };\n  const open = () => {\n    if (!isClient)\n      return;\n    close();\n    explicitlyClosed = false;\n    retried = 0;\n    _init();\n  };\n  if (immediate)\n    open();\n  if (autoConnect)\n    watch(urlRef, open);\n  tryOnScopeDispose(close);\n  return {\n    eventSource,\n    event,\n    data,\n    status,\n    error,\n    open,\n    close,\n    lastEventId\n  };\n}\nfunction useEyeDropper(options = {}) {\n  const { initialValue = \"\" } = options;\n  const isSupported = useSupported(() => typeof window !== \"undefined\" && \"EyeDropper\" in window);\n  const sRGBHex = ref(initialValue);\n  async function open(openOptions) {\n    if (!isSupported.value)\n      return;\n    const eyeDropper = new window.EyeDropper();\n    const result = await eyeDropper.open(openOptions);\n    sRGBHex.value = result.sRGBHex;\n    return result;\n  }\n  return { isSupported, sRGBHex, open };\n}\nfunction useFavicon(newIcon = null, options = {}) {\n  const {\n    baseUrl = \"\",\n    rel = \"icon\",\n    document: document2 = defaultDocument\n  } = options;\n  const favicon = toRef2(newIcon);\n  const applyIcon = (icon) => {\n    const elements = document2 == null ? void 0 : document2.head.querySelectorAll(`link[rel*=\"${rel}\"]`);\n    if (!elements || elements.length === 0) {\n      const link = document2 == null ? void 0 : document2.createElement(\"link\");\n      if (link) {\n        link.rel = rel;\n        link.href = `${baseUrl}${icon}`;\n        link.type = `image/${icon.split(\".\").pop()}`;\n        document2 == null ? void 0 : document2.head.append(link);\n      }\n      return;\n    }\n    elements == null ? void 0 : elements.forEach((el) => el.href = `${baseUrl}${icon}`);\n  };\n  watch(\n    favicon,\n    (i, o) => {\n      if (typeof i === \"string\" && i !== o)\n        applyIcon(i);\n    },\n    { immediate: true }\n  );\n  return favicon;\n}\nvar payloadMapping = {\n  json: \"application/json\",\n  text: \"text/plain\"\n};\nfunction isFetchOptions(obj) {\n  return obj && containsProp(obj, \"immediate\", \"refetch\", \"initialData\", \"timeout\", \"beforeFetch\", \"afterFetch\", \"onFetchError\", \"fetch\", \"updateDataOnError\");\n}\nvar reAbsolute = /^(?:[a-z][a-z\\d+\\-.]*:)?\\/\\//i;\nfunction isAbsoluteURL(url) {\n  return reAbsolute.test(url);\n}\nfunction headersToObject(headers) {\n  if (typeof Headers !== \"undefined\" && headers instanceof Headers)\n    return Object.fromEntries(headers.entries());\n  return headers;\n}\nfunction combineCallbacks(combination, ...callbacks) {\n  if (combination === \"overwrite\") {\n    return async (ctx) => {\n      const callback = callbacks[callbacks.length - 1];\n      if (callback)\n        return { ...ctx, ...await callback(ctx) };\n      return ctx;\n    };\n  } else {\n    return async (ctx) => {\n      for (const callback of callbacks) {\n        if (callback)\n          ctx = { ...ctx, ...await callback(ctx) };\n      }\n      return ctx;\n    };\n  }\n}\nfunction createFetch(config = {}) {\n  const _combination = config.combination || \"chain\";\n  const _options = config.options || {};\n  const _fetchOptions = config.fetchOptions || {};\n  function useFactoryFetch(url, ...args) {\n    const computedUrl = computed(() => {\n      const baseUrl = toValue(config.baseUrl);\n      const targetUrl = toValue(url);\n      return baseUrl && !isAbsoluteURL(targetUrl) ? joinPaths(baseUrl, targetUrl) : targetUrl;\n    });\n    let options = _options;\n    let fetchOptions = _fetchOptions;\n    if (args.length > 0) {\n      if (isFetchOptions(args[0])) {\n        options = {\n          ...options,\n          ...args[0],\n          beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[0].beforeFetch),\n          afterFetch: combineCallbacks(_combination, _options.afterFetch, args[0].afterFetch),\n          onFetchError: combineCallbacks(_combination, _options.onFetchError, args[0].onFetchError)\n        };\n      } else {\n        fetchOptions = {\n          ...fetchOptions,\n          ...args[0],\n          headers: {\n            ...headersToObject(fetchOptions.headers) || {},\n            ...headersToObject(args[0].headers) || {}\n          }\n        };\n      }\n    }\n    if (args.length > 1 && isFetchOptions(args[1])) {\n      options = {\n        ...options,\n        ...args[1],\n        beforeFetch: combineCallbacks(_combination, _options.beforeFetch, args[1].beforeFetch),\n        afterFetch: combineCallbacks(_combination, _options.afterFetch, args[1].afterFetch),\n        onFetchError: combineCallbacks(_combination, _options.onFetchError, args[1].onFetchError)\n      };\n    }\n    return useFetch(computedUrl, fetchOptions, options);\n  }\n  return useFactoryFetch;\n}\nfunction useFetch(url, ...args) {\n  var _a;\n  const supportsAbort = typeof AbortController === \"function\";\n  let fetchOptions = {};\n  let options = {\n    immediate: true,\n    refetch: false,\n    timeout: 0,\n    updateDataOnError: false\n  };\n  const config = {\n    method: \"GET\",\n    type: \"text\",\n    payload: void 0\n  };\n  if (args.length > 0) {\n    if (isFetchOptions(args[0]))\n      options = { ...options, ...args[0] };\n    else\n      fetchOptions = args[0];\n  }\n  if (args.length > 1) {\n    if (isFetchOptions(args[1]))\n      options = { ...options, ...args[1] };\n  }\n  const {\n    fetch = (_a = defaultWindow) == null ? void 0 : _a.fetch,\n    initialData,\n    timeout\n  } = options;\n  const responseEvent = createEventHook();\n  const errorEvent = createEventHook();\n  const finallyEvent = createEventHook();\n  const isFinished = ref(false);\n  const isFetching = ref(false);\n  const aborted = ref(false);\n  const statusCode = ref(null);\n  const response = shallowRef(null);\n  const error = shallowRef(null);\n  const data = shallowRef(initialData || null);\n  const canAbort = computed(() => supportsAbort && isFetching.value);\n  let controller;\n  let timer;\n  const abort = () => {\n    if (supportsAbort) {\n      controller == null ? void 0 : controller.abort();\n      controller = new AbortController();\n      controller.signal.onabort = () => aborted.value = true;\n      fetchOptions = {\n        ...fetchOptions,\n        signal: controller.signal\n      };\n    }\n  };\n  const loading = (isLoading) => {\n    isFetching.value = isLoading;\n    isFinished.value = !isLoading;\n  };\n  if (timeout)\n    timer = useTimeoutFn(abort, timeout, { immediate: false });\n  let executeCounter = 0;\n  const execute = async (throwOnFailed = false) => {\n    var _a2, _b;\n    abort();\n    loading(true);\n    error.value = null;\n    statusCode.value = null;\n    aborted.value = false;\n    executeCounter += 1;\n    const currentExecuteCounter = executeCounter;\n    const defaultFetchOptions = {\n      method: config.method,\n      headers: {}\n    };\n    const payload = toValue(config.payload);\n    if (payload) {\n      const headers = headersToObject(defaultFetchOptions.headers);\n      const proto = Object.getPrototypeOf(payload);\n      if (!config.payloadType && payload && (proto === Object.prototype || Array.isArray(proto)) && !(payload instanceof FormData))\n        config.payloadType = \"json\";\n      if (config.payloadType)\n        headers[\"Content-Type\"] = (_a2 = payloadMapping[config.payloadType]) != null ? _a2 : config.payloadType;\n      defaultFetchOptions.body = config.payloadType === \"json\" ? JSON.stringify(payload) : payload;\n    }\n    let isCanceled = false;\n    const context = {\n      url: toValue(url),\n      options: {\n        ...defaultFetchOptions,\n        ...fetchOptions\n      },\n      cancel: () => {\n        isCanceled = true;\n      }\n    };\n    if (options.beforeFetch)\n      Object.assign(context, await options.beforeFetch(context));\n    if (isCanceled || !fetch) {\n      loading(false);\n      return Promise.resolve(null);\n    }\n    let responseData = null;\n    if (timer)\n      timer.start();\n    return fetch(\n      context.url,\n      {\n        ...defaultFetchOptions,\n        ...context.options,\n        headers: {\n          ...headersToObject(defaultFetchOptions.headers),\n          ...headersToObject((_b = context.options) == null ? void 0 : _b.headers)\n        }\n      }\n    ).then(async (fetchResponse) => {\n      response.value = fetchResponse;\n      statusCode.value = fetchResponse.status;\n      responseData = await fetchResponse.clone()[config.type]();\n      if (!fetchResponse.ok) {\n        data.value = initialData || null;\n        throw new Error(fetchResponse.statusText);\n      }\n      if (options.afterFetch) {\n        ({ data: responseData } = await options.afterFetch({\n          data: responseData,\n          response: fetchResponse,\n          context,\n          execute\n        }));\n      }\n      data.value = responseData;\n      responseEvent.trigger(fetchResponse);\n      return fetchResponse;\n    }).catch(async (fetchError) => {\n      let errorData = fetchError.message || fetchError.name;\n      if (options.onFetchError) {\n        ({ error: errorData, data: responseData } = await options.onFetchError({\n          data: responseData,\n          error: fetchError,\n          response: response.value,\n          context,\n          execute\n        }));\n      }\n      error.value = errorData;\n      if (options.updateDataOnError)\n        data.value = responseData;\n      errorEvent.trigger(fetchError);\n      if (throwOnFailed)\n        throw fetchError;\n      return null;\n    }).finally(() => {\n      if (currentExecuteCounter === executeCounter)\n        loading(false);\n      if (timer)\n        timer.stop();\n      finallyEvent.trigger(null);\n    });\n  };\n  const refetch = toRef2(options.refetch);\n  watch(\n    [\n      refetch,\n      toRef2(url)\n    ],\n    ([refetch2]) => refetch2 && execute(),\n    { deep: true }\n  );\n  const shell = {\n    isFinished: readonly(isFinished),\n    isFetching: readonly(isFetching),\n    statusCode,\n    response,\n    error,\n    data,\n    canAbort,\n    aborted,\n    abort,\n    execute,\n    onFetchResponse: responseEvent.on,\n    onFetchError: errorEvent.on,\n    onFetchFinally: finallyEvent.on,\n    // method\n    get: setMethod(\"GET\"),\n    put: setMethod(\"PUT\"),\n    post: setMethod(\"POST\"),\n    delete: setMethod(\"DELETE\"),\n    patch: setMethod(\"PATCH\"),\n    head: setMethod(\"HEAD\"),\n    options: setMethod(\"OPTIONS\"),\n    // type\n    json: setType(\"json\"),\n    text: setType(\"text\"),\n    blob: setType(\"blob\"),\n    arrayBuffer: setType(\"arrayBuffer\"),\n    formData: setType(\"formData\")\n  };\n  function setMethod(method) {\n    return (payload, payloadType) => {\n      if (!isFetching.value) {\n        config.method = method;\n        config.payload = payload;\n        config.payloadType = payloadType;\n        if (isRef(config.payload)) {\n          watch(\n            [\n              refetch,\n              toRef2(config.payload)\n            ],\n            ([refetch2]) => refetch2 && execute(),\n            { deep: true }\n          );\n        }\n        return {\n          ...shell,\n          then(onFulfilled, onRejected) {\n            return waitUntilFinished().then(onFulfilled, onRejected);\n          }\n        };\n      }\n      return void 0;\n    };\n  }\n  function waitUntilFinished() {\n    return new Promise((resolve, reject) => {\n      until(isFinished).toBe(true).then(() => resolve(shell)).catch(reject);\n    });\n  }\n  function setType(type) {\n    return () => {\n      if (!isFetching.value) {\n        config.type = type;\n        return {\n          ...shell,\n          then(onFulfilled, onRejected) {\n            return waitUntilFinished().then(onFulfilled, onRejected);\n          }\n        };\n      }\n      return void 0;\n    };\n  }\n  if (options.immediate)\n    Promise.resolve().then(() => execute());\n  return {\n    ...shell,\n    then(onFulfilled, onRejected) {\n      return waitUntilFinished().then(onFulfilled, onRejected);\n    }\n  };\n}\nfunction joinPaths(start, end) {\n  if (!start.endsWith(\"/\") && !end.startsWith(\"/\")) {\n    return `${start}/${end}`;\n  }\n  if (start.endsWith(\"/\") && end.startsWith(\"/\")) {\n    return `${start.slice(0, -1)}${end}`;\n  }\n  return `${start}${end}`;\n}\nvar DEFAULT_OPTIONS = {\n  multiple: true,\n  accept: \"*\",\n  reset: false,\n  directory: false\n};\nfunction prepareInitialFiles(files) {\n  if (!files)\n    return null;\n  if (files instanceof FileList)\n    return files;\n  const dt = new DataTransfer();\n  for (const file of files) {\n    dt.items.add(file);\n  }\n  return dt.files;\n}\nfunction useFileDialog(options = {}) {\n  const {\n    document: document2 = defaultDocument\n  } = options;\n  const files = ref(prepareInitialFiles(options.initialFiles));\n  const { on: onChange, trigger: changeTrigger } = createEventHook();\n  const { on: onCancel, trigger: cancelTrigger } = createEventHook();\n  let input;\n  if (document2) {\n    input = document2.createElement(\"input\");\n    input.type = \"file\";\n    input.onchange = (event) => {\n      const result = event.target;\n      files.value = result.files;\n      changeTrigger(files.value);\n    };\n    input.oncancel = () => {\n      cancelTrigger();\n    };\n  }\n  const reset = () => {\n    files.value = null;\n    if (input && input.value) {\n      input.value = \"\";\n      changeTrigger(null);\n    }\n  };\n  const open = (localOptions) => {\n    if (!input)\n      return;\n    const _options = {\n      ...DEFAULT_OPTIONS,\n      ...options,\n      ...localOptions\n    };\n    input.multiple = _options.multiple;\n    input.accept = _options.accept;\n    input.webkitdirectory = _options.directory;\n    if (hasOwn(_options, \"capture\"))\n      input.capture = _options.capture;\n    if (_options.reset)\n      reset();\n    input.click();\n  };\n  return {\n    files: readonly(files),\n    open,\n    reset,\n    onCancel,\n    onChange\n  };\n}\nfunction useFileSystemAccess(options = {}) {\n  const {\n    window: _window = defaultWindow,\n    dataType = \"Text\"\n  } = options;\n  const window2 = _window;\n  const isSupported = useSupported(() => window2 && \"showSaveFilePicker\" in window2 && \"showOpenFilePicker\" in window2);\n  const fileHandle = ref();\n  const data = ref();\n  const file = ref();\n  const fileName = computed(() => {\n    var _a, _b;\n    return (_b = (_a = file.value) == null ? void 0 : _a.name) != null ? _b : \"\";\n  });\n  const fileMIME = computed(() => {\n    var _a, _b;\n    return (_b = (_a = file.value) == null ? void 0 : _a.type) != null ? _b : \"\";\n  });\n  const fileSize = computed(() => {\n    var _a, _b;\n    return (_b = (_a = file.value) == null ? void 0 : _a.size) != null ? _b : 0;\n  });\n  const fileLastModified = computed(() => {\n    var _a, _b;\n    return (_b = (_a = file.value) == null ? void 0 : _a.lastModified) != null ? _b : 0;\n  });\n  async function open(_options = {}) {\n    if (!isSupported.value)\n      return;\n    const [handle] = await window2.showOpenFilePicker({ ...toValue(options), ..._options });\n    fileHandle.value = handle;\n    await updateData();\n  }\n  async function create(_options = {}) {\n    if (!isSupported.value)\n      return;\n    fileHandle.value = await window2.showSaveFilePicker({ ...options, ..._options });\n    data.value = void 0;\n    await updateData();\n  }\n  async function save(_options = {}) {\n    if (!isSupported.value)\n      return;\n    if (!fileHandle.value)\n      return saveAs(_options);\n    if (data.value) {\n      const writableStream = await fileHandle.value.createWritable();\n      await writableStream.write(data.value);\n      await writableStream.close();\n    }\n    await updateFile();\n  }\n  async function saveAs(_options = {}) {\n    if (!isSupported.value)\n      return;\n    fileHandle.value = await window2.showSaveFilePicker({ ...options, ..._options });\n    if (data.value) {\n      const writableStream = await fileHandle.value.createWritable();\n      await writableStream.write(data.value);\n      await writableStream.close();\n    }\n    await updateFile();\n  }\n  async function updateFile() {\n    var _a;\n    file.value = await ((_a = fileHandle.value) == null ? void 0 : _a.getFile());\n  }\n  async function updateData() {\n    var _a, _b;\n    await updateFile();\n    const type = toValue(dataType);\n    if (type === \"Text\")\n      data.value = await ((_a = file.value) == null ? void 0 : _a.text());\n    else if (type === \"ArrayBuffer\")\n      data.value = await ((_b = file.value) == null ? void 0 : _b.arrayBuffer());\n    else if (type === \"Blob\")\n      data.value = file.value;\n  }\n  watch(() => toValue(dataType), updateData);\n  return {\n    isSupported,\n    data,\n    file,\n    fileName,\n    fileMIME,\n    fileSize,\n    fileLastModified,\n    open,\n    create,\n    save,\n    saveAs,\n    updateData\n  };\n}\nfunction useFocus(target, options = {}) {\n  const { initialValue = false, focusVisible = false, preventScroll = false } = options;\n  const innerFocused = ref(false);\n  const targetElement = computed(() => unrefElement(target));\n  const listenerOptions = { passive: true };\n  useEventListener(targetElement, \"focus\", (event) => {\n    var _a, _b;\n    if (!focusVisible || ((_b = (_a = event.target).matches) == null ? void 0 : _b.call(_a, \":focus-visible\")))\n      innerFocused.value = true;\n  }, listenerOptions);\n  useEventListener(targetElement, \"blur\", () => innerFocused.value = false, listenerOptions);\n  const focused = computed({\n    get: () => innerFocused.value,\n    set(value) {\n      var _a, _b;\n      if (!value && innerFocused.value)\n        (_a = targetElement.value) == null ? void 0 : _a.blur();\n      else if (value && !innerFocused.value)\n        (_b = targetElement.value) == null ? void 0 : _b.focus({ preventScroll });\n    }\n  });\n  watch(\n    targetElement,\n    () => {\n      focused.value = initialValue;\n    },\n    { immediate: true, flush: \"post\" }\n  );\n  return { focused };\n}\nvar EVENT_FOCUS_IN = \"focusin\";\nvar EVENT_FOCUS_OUT = \"focusout\";\nvar PSEUDO_CLASS_FOCUS_WITHIN = \":focus-within\";\nfunction useFocusWithin(target, options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const targetElement = computed(() => unrefElement(target));\n  const _focused = ref(false);\n  const focused = computed(() => _focused.value);\n  const activeElement = useActiveElement(options);\n  if (!window2 || !activeElement.value) {\n    return { focused };\n  }\n  const listenerOptions = { passive: true };\n  useEventListener(targetElement, EVENT_FOCUS_IN, () => _focused.value = true, listenerOptions);\n  useEventListener(targetElement, EVENT_FOCUS_OUT, () => {\n    var _a, _b, _c;\n    return _focused.value = (_c = (_b = (_a = targetElement.value) == null ? void 0 : _a.matches) == null ? void 0 : _b.call(_a, PSEUDO_CLASS_FOCUS_WITHIN)) != null ? _c : false;\n  }, listenerOptions);\n  return { focused };\n}\nfunction useFps(options) {\n  var _a;\n  const fps = ref(0);\n  if (typeof performance === \"undefined\")\n    return fps;\n  const every = (_a = options == null ? void 0 : options.every) != null ? _a : 10;\n  let last = performance.now();\n  let ticks = 0;\n  useRafFn(() => {\n    ticks += 1;\n    if (ticks >= every) {\n      const now2 = performance.now();\n      const diff = now2 - last;\n      fps.value = Math.round(1e3 / (diff / ticks));\n      last = now2;\n      ticks = 0;\n    }\n  });\n  return fps;\n}\nvar eventHandlers = [\n  \"fullscreenchange\",\n  \"webkitfullscreenchange\",\n  \"webkitendfullscreen\",\n  \"mozfullscreenchange\",\n  \"MSFullscreenChange\"\n];\nfunction useFullscreen(target, options = {}) {\n  const {\n    document: document2 = defaultDocument,\n    autoExit = false\n  } = options;\n  const targetRef = computed(() => {\n    var _a;\n    return (_a = unrefElement(target)) != null ? _a : document2 == null ? void 0 : document2.querySelector(\"html\");\n  });\n  const isFullscreen = ref(false);\n  const requestMethod = computed(() => {\n    return [\n      \"requestFullscreen\",\n      \"webkitRequestFullscreen\",\n      \"webkitEnterFullscreen\",\n      \"webkitEnterFullScreen\",\n      \"webkitRequestFullScreen\",\n      \"mozRequestFullScreen\",\n      \"msRequestFullscreen\"\n    ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value);\n  });\n  const exitMethod = computed(() => {\n    return [\n      \"exitFullscreen\",\n      \"webkitExitFullscreen\",\n      \"webkitExitFullScreen\",\n      \"webkitCancelFullScreen\",\n      \"mozCancelFullScreen\",\n      \"msExitFullscreen\"\n    ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value);\n  });\n  const fullscreenEnabled = computed(() => {\n    return [\n      \"fullScreen\",\n      \"webkitIsFullScreen\",\n      \"webkitDisplayingFullscreen\",\n      \"mozFullScreen\",\n      \"msFullscreenElement\"\n    ].find((m) => document2 && m in document2 || targetRef.value && m in targetRef.value);\n  });\n  const fullscreenElementMethod = [\n    \"fullscreenElement\",\n    \"webkitFullscreenElement\",\n    \"mozFullScreenElement\",\n    \"msFullscreenElement\"\n  ].find((m) => document2 && m in document2);\n  const isSupported = useSupported(() => targetRef.value && document2 && requestMethod.value !== void 0 && exitMethod.value !== void 0 && fullscreenEnabled.value !== void 0);\n  const isCurrentElementFullScreen = () => {\n    if (fullscreenElementMethod)\n      return (document2 == null ? void 0 : document2[fullscreenElementMethod]) === targetRef.value;\n    return false;\n  };\n  const isElementFullScreen = () => {\n    if (fullscreenEnabled.value) {\n      if (document2 && document2[fullscreenEnabled.value] != null) {\n        return document2[fullscreenEnabled.value];\n      } else {\n        const target2 = targetRef.value;\n        if ((target2 == null ? void 0 : target2[fullscreenEnabled.value]) != null) {\n          return Boolean(target2[fullscreenEnabled.value]);\n        }\n      }\n    }\n    return false;\n  };\n  async function exit() {\n    if (!isSupported.value || !isFullscreen.value)\n      return;\n    if (exitMethod.value) {\n      if ((document2 == null ? void 0 : document2[exitMethod.value]) != null) {\n        await document2[exitMethod.value]();\n      } else {\n        const target2 = targetRef.value;\n        if ((target2 == null ? void 0 : target2[exitMethod.value]) != null)\n          await target2[exitMethod.value]();\n      }\n    }\n    isFullscreen.value = false;\n  }\n  async function enter() {\n    if (!isSupported.value || isFullscreen.value)\n      return;\n    if (isElementFullScreen())\n      await exit();\n    const target2 = targetRef.value;\n    if (requestMethod.value && (target2 == null ? void 0 : target2[requestMethod.value]) != null) {\n      await target2[requestMethod.value]();\n      isFullscreen.value = true;\n    }\n  }\n  async function toggle() {\n    await (isFullscreen.value ? exit() : enter());\n  }\n  const handlerCallback = () => {\n    const isElementFullScreenValue = isElementFullScreen();\n    if (!isElementFullScreenValue || isElementFullScreenValue && isCurrentElementFullScreen())\n      isFullscreen.value = isElementFullScreenValue;\n  };\n  const listenerOptions = { capture: false, passive: true };\n  useEventListener(document2, eventHandlers, handlerCallback, listenerOptions);\n  useEventListener(() => unrefElement(targetRef), eventHandlers, handlerCallback, listenerOptions);\n  if (autoExit)\n    tryOnScopeDispose(exit);\n  return {\n    isSupported,\n    isFullscreen,\n    enter,\n    exit,\n    toggle\n  };\n}\nfunction mapGamepadToXbox360Controller(gamepad) {\n  return computed(() => {\n    if (gamepad.value) {\n      return {\n        buttons: {\n          a: gamepad.value.buttons[0],\n          b: gamepad.value.buttons[1],\n          x: gamepad.value.buttons[2],\n          y: gamepad.value.buttons[3]\n        },\n        bumper: {\n          left: gamepad.value.buttons[4],\n          right: gamepad.value.buttons[5]\n        },\n        triggers: {\n          left: gamepad.value.buttons[6],\n          right: gamepad.value.buttons[7]\n        },\n        stick: {\n          left: {\n            horizontal: gamepad.value.axes[0],\n            vertical: gamepad.value.axes[1],\n            button: gamepad.value.buttons[10]\n          },\n          right: {\n            horizontal: gamepad.value.axes[2],\n            vertical: gamepad.value.axes[3],\n            button: gamepad.value.buttons[11]\n          }\n        },\n        dpad: {\n          up: gamepad.value.buttons[12],\n          down: gamepad.value.buttons[13],\n          left: gamepad.value.buttons[14],\n          right: gamepad.value.buttons[15]\n        },\n        back: gamepad.value.buttons[8],\n        start: gamepad.value.buttons[9]\n      };\n    }\n    return null;\n  });\n}\nfunction useGamepad(options = {}) {\n  const {\n    navigator: navigator2 = defaultNavigator\n  } = options;\n  const isSupported = useSupported(() => navigator2 && \"getGamepads\" in navigator2);\n  const gamepads = ref([]);\n  const onConnectedHook = createEventHook();\n  const onDisconnectedHook = createEventHook();\n  const stateFromGamepad = (gamepad) => {\n    const hapticActuators = [];\n    const vibrationActuator = \"vibrationActuator\" in gamepad ? gamepad.vibrationActuator : null;\n    if (vibrationActuator)\n      hapticActuators.push(vibrationActuator);\n    if (gamepad.hapticActuators)\n      hapticActuators.push(...gamepad.hapticActuators);\n    return {\n      id: gamepad.id,\n      index: gamepad.index,\n      connected: gamepad.connected,\n      mapping: gamepad.mapping,\n      timestamp: gamepad.timestamp,\n      vibrationActuator: gamepad.vibrationActuator,\n      hapticActuators,\n      axes: gamepad.axes.map((axes) => axes),\n      buttons: gamepad.buttons.map((button) => ({ pressed: button.pressed, touched: button.touched, value: button.value }))\n    };\n  };\n  const updateGamepadState = () => {\n    const _gamepads = (navigator2 == null ? void 0 : navigator2.getGamepads()) || [];\n    for (const gamepad of _gamepads) {\n      if (gamepad && gamepads.value[gamepad.index])\n        gamepads.value[gamepad.index] = stateFromGamepad(gamepad);\n    }\n  };\n  const { isActive, pause, resume } = useRafFn(updateGamepadState);\n  const onGamepadConnected = (gamepad) => {\n    if (!gamepads.value.some(({ index }) => index === gamepad.index)) {\n      gamepads.value.push(stateFromGamepad(gamepad));\n      onConnectedHook.trigger(gamepad.index);\n    }\n    resume();\n  };\n  const onGamepadDisconnected = (gamepad) => {\n    gamepads.value = gamepads.value.filter((x) => x.index !== gamepad.index);\n    onDisconnectedHook.trigger(gamepad.index);\n  };\n  const listenerOptions = { passive: true };\n  useEventListener(\"gamepadconnected\", (e) => onGamepadConnected(e.gamepad), listenerOptions);\n  useEventListener(\"gamepaddisconnected\", (e) => onGamepadDisconnected(e.gamepad), listenerOptions);\n  tryOnMounted(() => {\n    const _gamepads = (navigator2 == null ? void 0 : navigator2.getGamepads()) || [];\n    for (const gamepad of _gamepads) {\n      if (gamepad && gamepads.value[gamepad.index])\n        onGamepadConnected(gamepad);\n    }\n  });\n  pause();\n  return {\n    isSupported,\n    onConnected: onConnectedHook.on,\n    onDisconnected: onDisconnectedHook.on,\n    gamepads,\n    pause,\n    resume,\n    isActive\n  };\n}\nfunction useGeolocation(options = {}) {\n  const {\n    enableHighAccuracy = true,\n    maximumAge = 3e4,\n    timeout = 27e3,\n    navigator: navigator2 = defaultNavigator,\n    immediate = true\n  } = options;\n  const isSupported = useSupported(() => navigator2 && \"geolocation\" in navigator2);\n  const locatedAt = ref(null);\n  const error = shallowRef(null);\n  const coords = ref({\n    accuracy: 0,\n    latitude: Number.POSITIVE_INFINITY,\n    longitude: Number.POSITIVE_INFINITY,\n    altitude: null,\n    altitudeAccuracy: null,\n    heading: null,\n    speed: null\n  });\n  function updatePosition(position) {\n    locatedAt.value = position.timestamp;\n    coords.value = position.coords;\n    error.value = null;\n  }\n  let watcher;\n  function resume() {\n    if (isSupported.value) {\n      watcher = navigator2.geolocation.watchPosition(\n        updatePosition,\n        (err) => error.value = err,\n        {\n          enableHighAccuracy,\n          maximumAge,\n          timeout\n        }\n      );\n    }\n  }\n  if (immediate)\n    resume();\n  function pause() {\n    if (watcher && navigator2)\n      navigator2.geolocation.clearWatch(watcher);\n  }\n  tryOnScopeDispose(() => {\n    pause();\n  });\n  return {\n    isSupported,\n    coords,\n    locatedAt,\n    error,\n    resume,\n    pause\n  };\n}\nvar defaultEvents$1 = [\"mousemove\", \"mousedown\", \"resize\", \"keydown\", \"touchstart\", \"wheel\"];\nvar oneMinute = 6e4;\nfunction useIdle(timeout = oneMinute, options = {}) {\n  const {\n    initialState = false,\n    listenForVisibilityChange = true,\n    events: events2 = defaultEvents$1,\n    window: window2 = defaultWindow,\n    eventFilter = throttleFilter(50)\n  } = options;\n  const idle = ref(initialState);\n  const lastActive = ref(timestamp());\n  let timer;\n  const reset = () => {\n    idle.value = false;\n    clearTimeout(timer);\n    timer = setTimeout(() => idle.value = true, timeout);\n  };\n  const onEvent = createFilterWrapper(\n    eventFilter,\n    () => {\n      lastActive.value = timestamp();\n      reset();\n    }\n  );\n  if (window2) {\n    const document2 = window2.document;\n    const listenerOptions = { passive: true };\n    for (const event of events2)\n      useEventListener(window2, event, onEvent, listenerOptions);\n    if (listenForVisibilityChange) {\n      useEventListener(document2, \"visibilitychange\", () => {\n        if (!document2.hidden)\n          onEvent();\n      }, listenerOptions);\n    }\n    reset();\n  }\n  return {\n    idle,\n    lastActive,\n    reset\n  };\n}\nasync function loadImage(options) {\n  return new Promise((resolve, reject) => {\n    const img = new Image();\n    const { src, srcset, sizes, class: clazz, loading, crossorigin, referrerPolicy, width, height, decoding, fetchPriority, ismap, usemap } = options;\n    img.src = src;\n    if (srcset != null)\n      img.srcset = srcset;\n    if (sizes != null)\n      img.sizes = sizes;\n    if (clazz != null)\n      img.className = clazz;\n    if (loading != null)\n      img.loading = loading;\n    if (crossorigin != null)\n      img.crossOrigin = crossorigin;\n    if (referrerPolicy != null)\n      img.referrerPolicy = referrerPolicy;\n    if (width != null)\n      img.width = width;\n    if (height != null)\n      img.height = height;\n    if (decoding != null)\n      img.decoding = decoding;\n    if (fetchPriority != null)\n      img.fetchPriority = fetchPriority;\n    if (ismap != null)\n      img.isMap = ismap;\n    if (usemap != null)\n      img.useMap = usemap;\n    img.onload = () => resolve(img);\n    img.onerror = reject;\n  });\n}\nfunction useImage(options, asyncStateOptions = {}) {\n  const state = useAsyncState(\n    () => loadImage(toValue(options)),\n    void 0,\n    {\n      resetOnExecute: true,\n      ...asyncStateOptions\n    }\n  );\n  watch(\n    () => toValue(options),\n    () => state.execute(asyncStateOptions.delay),\n    { deep: true }\n  );\n  return state;\n}\nfunction resolveElement(el) {\n  if (typeof Window !== \"undefined\" && el instanceof Window)\n    return el.document.documentElement;\n  if (typeof Document !== \"undefined\" && el instanceof Document)\n    return el.documentElement;\n  return el;\n}\nvar ARRIVED_STATE_THRESHOLD_PIXELS = 1;\nfunction useScroll(element, options = {}) {\n  const {\n    throttle = 0,\n    idle = 200,\n    onStop = noop,\n    onScroll = noop,\n    offset = {\n      left: 0,\n      right: 0,\n      top: 0,\n      bottom: 0\n    },\n    eventListenerOptions = {\n      capture: false,\n      passive: true\n    },\n    behavior = \"auto\",\n    window: window2 = defaultWindow,\n    onError = (e) => {\n      console.error(e);\n    }\n  } = options;\n  const internalX = ref(0);\n  const internalY = ref(0);\n  const x = computed({\n    get() {\n      return internalX.value;\n    },\n    set(x2) {\n      scrollTo(x2, void 0);\n    }\n  });\n  const y = computed({\n    get() {\n      return internalY.value;\n    },\n    set(y2) {\n      scrollTo(void 0, y2);\n    }\n  });\n  function scrollTo(_x, _y) {\n    var _a, _b, _c, _d;\n    if (!window2)\n      return;\n    const _element = toValue(element);\n    if (!_element)\n      return;\n    (_c = _element instanceof Document ? window2.document.body : _element) == null ? void 0 : _c.scrollTo({\n      top: (_a = toValue(_y)) != null ? _a : y.value,\n      left: (_b = toValue(_x)) != null ? _b : x.value,\n      behavior: toValue(behavior)\n    });\n    const scrollContainer = ((_d = _element == null ? void 0 : _element.document) == null ? void 0 : _d.documentElement) || (_element == null ? void 0 : _element.documentElement) || _element;\n    if (x != null)\n      internalX.value = scrollContainer.scrollLeft;\n    if (y != null)\n      internalY.value = scrollContainer.scrollTop;\n  }\n  const isScrolling = ref(false);\n  const arrivedState = reactive({\n    left: true,\n    right: false,\n    top: true,\n    bottom: false\n  });\n  const directions = reactive({\n    left: false,\n    right: false,\n    top: false,\n    bottom: false\n  });\n  const onScrollEnd = (e) => {\n    if (!isScrolling.value)\n      return;\n    isScrolling.value = false;\n    directions.left = false;\n    directions.right = false;\n    directions.top = false;\n    directions.bottom = false;\n    onStop(e);\n  };\n  const onScrollEndDebounced = useDebounceFn(onScrollEnd, throttle + idle);\n  const setArrivedState = (target) => {\n    var _a;\n    if (!window2)\n      return;\n    const el = ((_a = target == null ? void 0 : target.document) == null ? void 0 : _a.documentElement) || (target == null ? void 0 : target.documentElement) || unrefElement(target);\n    const { display, flexDirection, direction } = getComputedStyle(el);\n    const directionMultipler = direction === \"rtl\" ? -1 : 1;\n    const scrollLeft = el.scrollLeft;\n    directions.left = scrollLeft < internalX.value;\n    directions.right = scrollLeft > internalX.value;\n    const left = scrollLeft * directionMultipler <= (offset.left || 0);\n    const right = scrollLeft * directionMultipler + el.clientWidth >= el.scrollWidth - (offset.right || 0) - ARRIVED_STATE_THRESHOLD_PIXELS;\n    if (display === \"flex\" && flexDirection === \"row-reverse\") {\n      arrivedState.left = right;\n      arrivedState.right = left;\n    } else {\n      arrivedState.left = left;\n      arrivedState.right = right;\n    }\n    internalX.value = scrollLeft;\n    let scrollTop = el.scrollTop;\n    if (target === window2.document && !scrollTop)\n      scrollTop = window2.document.body.scrollTop;\n    directions.top = scrollTop < internalY.value;\n    directions.bottom = scrollTop > internalY.value;\n    const top = scrollTop <= (offset.top || 0);\n    const bottom = scrollTop + el.clientHeight >= el.scrollHeight - (offset.bottom || 0) - ARRIVED_STATE_THRESHOLD_PIXELS;\n    if (display === \"flex\" && flexDirection === \"column-reverse\") {\n      arrivedState.top = bottom;\n      arrivedState.bottom = top;\n    } else {\n      arrivedState.top = top;\n      arrivedState.bottom = bottom;\n    }\n    internalY.value = scrollTop;\n  };\n  const onScrollHandler = (e) => {\n    var _a;\n    if (!window2)\n      return;\n    const eventTarget = (_a = e.target.documentElement) != null ? _a : e.target;\n    setArrivedState(eventTarget);\n    isScrolling.value = true;\n    onScrollEndDebounced(e);\n    onScroll(e);\n  };\n  useEventListener(\n    element,\n    \"scroll\",\n    throttle ? useThrottleFn(onScrollHandler, throttle, true, false) : onScrollHandler,\n    eventListenerOptions\n  );\n  tryOnMounted(() => {\n    try {\n      const _element = toValue(element);\n      if (!_element)\n        return;\n      setArrivedState(_element);\n    } catch (e) {\n      onError(e);\n    }\n  });\n  useEventListener(\n    element,\n    \"scrollend\",\n    onScrollEnd,\n    eventListenerOptions\n  );\n  return {\n    x,\n    y,\n    isScrolling,\n    arrivedState,\n    directions,\n    measure() {\n      const _element = toValue(element);\n      if (window2 && _element)\n        setArrivedState(_element);\n    }\n  };\n}\nfunction useInfiniteScroll(element, onLoadMore, options = {}) {\n  var _a;\n  const {\n    direction = \"bottom\",\n    interval = 100,\n    canLoadMore = () => true\n  } = options;\n  const state = reactive(useScroll(\n    element,\n    {\n      ...options,\n      offset: {\n        [direction]: (_a = options.distance) != null ? _a : 0,\n        ...options.offset\n      }\n    }\n  ));\n  const promise = ref();\n  const isLoading = computed(() => !!promise.value);\n  const observedElement = computed(() => {\n    return resolveElement(toValue(element));\n  });\n  const isElementVisible = useElementVisibility(observedElement);\n  function checkAndLoad() {\n    state.measure();\n    if (!observedElement.value || !isElementVisible.value || !canLoadMore(observedElement.value))\n      return;\n    const { scrollHeight, clientHeight, scrollWidth, clientWidth } = observedElement.value;\n    const isNarrower = direction === \"bottom\" || direction === \"top\" ? scrollHeight <= clientHeight : scrollWidth <= clientWidth;\n    if (state.arrivedState[direction] || isNarrower) {\n      if (!promise.value) {\n        promise.value = Promise.all([\n          onLoadMore(state),\n          new Promise((resolve) => setTimeout(resolve, interval))\n        ]).finally(() => {\n          promise.value = null;\n          nextTick(() => checkAndLoad());\n        });\n      }\n    }\n  }\n  const stop = watch(\n    () => [state.arrivedState[direction], isElementVisible.value],\n    checkAndLoad,\n    { immediate: true }\n  );\n  tryOnUnmounted(stop);\n  return {\n    isLoading,\n    reset() {\n      nextTick(() => checkAndLoad());\n    }\n  };\n}\nvar defaultEvents = [\"mousedown\", \"mouseup\", \"keydown\", \"keyup\"];\nfunction useKeyModifier(modifier, options = {}) {\n  const {\n    events: events2 = defaultEvents,\n    document: document2 = defaultDocument,\n    initial = null\n  } = options;\n  const state = ref(initial);\n  if (document2) {\n    events2.forEach((listenerEvent) => {\n      useEventListener(document2, listenerEvent, (evt) => {\n        if (typeof evt.getModifierState === \"function\")\n          state.value = evt.getModifierState(modifier);\n      }, { passive: true });\n    });\n  }\n  return state;\n}\nfunction useLocalStorage(key, initialValue, options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  return useStorage(key, initialValue, window2 == null ? void 0 : window2.localStorage, options);\n}\nvar DefaultMagicKeysAliasMap = {\n  ctrl: \"control\",\n  command: \"meta\",\n  cmd: \"meta\",\n  option: \"alt\",\n  up: \"arrowup\",\n  down: \"arrowdown\",\n  left: \"arrowleft\",\n  right: \"arrowright\"\n};\nfunction useMagicKeys(options = {}) {\n  const {\n    reactive: useReactive = false,\n    target = defaultWindow,\n    aliasMap = DefaultMagicKeysAliasMap,\n    passive = true,\n    onEventFired = noop\n  } = options;\n  const current = reactive(/* @__PURE__ */ new Set());\n  const obj = {\n    toJSON() {\n      return {};\n    },\n    current\n  };\n  const refs = useReactive ? reactive(obj) : obj;\n  const metaDeps = /* @__PURE__ */ new Set();\n  const usedKeys = /* @__PURE__ */ new Set();\n  function setRefs(key, value) {\n    if (key in refs) {\n      if (useReactive)\n        refs[key] = value;\n      else\n        refs[key].value = value;\n    }\n  }\n  function reset() {\n    current.clear();\n    for (const key of usedKeys)\n      setRefs(key, false);\n  }\n  function updateRefs(e, value) {\n    var _a, _b;\n    const key = (_a = e.key) == null ? void 0 : _a.toLowerCase();\n    const code = (_b = e.code) == null ? void 0 : _b.toLowerCase();\n    const values = [code, key].filter(Boolean);\n    if (key) {\n      if (value)\n        current.add(key);\n      else\n        current.delete(key);\n    }\n    for (const key2 of values) {\n      usedKeys.add(key2);\n      setRefs(key2, value);\n    }\n    if (key === \"meta\" && !value) {\n      metaDeps.forEach((key2) => {\n        current.delete(key2);\n        setRefs(key2, false);\n      });\n      metaDeps.clear();\n    } else if (typeof e.getModifierState === \"function\" && e.getModifierState(\"Meta\") && value) {\n      [...current, ...values].forEach((key2) => metaDeps.add(key2));\n    }\n  }\n  useEventListener(target, \"keydown\", (e) => {\n    updateRefs(e, true);\n    return onEventFired(e);\n  }, { passive });\n  useEventListener(target, \"keyup\", (e) => {\n    updateRefs(e, false);\n    return onEventFired(e);\n  }, { passive });\n  useEventListener(\"blur\", reset, { passive });\n  useEventListener(\"focus\", reset, { passive });\n  const proxy = new Proxy(\n    refs,\n    {\n      get(target2, prop, rec) {\n        if (typeof prop !== \"string\")\n          return Reflect.get(target2, prop, rec);\n        prop = prop.toLowerCase();\n        if (prop in aliasMap)\n          prop = aliasMap[prop];\n        if (!(prop in refs)) {\n          if (/[+_-]/.test(prop)) {\n            const keys2 = prop.split(/[+_-]/g).map((i) => i.trim());\n            refs[prop] = computed(() => keys2.map((key) => toValue(proxy[key])).every(Boolean));\n          } else {\n            refs[prop] = ref(false);\n          }\n        }\n        const r = Reflect.get(target2, prop, rec);\n        return useReactive ? toValue(r) : r;\n      }\n    }\n  );\n  return proxy;\n}\nfunction usingElRef(source, cb) {\n  if (toValue(source))\n    cb(toValue(source));\n}\nfunction timeRangeToArray(timeRanges) {\n  let ranges = [];\n  for (let i = 0; i < timeRanges.length; ++i)\n    ranges = [...ranges, [timeRanges.start(i), timeRanges.end(i)]];\n  return ranges;\n}\nfunction tracksToArray(tracks) {\n  return Array.from(tracks).map(({ label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType }, id) => ({ id, label, kind, language, mode, activeCues, cues, inBandMetadataTrackDispatchType }));\n}\nvar defaultOptions = {\n  src: \"\",\n  tracks: []\n};\nfunction useMediaControls(target, options = {}) {\n  target = toRef2(target);\n  options = {\n    ...defaultOptions,\n    ...options\n  };\n  const {\n    document: document2 = defaultDocument\n  } = options;\n  const listenerOptions = { passive: true };\n  const currentTime = ref(0);\n  const duration = ref(0);\n  const seeking = ref(false);\n  const volume = ref(1);\n  const waiting = ref(false);\n  const ended = ref(false);\n  const playing = ref(false);\n  const rate = ref(1);\n  const stalled = ref(false);\n  const buffered = ref([]);\n  const tracks = ref([]);\n  const selectedTrack = ref(-1);\n  const isPictureInPicture = ref(false);\n  const muted = ref(false);\n  const supportsPictureInPicture = document2 && \"pictureInPictureEnabled\" in document2;\n  const sourceErrorEvent = createEventHook();\n  const playbackErrorEvent = createEventHook();\n  const disableTrack = (track) => {\n    usingElRef(target, (el) => {\n      if (track) {\n        const id = typeof track === \"number\" ? track : track.id;\n        el.textTracks[id].mode = \"disabled\";\n      } else {\n        for (let i = 0; i < el.textTracks.length; ++i)\n          el.textTracks[i].mode = \"disabled\";\n      }\n      selectedTrack.value = -1;\n    });\n  };\n  const enableTrack = (track, disableTracks = true) => {\n    usingElRef(target, (el) => {\n      const id = typeof track === \"number\" ? track : track.id;\n      if (disableTracks)\n        disableTrack();\n      el.textTracks[id].mode = \"showing\";\n      selectedTrack.value = id;\n    });\n  };\n  const togglePictureInPicture = () => {\n    return new Promise((resolve, reject) => {\n      usingElRef(target, async (el) => {\n        if (supportsPictureInPicture) {\n          if (!isPictureInPicture.value) {\n            el.requestPictureInPicture().then(resolve).catch(reject);\n          } else {\n            document2.exitPictureInPicture().then(resolve).catch(reject);\n          }\n        }\n      });\n    });\n  };\n  watchEffect(() => {\n    if (!document2)\n      return;\n    const el = toValue(target);\n    if (!el)\n      return;\n    const src = toValue(options.src);\n    let sources = [];\n    if (!src)\n      return;\n    if (typeof src === \"string\")\n      sources = [{ src }];\n    else if (Array.isArray(src))\n      sources = src;\n    else if (isObject(src))\n      sources = [src];\n    el.querySelectorAll(\"source\").forEach((e) => {\n      e.remove();\n    });\n    sources.forEach(({ src: src2, type, media }) => {\n      const source = document2.createElement(\"source\");\n      source.setAttribute(\"src\", src2);\n      source.setAttribute(\"type\", type || \"\");\n      source.setAttribute(\"media\", media || \"\");\n      useEventListener(source, \"error\", sourceErrorEvent.trigger, listenerOptions);\n      el.appendChild(source);\n    });\n    el.load();\n  });\n  watch([target, volume], () => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    el.volume = volume.value;\n  });\n  watch([target, muted], () => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    el.muted = muted.value;\n  });\n  watch([target, rate], () => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    el.playbackRate = rate.value;\n  });\n  watchEffect(() => {\n    if (!document2)\n      return;\n    const textTracks = toValue(options.tracks);\n    const el = toValue(target);\n    if (!textTracks || !textTracks.length || !el)\n      return;\n    el.querySelectorAll(\"track\").forEach((e) => e.remove());\n    textTracks.forEach(({ default: isDefault, kind, label, src, srcLang }, i) => {\n      const track = document2.createElement(\"track\");\n      track.default = isDefault || false;\n      track.kind = kind;\n      track.label = label;\n      track.src = src;\n      track.srclang = srcLang;\n      if (track.default)\n        selectedTrack.value = i;\n      el.appendChild(track);\n    });\n  });\n  const { ignoreUpdates: ignoreCurrentTimeUpdates } = watchIgnorable(currentTime, (time) => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    el.currentTime = time;\n  });\n  const { ignoreUpdates: ignorePlayingUpdates } = watchIgnorable(playing, (isPlaying) => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    if (isPlaying) {\n      el.play().catch((e) => {\n        playbackErrorEvent.trigger(e);\n        throw e;\n      });\n    } else {\n      el.pause();\n    }\n  });\n  useEventListener(\n    target,\n    \"timeupdate\",\n    () => ignoreCurrentTimeUpdates(() => currentTime.value = toValue(target).currentTime),\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"durationchange\",\n    () => duration.value = toValue(target).duration,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"progress\",\n    () => buffered.value = timeRangeToArray(toValue(target).buffered),\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"seeking\",\n    () => seeking.value = true,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"seeked\",\n    () => seeking.value = false,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    [\"waiting\", \"loadstart\"],\n    () => {\n      waiting.value = true;\n      ignorePlayingUpdates(() => playing.value = false);\n    },\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"loadeddata\",\n    () => waiting.value = false,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"playing\",\n    () => {\n      waiting.value = false;\n      ended.value = false;\n      ignorePlayingUpdates(() => playing.value = true);\n    },\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"ratechange\",\n    () => rate.value = toValue(target).playbackRate,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"stalled\",\n    () => stalled.value = true,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"ended\",\n    () => ended.value = true,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"pause\",\n    () => ignorePlayingUpdates(() => playing.value = false),\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"play\",\n    () => ignorePlayingUpdates(() => playing.value = true),\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"enterpictureinpicture\",\n    () => isPictureInPicture.value = true,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"leavepictureinpicture\",\n    () => isPictureInPicture.value = false,\n    listenerOptions\n  );\n  useEventListener(\n    target,\n    \"volumechange\",\n    () => {\n      const el = toValue(target);\n      if (!el)\n        return;\n      volume.value = el.volume;\n      muted.value = el.muted;\n    },\n    listenerOptions\n  );\n  const listeners = [];\n  const stop = watch([target], () => {\n    const el = toValue(target);\n    if (!el)\n      return;\n    stop();\n    listeners[0] = useEventListener(el.textTracks, \"addtrack\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n    listeners[1] = useEventListener(el.textTracks, \"removetrack\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n    listeners[2] = useEventListener(el.textTracks, \"change\", () => tracks.value = tracksToArray(el.textTracks), listenerOptions);\n  });\n  tryOnScopeDispose(() => listeners.forEach((listener) => listener()));\n  return {\n    currentTime,\n    duration,\n    waiting,\n    seeking,\n    ended,\n    stalled,\n    buffered,\n    playing,\n    rate,\n    // Volume\n    volume,\n    muted,\n    // Tracks\n    tracks,\n    selectedTrack,\n    enableTrack,\n    disableTrack,\n    // Picture in Picture\n    supportsPictureInPicture,\n    togglePictureInPicture,\n    isPictureInPicture,\n    // Events\n    onSourceError: sourceErrorEvent.on,\n    onPlaybackError: playbackErrorEvent.on\n  };\n}\nfunction useMemoize(resolver, options) {\n  const initCache = () => {\n    if (options == null ? void 0 : options.cache)\n      return shallowReactive(options.cache);\n    return shallowReactive(/* @__PURE__ */ new Map());\n  };\n  const cache = initCache();\n  const generateKey = (...args) => (options == null ? void 0 : options.getKey) ? options.getKey(...args) : JSON.stringify(args);\n  const _loadData = (key, ...args) => {\n    cache.set(key, resolver(...args));\n    return cache.get(key);\n  };\n  const loadData = (...args) => _loadData(generateKey(...args), ...args);\n  const deleteData = (...args) => {\n    cache.delete(generateKey(...args));\n  };\n  const clearData = () => {\n    cache.clear();\n  };\n  const memoized = (...args) => {\n    const key = generateKey(...args);\n    if (cache.has(key))\n      return cache.get(key);\n    return _loadData(key, ...args);\n  };\n  memoized.load = loadData;\n  memoized.delete = deleteData;\n  memoized.clear = clearData;\n  memoized.generateKey = generateKey;\n  memoized.cache = cache;\n  return memoized;\n}\nfunction useMemory(options = {}) {\n  const memory = ref();\n  const isSupported = useSupported(() => typeof performance !== \"undefined\" && \"memory\" in performance);\n  if (isSupported.value) {\n    const { interval = 1e3 } = options;\n    useIntervalFn(() => {\n      memory.value = performance.memory;\n    }, interval, { immediate: options.immediate, immediateCallback: options.immediateCallback });\n  }\n  return { isSupported, memory };\n}\nvar UseMouseBuiltinExtractors = {\n  page: (event) => [event.pageX, event.pageY],\n  client: (event) => [event.clientX, event.clientY],\n  screen: (event) => [event.screenX, event.screenY],\n  movement: (event) => event instanceof Touch ? null : [event.movementX, event.movementY]\n};\nfunction useMouse(options = {}) {\n  const {\n    type = \"page\",\n    touch = true,\n    resetOnTouchEnds = false,\n    initialValue = { x: 0, y: 0 },\n    window: window2 = defaultWindow,\n    target = window2,\n    scroll = true,\n    eventFilter\n  } = options;\n  let _prevMouseEvent = null;\n  let _prevScrollX = 0;\n  let _prevScrollY = 0;\n  const x = ref(initialValue.x);\n  const y = ref(initialValue.y);\n  const sourceType = ref(null);\n  const extractor = typeof type === \"function\" ? type : UseMouseBuiltinExtractors[type];\n  const mouseHandler = (event) => {\n    const result = extractor(event);\n    _prevMouseEvent = event;\n    if (result) {\n      [x.value, y.value] = result;\n      sourceType.value = \"mouse\";\n    }\n    if (window2) {\n      _prevScrollX = window2.scrollX;\n      _prevScrollY = window2.scrollY;\n    }\n  };\n  const touchHandler = (event) => {\n    if (event.touches.length > 0) {\n      const result = extractor(event.touches[0]);\n      if (result) {\n        [x.value, y.value] = result;\n        sourceType.value = \"touch\";\n      }\n    }\n  };\n  const scrollHandler = () => {\n    if (!_prevMouseEvent || !window2)\n      return;\n    const pos = extractor(_prevMouseEvent);\n    if (_prevMouseEvent instanceof MouseEvent && pos) {\n      x.value = pos[0] + window2.scrollX - _prevScrollX;\n      y.value = pos[1] + window2.scrollY - _prevScrollY;\n    }\n  };\n  const reset = () => {\n    x.value = initialValue.x;\n    y.value = initialValue.y;\n  };\n  const mouseHandlerWrapper = eventFilter ? (event) => eventFilter(() => mouseHandler(event), {}) : (event) => mouseHandler(event);\n  const touchHandlerWrapper = eventFilter ? (event) => eventFilter(() => touchHandler(event), {}) : (event) => touchHandler(event);\n  const scrollHandlerWrapper = eventFilter ? () => eventFilter(() => scrollHandler(), {}) : () => scrollHandler();\n  if (target) {\n    const listenerOptions = { passive: true };\n    useEventListener(target, [\"mousemove\", \"dragover\"], mouseHandlerWrapper, listenerOptions);\n    if (touch && type !== \"movement\") {\n      useEventListener(target, [\"touchstart\", \"touchmove\"], touchHandlerWrapper, listenerOptions);\n      if (resetOnTouchEnds)\n        useEventListener(target, \"touchend\", reset, listenerOptions);\n    }\n    if (scroll && type === \"page\")\n      useEventListener(window2, \"scroll\", scrollHandlerWrapper, listenerOptions);\n  }\n  return {\n    x,\n    y,\n    sourceType\n  };\n}\nfunction useMouseInElement(target, options = {}) {\n  const {\n    handleOutside = true,\n    window: window2 = defaultWindow\n  } = options;\n  const type = options.type || \"page\";\n  const { x, y, sourceType } = useMouse(options);\n  const targetRef = ref(target != null ? target : window2 == null ? void 0 : window2.document.body);\n  const elementX = ref(0);\n  const elementY = ref(0);\n  const elementPositionX = ref(0);\n  const elementPositionY = ref(0);\n  const elementHeight = ref(0);\n  const elementWidth = ref(0);\n  const isOutside = ref(true);\n  let stop = () => {\n  };\n  if (window2) {\n    stop = watch(\n      [targetRef, x, y],\n      () => {\n        const el = unrefElement(targetRef);\n        if (!el || !(el instanceof Element))\n          return;\n        const {\n          left,\n          top,\n          width,\n          height\n        } = el.getBoundingClientRect();\n        elementPositionX.value = left + (type === \"page\" ? window2.pageXOffset : 0);\n        elementPositionY.value = top + (type === \"page\" ? window2.pageYOffset : 0);\n        elementHeight.value = height;\n        elementWidth.value = width;\n        const elX = x.value - elementPositionX.value;\n        const elY = y.value - elementPositionY.value;\n        isOutside.value = width === 0 || height === 0 || elX < 0 || elY < 0 || elX > width || elY > height;\n        if (handleOutside || !isOutside.value) {\n          elementX.value = elX;\n          elementY.value = elY;\n        }\n      },\n      { immediate: true }\n    );\n    useEventListener(\n      document,\n      \"mouseleave\",\n      () => isOutside.value = true,\n      { passive: true }\n    );\n  }\n  return {\n    x,\n    y,\n    sourceType,\n    elementX,\n    elementY,\n    elementPositionX,\n    elementPositionY,\n    elementHeight,\n    elementWidth,\n    isOutside,\n    stop\n  };\n}\nfunction useMousePressed(options = {}) {\n  const {\n    touch = true,\n    drag = true,\n    capture = false,\n    initialValue = false,\n    window: window2 = defaultWindow\n  } = options;\n  const pressed = ref(initialValue);\n  const sourceType = ref(null);\n  if (!window2) {\n    return {\n      pressed,\n      sourceType\n    };\n  }\n  const onPressed = (srcType) => (event) => {\n    var _a;\n    pressed.value = true;\n    sourceType.value = srcType;\n    (_a = options.onPressed) == null ? void 0 : _a.call(options, event);\n  };\n  const onReleased = (event) => {\n    var _a;\n    pressed.value = false;\n    sourceType.value = null;\n    (_a = options.onReleased) == null ? void 0 : _a.call(options, event);\n  };\n  const target = computed(() => unrefElement(options.target) || window2);\n  const listenerOptions = { passive: true, capture };\n  useEventListener(target, \"mousedown\", onPressed(\"mouse\"), listenerOptions);\n  useEventListener(window2, \"mouseleave\", onReleased, listenerOptions);\n  useEventListener(window2, \"mouseup\", onReleased, listenerOptions);\n  if (drag) {\n    useEventListener(target, \"dragstart\", onPressed(\"mouse\"), listenerOptions);\n    useEventListener(window2, \"drop\", onReleased, listenerOptions);\n    useEventListener(window2, \"dragend\", onReleased, listenerOptions);\n  }\n  if (touch) {\n    useEventListener(target, \"touchstart\", onPressed(\"touch\"), listenerOptions);\n    useEventListener(window2, \"touchend\", onReleased, listenerOptions);\n    useEventListener(window2, \"touchcancel\", onReleased, listenerOptions);\n  }\n  return {\n    pressed,\n    sourceType\n  };\n}\nfunction useNavigatorLanguage(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const navigator2 = window2 == null ? void 0 : window2.navigator;\n  const isSupported = useSupported(() => navigator2 && \"language\" in navigator2);\n  const language = ref(navigator2 == null ? void 0 : navigator2.language);\n  useEventListener(window2, \"languagechange\", () => {\n    if (navigator2)\n      language.value = navigator2.language;\n  }, { passive: true });\n  return {\n    isSupported,\n    language\n  };\n}\nfunction useNetwork(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const navigator2 = window2 == null ? void 0 : window2.navigator;\n  const isSupported = useSupported(() => navigator2 && \"connection\" in navigator2);\n  const isOnline = ref(true);\n  const saveData = ref(false);\n  const offlineAt = ref(void 0);\n  const onlineAt = ref(void 0);\n  const downlink = ref(void 0);\n  const downlinkMax = ref(void 0);\n  const rtt = ref(void 0);\n  const effectiveType = ref(void 0);\n  const type = ref(\"unknown\");\n  const connection = isSupported.value && navigator2.connection;\n  function updateNetworkInformation() {\n    if (!navigator2)\n      return;\n    isOnline.value = navigator2.onLine;\n    offlineAt.value = isOnline.value ? void 0 : Date.now();\n    onlineAt.value = isOnline.value ? Date.now() : void 0;\n    if (connection) {\n      downlink.value = connection.downlink;\n      downlinkMax.value = connection.downlinkMax;\n      effectiveType.value = connection.effectiveType;\n      rtt.value = connection.rtt;\n      saveData.value = connection.saveData;\n      type.value = connection.type;\n    }\n  }\n  const listenerOptions = { passive: true };\n  if (window2) {\n    useEventListener(window2, \"offline\", () => {\n      isOnline.value = false;\n      offlineAt.value = Date.now();\n    }, listenerOptions);\n    useEventListener(window2, \"online\", () => {\n      isOnline.value = true;\n      onlineAt.value = Date.now();\n    }, listenerOptions);\n  }\n  if (connection)\n    useEventListener(connection, \"change\", updateNetworkInformation, listenerOptions);\n  updateNetworkInformation();\n  return {\n    isSupported,\n    isOnline: readonly(isOnline),\n    saveData: readonly(saveData),\n    offlineAt: readonly(offlineAt),\n    onlineAt: readonly(onlineAt),\n    downlink: readonly(downlink),\n    downlinkMax: readonly(downlinkMax),\n    effectiveType: readonly(effectiveType),\n    rtt: readonly(rtt),\n    type: readonly(type)\n  };\n}\nfunction useNow(options = {}) {\n  const {\n    controls: exposeControls = false,\n    interval = \"requestAnimationFrame\"\n  } = options;\n  const now2 = ref(/* @__PURE__ */ new Date());\n  const update = () => now2.value = /* @__PURE__ */ new Date();\n  const controls = interval === \"requestAnimationFrame\" ? useRafFn(update, { immediate: true }) : useIntervalFn(update, interval, { immediate: true });\n  if (exposeControls) {\n    return {\n      now: now2,\n      ...controls\n    };\n  } else {\n    return now2;\n  }\n}\nfunction useObjectUrl(object) {\n  const url = ref();\n  const release = () => {\n    if (url.value)\n      URL.revokeObjectURL(url.value);\n    url.value = void 0;\n  };\n  watch(\n    () => toValue(object),\n    (newObject) => {\n      release();\n      if (newObject)\n        url.value = URL.createObjectURL(newObject);\n    },\n    { immediate: true }\n  );\n  tryOnScopeDispose(release);\n  return readonly(url);\n}\nfunction useClamp(value, min, max) {\n  if (typeof value === \"function\" || isReadonly(value))\n    return computed(() => clamp(toValue(value), toValue(min), toValue(max)));\n  const _value = ref(value);\n  return computed({\n    get() {\n      return _value.value = clamp(_value.value, toValue(min), toValue(max));\n    },\n    set(value2) {\n      _value.value = clamp(value2, toValue(min), toValue(max));\n    }\n  });\n}\nfunction useOffsetPagination(options) {\n  const {\n    total = Number.POSITIVE_INFINITY,\n    pageSize = 10,\n    page = 1,\n    onPageChange = noop,\n    onPageSizeChange = noop,\n    onPageCountChange = noop\n  } = options;\n  const currentPageSize = useClamp(pageSize, 1, Number.POSITIVE_INFINITY);\n  const pageCount = computed(() => Math.max(\n    1,\n    Math.ceil(toValue(total) / toValue(currentPageSize))\n  ));\n  const currentPage = useClamp(page, 1, pageCount);\n  const isFirstPage = computed(() => currentPage.value === 1);\n  const isLastPage = computed(() => currentPage.value === pageCount.value);\n  if (isRef(page)) {\n    syncRef(page, currentPage, {\n      direction: isReadonly(page) ? \"ltr\" : \"both\"\n    });\n  }\n  if (isRef(pageSize)) {\n    syncRef(pageSize, currentPageSize, {\n      direction: isReadonly(pageSize) ? \"ltr\" : \"both\"\n    });\n  }\n  function prev() {\n    currentPage.value--;\n  }\n  function next() {\n    currentPage.value++;\n  }\n  const returnValue = {\n    currentPage,\n    currentPageSize,\n    pageCount,\n    isFirstPage,\n    isLastPage,\n    prev,\n    next\n  };\n  watch(currentPage, () => {\n    onPageChange(reactive(returnValue));\n  });\n  watch(currentPageSize, () => {\n    onPageSizeChange(reactive(returnValue));\n  });\n  watch(pageCount, () => {\n    onPageCountChange(reactive(returnValue));\n  });\n  return returnValue;\n}\nfunction useOnline(options = {}) {\n  const { isOnline } = useNetwork(options);\n  return isOnline;\n}\nfunction usePageLeave(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  const isLeft = ref(false);\n  const handler = (event) => {\n    if (!window2)\n      return;\n    event = event || window2.event;\n    const from = event.relatedTarget || event.toElement;\n    isLeft.value = !from;\n  };\n  if (window2) {\n    const listenerOptions = { passive: true };\n    useEventListener(window2, \"mouseout\", handler, listenerOptions);\n    useEventListener(window2.document, \"mouseleave\", handler, listenerOptions);\n    useEventListener(window2.document, \"mouseenter\", handler, listenerOptions);\n  }\n  return isLeft;\n}\nfunction useScreenOrientation(options = {}) {\n  const {\n    window: window2 = defaultWindow\n  } = options;\n  const isSupported = useSupported(() => window2 && \"screen\" in window2 && \"orientation\" in window2.screen);\n  const screenOrientation = isSupported.value ? window2.screen.orientation : {};\n  const orientation = ref(screenOrientation.type);\n  const angle = ref(screenOrientation.angle || 0);\n  if (isSupported.value) {\n    useEventListener(window2, \"orientationchange\", () => {\n      orientation.value = screenOrientation.type;\n      angle.value = screenOrientation.angle;\n    }, { passive: true });\n  }\n  const lockOrientation = (type) => {\n    if (isSupported.value && typeof screenOrientation.lock === \"function\")\n      return screenOrientation.lock(type);\n    return Promise.reject(new Error(\"Not supported\"));\n  };\n  const unlockOrientation = () => {\n    if (isSupported.value && typeof screenOrientation.unlock === \"function\")\n      screenOrientation.unlock();\n  };\n  return {\n    isSupported,\n    orientation,\n    angle,\n    lockOrientation,\n    unlockOrientation\n  };\n}\nfunction useParallax(target, options = {}) {\n  const {\n    deviceOrientationTiltAdjust = (i) => i,\n    deviceOrientationRollAdjust = (i) => i,\n    mouseTiltAdjust = (i) => i,\n    mouseRollAdjust = (i) => i,\n    window: window2 = defaultWindow\n  } = options;\n  const orientation = reactive(useDeviceOrientation({ window: window2 }));\n  const screenOrientation = reactive(useScreenOrientation({ window: window2 }));\n  const {\n    elementX: x,\n    elementY: y,\n    elementWidth: width,\n    elementHeight: height\n  } = useMouseInElement(target, { handleOutside: false, window: window2 });\n  const source = computed(() => {\n    if (orientation.isSupported && (orientation.alpha != null && orientation.alpha !== 0 || orientation.gamma != null && orientation.gamma !== 0)) {\n      return \"deviceOrientation\";\n    }\n    return \"mouse\";\n  });\n  const roll = computed(() => {\n    if (source.value === \"deviceOrientation\") {\n      let value;\n      switch (screenOrientation.orientation) {\n        case \"landscape-primary\":\n          value = orientation.gamma / 90;\n          break;\n        case \"landscape-secondary\":\n          value = -orientation.gamma / 90;\n          break;\n        case \"portrait-primary\":\n          value = -orientation.beta / 90;\n          break;\n        case \"portrait-secondary\":\n          value = orientation.beta / 90;\n          break;\n        default:\n          value = -orientation.beta / 90;\n      }\n      return deviceOrientationRollAdjust(value);\n    } else {\n      const value = -(y.value - height.value / 2) / height.value;\n      return mouseRollAdjust(value);\n    }\n  });\n  const tilt = computed(() => {\n    if (source.value === \"deviceOrientation\") {\n      let value;\n      switch (screenOrientation.orientation) {\n        case \"landscape-primary\":\n          value = orientation.beta / 90;\n          break;\n        case \"landscape-secondary\":\n          value = -orientation.beta / 90;\n          break;\n        case \"portrait-primary\":\n          value = orientation.gamma / 90;\n          break;\n        case \"portrait-secondary\":\n          value = -orientation.gamma / 90;\n          break;\n        default:\n          value = orientation.gamma / 90;\n      }\n      return deviceOrientationTiltAdjust(value);\n    } else {\n      const value = (x.value - width.value / 2) / width.value;\n      return mouseTiltAdjust(value);\n    }\n  });\n  return { roll, tilt, source };\n}\nfunction useParentElement(element = useCurrentElement()) {\n  const parentElement = shallowRef();\n  const update = () => {\n    const el = unrefElement(element);\n    if (el)\n      parentElement.value = el.parentElement;\n  };\n  tryOnMounted(update);\n  watch(() => toValue(element), update);\n  return parentElement;\n}\nfunction usePerformanceObserver(options, callback) {\n  const {\n    window: window2 = defaultWindow,\n    immediate = true,\n    ...performanceOptions\n  } = options;\n  const isSupported = useSupported(() => window2 && \"PerformanceObserver\" in window2);\n  let observer;\n  const stop = () => {\n    observer == null ? void 0 : observer.disconnect();\n  };\n  const start = () => {\n    if (isSupported.value) {\n      stop();\n      observer = new PerformanceObserver(callback);\n      observer.observe(performanceOptions);\n    }\n  };\n  tryOnScopeDispose(stop);\n  if (immediate)\n    start();\n  return {\n    isSupported,\n    start,\n    stop\n  };\n}\nvar defaultState = {\n  x: 0,\n  y: 0,\n  pointerId: 0,\n  pressure: 0,\n  tiltX: 0,\n  tiltY: 0,\n  width: 0,\n  height: 0,\n  twist: 0,\n  pointerType: null\n};\nvar keys = Object.keys(defaultState);\nfunction usePointer(options = {}) {\n  const {\n    target = defaultWindow\n  } = options;\n  const isInside = ref(false);\n  const state = ref(options.initialValue || {});\n  Object.assign(state.value, defaultState, state.value);\n  const handler = (event) => {\n    isInside.value = true;\n    if (options.pointerTypes && !options.pointerTypes.includes(event.pointerType))\n      return;\n    state.value = objectPick(event, keys, false);\n  };\n  if (target) {\n    const listenerOptions = { passive: true };\n    useEventListener(target, [\"pointerdown\", \"pointermove\", \"pointerup\"], handler, listenerOptions);\n    useEventListener(target, \"pointerleave\", () => isInside.value = false, listenerOptions);\n  }\n  return {\n    ...toRefs2(state),\n    isInside\n  };\n}\nfunction usePointerLock(target, options = {}) {\n  const { document: document2 = defaultDocument } = options;\n  const isSupported = useSupported(() => document2 && \"pointerLockElement\" in document2);\n  const element = ref();\n  const triggerElement = ref();\n  let targetElement;\n  if (isSupported.value) {\n    const listenerOptions = { passive: true };\n    useEventListener(document2, \"pointerlockchange\", () => {\n      var _a;\n      const currentElement = (_a = document2.pointerLockElement) != null ? _a : element.value;\n      if (targetElement && currentElement === targetElement) {\n        element.value = document2.pointerLockElement;\n        if (!element.value)\n          targetElement = triggerElement.value = null;\n      }\n    }, listenerOptions);\n    useEventListener(document2, \"pointerlockerror\", () => {\n      var _a;\n      const currentElement = (_a = document2.pointerLockElement) != null ? _a : element.value;\n      if (targetElement && currentElement === targetElement) {\n        const action = document2.pointerLockElement ? \"release\" : \"acquire\";\n        throw new Error(`Failed to ${action} pointer lock.`);\n      }\n    }, listenerOptions);\n  }\n  async function lock(e) {\n    var _a;\n    if (!isSupported.value)\n      throw new Error(\"Pointer Lock API is not supported by your browser.\");\n    triggerElement.value = e instanceof Event ? e.currentTarget : null;\n    targetElement = e instanceof Event ? (_a = unrefElement(target)) != null ? _a : triggerElement.value : unrefElement(e);\n    if (!targetElement)\n      throw new Error(\"Target element undefined.\");\n    targetElement.requestPointerLock();\n    return await until(element).toBe(targetElement);\n  }\n  async function unlock() {\n    if (!element.value)\n      return false;\n    document2.exitPointerLock();\n    await until(element).toBeNull();\n    return true;\n  }\n  return {\n    isSupported,\n    element,\n    triggerElement,\n    lock,\n    unlock\n  };\n}\nfunction usePointerSwipe(target, options = {}) {\n  const targetRef = toRef2(target);\n  const {\n    threshold = 50,\n    onSwipe,\n    onSwipeEnd,\n    onSwipeStart,\n    disableTextSelect = false\n  } = options;\n  const posStart = reactive({ x: 0, y: 0 });\n  const updatePosStart = (x, y) => {\n    posStart.x = x;\n    posStart.y = y;\n  };\n  const posEnd = reactive({ x: 0, y: 0 });\n  const updatePosEnd = (x, y) => {\n    posEnd.x = x;\n    posEnd.y = y;\n  };\n  const distanceX = computed(() => posStart.x - posEnd.x);\n  const distanceY = computed(() => posStart.y - posEnd.y);\n  const { max, abs } = Math;\n  const isThresholdExceeded = computed(() => max(abs(distanceX.value), abs(distanceY.value)) >= threshold);\n  const isSwiping = ref(false);\n  const isPointerDown = ref(false);\n  const direction = computed(() => {\n    if (!isThresholdExceeded.value)\n      return \"none\";\n    if (abs(distanceX.value) > abs(distanceY.value)) {\n      return distanceX.value > 0 ? \"left\" : \"right\";\n    } else {\n      return distanceY.value > 0 ? \"up\" : \"down\";\n    }\n  });\n  const eventIsAllowed = (e) => {\n    var _a, _b, _c;\n    const isReleasingButton = e.buttons === 0;\n    const isPrimaryButton = e.buttons === 1;\n    return (_c = (_b = (_a = options.pointerTypes) == null ? void 0 : _a.includes(e.pointerType)) != null ? _b : isReleasingButton || isPrimaryButton) != null ? _c : true;\n  };\n  const listenerOptions = { passive: true };\n  const stops = [\n    useEventListener(target, \"pointerdown\", (e) => {\n      if (!eventIsAllowed(e))\n        return;\n      isPointerDown.value = true;\n      const eventTarget = e.target;\n      eventTarget == null ? void 0 : eventTarget.setPointerCapture(e.pointerId);\n      const { clientX: x, clientY: y } = e;\n      updatePosStart(x, y);\n      updatePosEnd(x, y);\n      onSwipeStart == null ? void 0 : onSwipeStart(e);\n    }, listenerOptions),\n    useEventListener(target, \"pointermove\", (e) => {\n      if (!eventIsAllowed(e))\n        return;\n      if (!isPointerDown.value)\n        return;\n      const { clientX: x, clientY: y } = e;\n      updatePosEnd(x, y);\n      if (!isSwiping.value && isThresholdExceeded.value)\n        isSwiping.value = true;\n      if (isSwiping.value)\n        onSwipe == null ? void 0 : onSwipe(e);\n    }, listenerOptions),\n    useEventListener(target, \"pointerup\", (e) => {\n      if (!eventIsAllowed(e))\n        return;\n      if (isSwiping.value)\n        onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value);\n      isPointerDown.value = false;\n      isSwiping.value = false;\n    }, listenerOptions)\n  ];\n  tryOnMounted(() => {\n    var _a, _b, _c, _d, _e, _f, _g, _h;\n    (_b = (_a = targetRef.value) == null ? void 0 : _a.style) == null ? void 0 : _b.setProperty(\"touch-action\", \"none\");\n    if (disableTextSelect) {\n      (_d = (_c = targetRef.value) == null ? void 0 : _c.style) == null ? void 0 : _d.setProperty(\"-webkit-user-select\", \"none\");\n      (_f = (_e = targetRef.value) == null ? void 0 : _e.style) == null ? void 0 : _f.setProperty(\"-ms-user-select\", \"none\");\n      (_h = (_g = targetRef.value) == null ? void 0 : _g.style) == null ? void 0 : _h.setProperty(\"user-select\", \"none\");\n    }\n  });\n  const stop = () => stops.forEach((s) => s());\n  return {\n    isSwiping: readonly(isSwiping),\n    direction: readonly(direction),\n    posStart: readonly(posStart),\n    posEnd: readonly(posEnd),\n    distanceX,\n    distanceY,\n    stop\n  };\n}\nfunction usePreferredColorScheme(options) {\n  const isLight = useMediaQuery(\"(prefers-color-scheme: light)\", options);\n  const isDark = useMediaQuery(\"(prefers-color-scheme: dark)\", options);\n  return computed(() => {\n    if (isDark.value)\n      return \"dark\";\n    if (isLight.value)\n      return \"light\";\n    return \"no-preference\";\n  });\n}\nfunction usePreferredContrast(options) {\n  const isMore = useMediaQuery(\"(prefers-contrast: more)\", options);\n  const isLess = useMediaQuery(\"(prefers-contrast: less)\", options);\n  const isCustom = useMediaQuery(\"(prefers-contrast: custom)\", options);\n  return computed(() => {\n    if (isMore.value)\n      return \"more\";\n    if (isLess.value)\n      return \"less\";\n    if (isCustom.value)\n      return \"custom\";\n    return \"no-preference\";\n  });\n}\nfunction usePreferredLanguages(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  if (!window2)\n    return ref([\"en\"]);\n  const navigator2 = window2.navigator;\n  const value = ref(navigator2.languages);\n  useEventListener(window2, \"languagechange\", () => {\n    value.value = navigator2.languages;\n  }, { passive: true });\n  return value;\n}\nfunction usePreferredReducedMotion(options) {\n  const isReduced = useMediaQuery(\"(prefers-reduced-motion: reduce)\", options);\n  return computed(() => {\n    if (isReduced.value)\n      return \"reduce\";\n    return \"no-preference\";\n  });\n}\nfunction usePreferredReducedTransparency(options) {\n  const isReduced = useMediaQuery(\"(prefers-reduced-transparency: reduce)\", options);\n  return computed(() => {\n    if (isReduced.value)\n      return \"reduce\";\n    return \"no-preference\";\n  });\n}\nfunction usePrevious(value, initialValue) {\n  const previous = shallowRef(initialValue);\n  watch(\n    toRef2(value),\n    (_, oldValue) => {\n      previous.value = oldValue;\n    },\n    { flush: \"sync\" }\n  );\n  return readonly(previous);\n}\nvar topVarName = \"--vueuse-safe-area-top\";\nvar rightVarName = \"--vueuse-safe-area-right\";\nvar bottomVarName = \"--vueuse-safe-area-bottom\";\nvar leftVarName = \"--vueuse-safe-area-left\";\nfunction useScreenSafeArea() {\n  const top = ref(\"\");\n  const right = ref(\"\");\n  const bottom = ref(\"\");\n  const left = ref(\"\");\n  if (isClient) {\n    const topCssVar = useCssVar(topVarName);\n    const rightCssVar = useCssVar(rightVarName);\n    const bottomCssVar = useCssVar(bottomVarName);\n    const leftCssVar = useCssVar(leftVarName);\n    topCssVar.value = \"env(safe-area-inset-top, 0px)\";\n    rightCssVar.value = \"env(safe-area-inset-right, 0px)\";\n    bottomCssVar.value = \"env(safe-area-inset-bottom, 0px)\";\n    leftCssVar.value = \"env(safe-area-inset-left, 0px)\";\n    update();\n    useEventListener(\"resize\", useDebounceFn(update), { passive: true });\n  }\n  function update() {\n    top.value = getValue(topVarName);\n    right.value = getValue(rightVarName);\n    bottom.value = getValue(bottomVarName);\n    left.value = getValue(leftVarName);\n  }\n  return {\n    top,\n    right,\n    bottom,\n    left,\n    update\n  };\n}\nfunction getValue(position) {\n  return getComputedStyle(document.documentElement).getPropertyValue(position);\n}\nfunction useScriptTag(src, onLoaded = noop, options = {}) {\n  const {\n    immediate = true,\n    manual = false,\n    type = \"text/javascript\",\n    async = true,\n    crossOrigin,\n    referrerPolicy,\n    noModule,\n    defer,\n    document: document2 = defaultDocument,\n    attrs = {}\n  } = options;\n  const scriptTag = ref(null);\n  let _promise = null;\n  const loadScript = (waitForScriptLoad) => new Promise((resolve, reject) => {\n    const resolveWithElement = (el2) => {\n      scriptTag.value = el2;\n      resolve(el2);\n      return el2;\n    };\n    if (!document2) {\n      resolve(false);\n      return;\n    }\n    let shouldAppend = false;\n    let el = document2.querySelector(`script[src=\"${toValue(src)}\"]`);\n    if (!el) {\n      el = document2.createElement(\"script\");\n      el.type = type;\n      el.async = async;\n      el.src = toValue(src);\n      if (defer)\n        el.defer = defer;\n      if (crossOrigin)\n        el.crossOrigin = crossOrigin;\n      if (noModule)\n        el.noModule = noModule;\n      if (referrerPolicy)\n        el.referrerPolicy = referrerPolicy;\n      Object.entries(attrs).forEach(([name, value]) => el == null ? void 0 : el.setAttribute(name, value));\n      shouldAppend = true;\n    } else if (el.hasAttribute(\"data-loaded\")) {\n      resolveWithElement(el);\n    }\n    const listenerOptions = {\n      passive: true\n    };\n    useEventListener(el, \"error\", (event) => reject(event), listenerOptions);\n    useEventListener(el, \"abort\", (event) => reject(event), listenerOptions);\n    useEventListener(el, \"load\", () => {\n      el.setAttribute(\"data-loaded\", \"true\");\n      onLoaded(el);\n      resolveWithElement(el);\n    }, listenerOptions);\n    if (shouldAppend)\n      el = document2.head.appendChild(el);\n    if (!waitForScriptLoad)\n      resolveWithElement(el);\n  });\n  const load = (waitForScriptLoad = true) => {\n    if (!_promise)\n      _promise = loadScript(waitForScriptLoad);\n    return _promise;\n  };\n  const unload = () => {\n    if (!document2)\n      return;\n    _promise = null;\n    if (scriptTag.value)\n      scriptTag.value = null;\n    const el = document2.querySelector(`script[src=\"${toValue(src)}\"]`);\n    if (el)\n      document2.head.removeChild(el);\n  };\n  if (immediate && !manual)\n    tryOnMounted(load);\n  if (!manual)\n    tryOnUnmounted(unload);\n  return { scriptTag, load, unload };\n}\nfunction checkOverflowScroll(ele) {\n  const style = window.getComputedStyle(ele);\n  if (style.overflowX === \"scroll\" || style.overflowY === \"scroll\" || style.overflowX === \"auto\" && ele.clientWidth < ele.scrollWidth || style.overflowY === \"auto\" && ele.clientHeight < ele.scrollHeight) {\n    return true;\n  } else {\n    const parent = ele.parentNode;\n    if (!parent || parent.tagName === \"BODY\")\n      return false;\n    return checkOverflowScroll(parent);\n  }\n}\nfunction preventDefault(rawEvent) {\n  const e = rawEvent || window.event;\n  const _target = e.target;\n  if (checkOverflowScroll(_target))\n    return false;\n  if (e.touches.length > 1)\n    return true;\n  if (e.preventDefault)\n    e.preventDefault();\n  return false;\n}\nvar elInitialOverflow = /* @__PURE__ */ new WeakMap();\nfunction useScrollLock(element, initialState = false) {\n  const isLocked = ref(initialState);\n  let stopTouchMoveListener = null;\n  let initialOverflow = \"\";\n  watch(toRef2(element), (el) => {\n    const target = resolveElement(toValue(el));\n    if (target) {\n      const ele = target;\n      if (!elInitialOverflow.get(ele))\n        elInitialOverflow.set(ele, ele.style.overflow);\n      if (ele.style.overflow !== \"hidden\")\n        initialOverflow = ele.style.overflow;\n      if (ele.style.overflow === \"hidden\")\n        return isLocked.value = true;\n      if (isLocked.value)\n        return ele.style.overflow = \"hidden\";\n    }\n  }, {\n    immediate: true\n  });\n  const lock = () => {\n    const el = resolveElement(toValue(element));\n    if (!el || isLocked.value)\n      return;\n    if (isIOS) {\n      stopTouchMoveListener = useEventListener(\n        el,\n        \"touchmove\",\n        (e) => {\n          preventDefault(e);\n        },\n        { passive: false }\n      );\n    }\n    el.style.overflow = \"hidden\";\n    isLocked.value = true;\n  };\n  const unlock = () => {\n    const el = resolveElement(toValue(element));\n    if (!el || !isLocked.value)\n      return;\n    if (isIOS)\n      stopTouchMoveListener == null ? void 0 : stopTouchMoveListener();\n    el.style.overflow = initialOverflow;\n    elInitialOverflow.delete(el);\n    isLocked.value = false;\n  };\n  tryOnScopeDispose(unlock);\n  return computed({\n    get() {\n      return isLocked.value;\n    },\n    set(v) {\n      if (v)\n        lock();\n      else unlock();\n    }\n  });\n}\nfunction useSessionStorage(key, initialValue, options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  return useStorage(key, initialValue, window2 == null ? void 0 : window2.sessionStorage, options);\n}\nfunction useShare(shareOptions = {}, options = {}) {\n  const { navigator: navigator2 = defaultNavigator } = options;\n  const _navigator = navigator2;\n  const isSupported = useSupported(() => _navigator && \"canShare\" in _navigator);\n  const share = async (overrideOptions = {}) => {\n    if (isSupported.value) {\n      const data = {\n        ...toValue(shareOptions),\n        ...toValue(overrideOptions)\n      };\n      let granted = true;\n      if (data.files && _navigator.canShare)\n        granted = _navigator.canShare({ files: data.files });\n      if (granted)\n        return _navigator.share(data);\n    }\n  };\n  return {\n    isSupported,\n    share\n  };\n}\nvar defaultSortFn = (source, compareFn) => source.sort(compareFn);\nvar defaultCompare = (a, b) => a - b;\nfunction useSorted(...args) {\n  var _a, _b, _c, _d;\n  const [source] = args;\n  let compareFn = defaultCompare;\n  let options = {};\n  if (args.length === 2) {\n    if (typeof args[1] === \"object\") {\n      options = args[1];\n      compareFn = (_a = options.compareFn) != null ? _a : defaultCompare;\n    } else {\n      compareFn = (_b = args[1]) != null ? _b : defaultCompare;\n    }\n  } else if (args.length > 2) {\n    compareFn = (_c = args[1]) != null ? _c : defaultCompare;\n    options = (_d = args[2]) != null ? _d : {};\n  }\n  const {\n    dirty = false,\n    sortFn = defaultSortFn\n  } = options;\n  if (!dirty)\n    return computed(() => sortFn([...toValue(source)], compareFn));\n  watchEffect(() => {\n    const result = sortFn(toValue(source), compareFn);\n    if (isRef(source))\n      source.value = result;\n    else\n      source.splice(0, source.length, ...result);\n  });\n  return source;\n}\nfunction useSpeechRecognition(options = {}) {\n  const {\n    interimResults = true,\n    continuous = true,\n    maxAlternatives = 1,\n    window: window2 = defaultWindow\n  } = options;\n  const lang = toRef2(options.lang || \"en-US\");\n  const isListening = ref(false);\n  const isFinal = ref(false);\n  const result = ref(\"\");\n  const error = shallowRef(void 0);\n  let recognition;\n  const start = () => {\n    if (!isListening.value)\n      recognition == null ? void 0 : recognition.start();\n  };\n  const stop = () => {\n    if (isListening.value)\n      recognition == null ? void 0 : recognition.stop();\n  };\n  const toggle = (value = !isListening.value) => {\n    if (value) {\n      start();\n    } else {\n      stop();\n    }\n  };\n  const SpeechRecognition = window2 && (window2.SpeechRecognition || window2.webkitSpeechRecognition);\n  const isSupported = useSupported(() => SpeechRecognition);\n  if (isSupported.value) {\n    recognition = new SpeechRecognition();\n    recognition.continuous = continuous;\n    recognition.interimResults = interimResults;\n    recognition.lang = toValue(lang);\n    recognition.maxAlternatives = maxAlternatives;\n    recognition.onstart = () => {\n      isListening.value = true;\n      isFinal.value = false;\n    };\n    watch(lang, (lang2) => {\n      if (recognition && !isListening.value)\n        recognition.lang = lang2;\n    });\n    recognition.onresult = (event) => {\n      const currentResult = event.results[event.resultIndex];\n      const { transcript } = currentResult[0];\n      isFinal.value = currentResult.isFinal;\n      result.value = transcript;\n      error.value = void 0;\n    };\n    recognition.onerror = (event) => {\n      error.value = event;\n    };\n    recognition.onend = () => {\n      isListening.value = false;\n      recognition.lang = toValue(lang);\n    };\n    watch(isListening, () => {\n      if (isListening.value)\n        recognition.start();\n      else\n        recognition.stop();\n    });\n  }\n  tryOnScopeDispose(() => {\n    stop();\n  });\n  return {\n    isSupported,\n    isListening,\n    isFinal,\n    recognition,\n    result,\n    error,\n    toggle,\n    start,\n    stop\n  };\n}\nfunction useSpeechSynthesis(text, options = {}) {\n  const {\n    pitch = 1,\n    rate = 1,\n    volume = 1,\n    window: window2 = defaultWindow\n  } = options;\n  const synth = window2 && window2.speechSynthesis;\n  const isSupported = useSupported(() => synth);\n  const isPlaying = ref(false);\n  const status = ref(\"init\");\n  const spokenText = toRef2(text || \"\");\n  const lang = toRef2(options.lang || \"en-US\");\n  const error = shallowRef(void 0);\n  const toggle = (value = !isPlaying.value) => {\n    isPlaying.value = value;\n  };\n  const bindEventsForUtterance = (utterance2) => {\n    utterance2.lang = toValue(lang);\n    utterance2.voice = toValue(options.voice) || null;\n    utterance2.pitch = toValue(pitch);\n    utterance2.rate = toValue(rate);\n    utterance2.volume = volume;\n    utterance2.onstart = () => {\n      isPlaying.value = true;\n      status.value = \"play\";\n    };\n    utterance2.onpause = () => {\n      isPlaying.value = false;\n      status.value = \"pause\";\n    };\n    utterance2.onresume = () => {\n      isPlaying.value = true;\n      status.value = \"play\";\n    };\n    utterance2.onend = () => {\n      isPlaying.value = false;\n      status.value = \"end\";\n    };\n    utterance2.onerror = (event) => {\n      error.value = event;\n    };\n  };\n  const utterance = computed(() => {\n    isPlaying.value = false;\n    status.value = \"init\";\n    const newUtterance = new SpeechSynthesisUtterance(spokenText.value);\n    bindEventsForUtterance(newUtterance);\n    return newUtterance;\n  });\n  const speak = () => {\n    synth.cancel();\n    if (utterance)\n      synth.speak(utterance.value);\n  };\n  const stop = () => {\n    synth.cancel();\n    isPlaying.value = false;\n  };\n  if (isSupported.value) {\n    bindEventsForUtterance(utterance.value);\n    watch(lang, (lang2) => {\n      if (utterance.value && !isPlaying.value)\n        utterance.value.lang = lang2;\n    });\n    if (options.voice) {\n      watch(options.voice, () => {\n        synth.cancel();\n      });\n    }\n    watch(isPlaying, () => {\n      if (isPlaying.value)\n        synth.resume();\n      else\n        synth.pause();\n    });\n  }\n  tryOnScopeDispose(() => {\n    isPlaying.value = false;\n  });\n  return {\n    isSupported,\n    isPlaying,\n    status,\n    utterance,\n    error,\n    stop,\n    toggle,\n    speak\n  };\n}\nfunction useStepper(steps, initialStep) {\n  const stepsRef = ref(steps);\n  const stepNames = computed(() => Array.isArray(stepsRef.value) ? stepsRef.value : Object.keys(stepsRef.value));\n  const index = ref(stepNames.value.indexOf(initialStep != null ? initialStep : stepNames.value[0]));\n  const current = computed(() => at(index.value));\n  const isFirst = computed(() => index.value === 0);\n  const isLast = computed(() => index.value === stepNames.value.length - 1);\n  const next = computed(() => stepNames.value[index.value + 1]);\n  const previous = computed(() => stepNames.value[index.value - 1]);\n  function at(index2) {\n    if (Array.isArray(stepsRef.value))\n      return stepsRef.value[index2];\n    return stepsRef.value[stepNames.value[index2]];\n  }\n  function get2(step) {\n    if (!stepNames.value.includes(step))\n      return;\n    return at(stepNames.value.indexOf(step));\n  }\n  function goTo(step) {\n    if (stepNames.value.includes(step))\n      index.value = stepNames.value.indexOf(step);\n  }\n  function goToNext() {\n    if (isLast.value)\n      return;\n    index.value++;\n  }\n  function goToPrevious() {\n    if (isFirst.value)\n      return;\n    index.value--;\n  }\n  function goBackTo(step) {\n    if (isAfter(step))\n      goTo(step);\n  }\n  function isNext(step) {\n    return stepNames.value.indexOf(step) === index.value + 1;\n  }\n  function isPrevious(step) {\n    return stepNames.value.indexOf(step) === index.value - 1;\n  }\n  function isCurrent(step) {\n    return stepNames.value.indexOf(step) === index.value;\n  }\n  function isBefore(step) {\n    return index.value < stepNames.value.indexOf(step);\n  }\n  function isAfter(step) {\n    return index.value > stepNames.value.indexOf(step);\n  }\n  return {\n    steps: stepsRef,\n    stepNames,\n    index,\n    current,\n    next,\n    previous,\n    isFirst,\n    isLast,\n    at,\n    get: get2,\n    goTo,\n    goToNext,\n    goToPrevious,\n    goBackTo,\n    isNext,\n    isPrevious,\n    isCurrent,\n    isBefore,\n    isAfter\n  };\n}\nfunction useStorageAsync(key, initialValue, storage, options = {}) {\n  var _a;\n  const {\n    flush = \"pre\",\n    deep = true,\n    listenToStorageChanges = true,\n    writeDefaults = true,\n    mergeDefaults = false,\n    shallow,\n    window: window2 = defaultWindow,\n    eventFilter,\n    onError = (e) => {\n      console.error(e);\n    }\n  } = options;\n  const rawInit = toValue(initialValue);\n  const type = guessSerializerType(rawInit);\n  const data = (shallow ? shallowRef : ref)(toValue(initialValue));\n  const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];\n  if (!storage) {\n    try {\n      storage = getSSRHandler(\"getDefaultStorageAsync\", () => {\n        var _a2;\n        return (_a2 = defaultWindow) == null ? void 0 : _a2.localStorage;\n      })();\n    } catch (e) {\n      onError(e);\n    }\n  }\n  async function read(event) {\n    if (!storage || event && event.key !== key)\n      return;\n    try {\n      const rawValue = event ? event.newValue : await storage.getItem(key);\n      if (rawValue == null) {\n        data.value = rawInit;\n        if (writeDefaults && rawInit !== null)\n          await storage.setItem(key, await serializer.write(rawInit));\n      } else if (mergeDefaults) {\n        const value = await serializer.read(rawValue);\n        if (typeof mergeDefaults === \"function\")\n          data.value = mergeDefaults(value, rawInit);\n        else if (type === \"object\" && !Array.isArray(value))\n          data.value = { ...rawInit, ...value };\n        else data.value = value;\n      } else {\n        data.value = await serializer.read(rawValue);\n      }\n    } catch (e) {\n      onError(e);\n    }\n  }\n  read();\n  if (window2 && listenToStorageChanges)\n    useEventListener(window2, \"storage\", (e) => Promise.resolve().then(() => read(e)), { passive: true });\n  if (storage) {\n    watchWithFilter(\n      data,\n      async () => {\n        try {\n          if (data.value == null)\n            await storage.removeItem(key);\n          else\n            await storage.setItem(key, await serializer.write(data.value));\n        } catch (e) {\n          onError(e);\n        }\n      },\n      {\n        flush,\n        deep,\n        eventFilter\n      }\n    );\n  }\n  return data;\n}\nvar _id = 0;\nfunction useStyleTag(css, options = {}) {\n  const isLoaded = ref(false);\n  const {\n    document: document2 = defaultDocument,\n    immediate = true,\n    manual = false,\n    id = `vueuse_styletag_${++_id}`\n  } = options;\n  const cssRef = ref(css);\n  let stop = () => {\n  };\n  const load = () => {\n    if (!document2)\n      return;\n    const el = document2.getElementById(id) || document2.createElement(\"style\");\n    if (!el.isConnected) {\n      el.id = id;\n      if (options.media)\n        el.media = options.media;\n      document2.head.appendChild(el);\n    }\n    if (isLoaded.value)\n      return;\n    stop = watch(\n      cssRef,\n      (value) => {\n        el.textContent = value;\n      },\n      { immediate: true }\n    );\n    isLoaded.value = true;\n  };\n  const unload = () => {\n    if (!document2 || !isLoaded.value)\n      return;\n    stop();\n    document2.head.removeChild(document2.getElementById(id));\n    isLoaded.value = false;\n  };\n  if (immediate && !manual)\n    tryOnMounted(load);\n  if (!manual)\n    tryOnScopeDispose(unload);\n  return {\n    id,\n    css: cssRef,\n    unload,\n    load,\n    isLoaded: readonly(isLoaded)\n  };\n}\nfunction useSwipe(target, options = {}) {\n  const {\n    threshold = 50,\n    onSwipe,\n    onSwipeEnd,\n    onSwipeStart,\n    passive = true\n  } = options;\n  const coordsStart = reactive({ x: 0, y: 0 });\n  const coordsEnd = reactive({ x: 0, y: 0 });\n  const diffX = computed(() => coordsStart.x - coordsEnd.x);\n  const diffY = computed(() => coordsStart.y - coordsEnd.y);\n  const { max, abs } = Math;\n  const isThresholdExceeded = computed(() => max(abs(diffX.value), abs(diffY.value)) >= threshold);\n  const isSwiping = ref(false);\n  const direction = computed(() => {\n    if (!isThresholdExceeded.value)\n      return \"none\";\n    if (abs(diffX.value) > abs(diffY.value)) {\n      return diffX.value > 0 ? \"left\" : \"right\";\n    } else {\n      return diffY.value > 0 ? \"up\" : \"down\";\n    }\n  });\n  const getTouchEventCoords = (e) => [e.touches[0].clientX, e.touches[0].clientY];\n  const updateCoordsStart = (x, y) => {\n    coordsStart.x = x;\n    coordsStart.y = y;\n  };\n  const updateCoordsEnd = (x, y) => {\n    coordsEnd.x = x;\n    coordsEnd.y = y;\n  };\n  const listenerOptions = { passive, capture: !passive };\n  const onTouchEnd = (e) => {\n    if (isSwiping.value)\n      onSwipeEnd == null ? void 0 : onSwipeEnd(e, direction.value);\n    isSwiping.value = false;\n  };\n  const stops = [\n    useEventListener(target, \"touchstart\", (e) => {\n      if (e.touches.length !== 1)\n        return;\n      const [x, y] = getTouchEventCoords(e);\n      updateCoordsStart(x, y);\n      updateCoordsEnd(x, y);\n      onSwipeStart == null ? void 0 : onSwipeStart(e);\n    }, listenerOptions),\n    useEventListener(target, \"touchmove\", (e) => {\n      if (e.touches.length !== 1)\n        return;\n      const [x, y] = getTouchEventCoords(e);\n      updateCoordsEnd(x, y);\n      if (listenerOptions.capture && !listenerOptions.passive && Math.abs(diffX.value) > Math.abs(diffY.value))\n        e.preventDefault();\n      if (!isSwiping.value && isThresholdExceeded.value)\n        isSwiping.value = true;\n      if (isSwiping.value)\n        onSwipe == null ? void 0 : onSwipe(e);\n    }, listenerOptions),\n    useEventListener(target, [\"touchend\", \"touchcancel\"], onTouchEnd, listenerOptions)\n  ];\n  const stop = () => stops.forEach((s) => s());\n  return {\n    isSwiping,\n    direction,\n    coordsStart,\n    coordsEnd,\n    lengthX: diffX,\n    lengthY: diffY,\n    stop,\n    // TODO: Remove in the next major version\n    isPassiveEventSupported: true\n  };\n}\nfunction useTemplateRefsList() {\n  const refs = ref([]);\n  refs.value.set = (el) => {\n    if (el)\n      refs.value.push(el);\n  };\n  onBeforeUpdate(() => {\n    refs.value.length = 0;\n  });\n  return refs;\n}\nfunction useTextDirection(options = {}) {\n  const {\n    document: document2 = defaultDocument,\n    selector = \"html\",\n    observe = false,\n    initialValue = \"ltr\"\n  } = options;\n  function getValue2() {\n    var _a, _b;\n    return (_b = (_a = document2 == null ? void 0 : document2.querySelector(selector)) == null ? void 0 : _a.getAttribute(\"dir\")) != null ? _b : initialValue;\n  }\n  const dir = ref(getValue2());\n  tryOnMounted(() => dir.value = getValue2());\n  if (observe && document2) {\n    useMutationObserver(\n      document2.querySelector(selector),\n      () => dir.value = getValue2(),\n      { attributes: true }\n    );\n  }\n  return computed({\n    get() {\n      return dir.value;\n    },\n    set(v) {\n      var _a, _b;\n      dir.value = v;\n      if (!document2)\n        return;\n      if (dir.value)\n        (_a = document2.querySelector(selector)) == null ? void 0 : _a.setAttribute(\"dir\", dir.value);\n      else\n        (_b = document2.querySelector(selector)) == null ? void 0 : _b.removeAttribute(\"dir\");\n    }\n  });\n}\nfunction getRangesFromSelection(selection) {\n  var _a;\n  const rangeCount = (_a = selection.rangeCount) != null ? _a : 0;\n  return Array.from({ length: rangeCount }, (_, i) => selection.getRangeAt(i));\n}\nfunction useTextSelection(options = {}) {\n  const {\n    window: window2 = defaultWindow\n  } = options;\n  const selection = ref(null);\n  const text = computed(() => {\n    var _a, _b;\n    return (_b = (_a = selection.value) == null ? void 0 : _a.toString()) != null ? _b : \"\";\n  });\n  const ranges = computed(() => selection.value ? getRangesFromSelection(selection.value) : []);\n  const rects = computed(() => ranges.value.map((range) => range.getBoundingClientRect()));\n  function onSelectionChange() {\n    selection.value = null;\n    if (window2)\n      selection.value = window2.getSelection();\n  }\n  if (window2)\n    useEventListener(window2.document, \"selectionchange\", onSelectionChange, { passive: true });\n  return {\n    text,\n    rects,\n    ranges,\n    selection\n  };\n}\nfunction useTextareaAutosize(options) {\n  var _a, _b;\n  const textarea = toRef2(options == null ? void 0 : options.element);\n  const input = toRef2((_a = options == null ? void 0 : options.input) != null ? _a : \"\");\n  const styleProp = (_b = options == null ? void 0 : options.styleProp) != null ? _b : \"height\";\n  const textareaScrollHeight = ref(1);\n  const textareaOldWidth = ref(0);\n  function triggerResize() {\n    var _a2;\n    if (!textarea.value)\n      return;\n    let height = \"\";\n    textarea.value.style[styleProp] = \"1px\";\n    textareaScrollHeight.value = (_a2 = textarea.value) == null ? void 0 : _a2.scrollHeight;\n    const _styleTarget = toValue(options == null ? void 0 : options.styleTarget);\n    if (_styleTarget)\n      _styleTarget.style[styleProp] = `${textareaScrollHeight.value}px`;\n    else\n      height = `${textareaScrollHeight.value}px`;\n    textarea.value.style[styleProp] = height;\n  }\n  watch([input, textarea], () => nextTick(triggerResize), { immediate: true });\n  watch(textareaScrollHeight, () => {\n    var _a2;\n    return (_a2 = options == null ? void 0 : options.onResize) == null ? void 0 : _a2.call(options);\n  });\n  useResizeObserver(textarea, ([{ contentRect }]) => {\n    if (textareaOldWidth.value === contentRect.width)\n      return;\n    textareaOldWidth.value = contentRect.width;\n    triggerResize();\n  });\n  if (options == null ? void 0 : options.watch)\n    watch(options.watch, triggerResize, { immediate: true, deep: true });\n  return {\n    textarea,\n    input,\n    triggerResize\n  };\n}\nfunction useThrottledRefHistory(source, options = {}) {\n  const { throttle = 200, trailing = true } = options;\n  const filter = throttleFilter(throttle, trailing);\n  const history = useRefHistory(source, { ...options, eventFilter: filter });\n  return {\n    ...history\n  };\n}\nvar DEFAULT_UNITS = [\n  { max: 6e4, value: 1e3, name: \"second\" },\n  { max: 276e4, value: 6e4, name: \"minute\" },\n  { max: 72e6, value: 36e5, name: \"hour\" },\n  { max: 5184e5, value: 864e5, name: \"day\" },\n  { max: 24192e5, value: 6048e5, name: \"week\" },\n  { max: 28512e6, value: 2592e6, name: \"month\" },\n  { max: Number.POSITIVE_INFINITY, value: 31536e6, name: \"year\" }\n];\nvar DEFAULT_MESSAGES = {\n  justNow: \"just now\",\n  past: (n) => n.match(/\\d/) ? `${n} ago` : n,\n  future: (n) => n.match(/\\d/) ? `in ${n}` : n,\n  month: (n, past) => n === 1 ? past ? \"last month\" : \"next month\" : `${n} month${n > 1 ? \"s\" : \"\"}`,\n  year: (n, past) => n === 1 ? past ? \"last year\" : \"next year\" : `${n} year${n > 1 ? \"s\" : \"\"}`,\n  day: (n, past) => n === 1 ? past ? \"yesterday\" : \"tomorrow\" : `${n} day${n > 1 ? \"s\" : \"\"}`,\n  week: (n, past) => n === 1 ? past ? \"last week\" : \"next week\" : `${n} week${n > 1 ? \"s\" : \"\"}`,\n  hour: (n) => `${n} hour${n > 1 ? \"s\" : \"\"}`,\n  minute: (n) => `${n} minute${n > 1 ? \"s\" : \"\"}`,\n  second: (n) => `${n} second${n > 1 ? \"s\" : \"\"}`,\n  invalid: \"\"\n};\nfunction DEFAULT_FORMATTER(date) {\n  return date.toISOString().slice(0, 10);\n}\nfunction useTimeAgo(time, options = {}) {\n  const {\n    controls: exposeControls = false,\n    updateInterval = 3e4\n  } = options;\n  const { now: now2, ...controls } = useNow({ interval: updateInterval, controls: true });\n  const timeAgo = computed(() => formatTimeAgo(new Date(toValue(time)), options, toValue(now2)));\n  if (exposeControls) {\n    return {\n      timeAgo,\n      ...controls\n    };\n  } else {\n    return timeAgo;\n  }\n}\nfunction formatTimeAgo(from, options = {}, now2 = Date.now()) {\n  var _a;\n  const {\n    max,\n    messages = DEFAULT_MESSAGES,\n    fullDateFormatter = DEFAULT_FORMATTER,\n    units = DEFAULT_UNITS,\n    showSecond = false,\n    rounding = \"round\"\n  } = options;\n  const roundFn = typeof rounding === \"number\" ? (n) => +n.toFixed(rounding) : Math[rounding];\n  const diff = +now2 - +from;\n  const absDiff = Math.abs(diff);\n  function getValue2(diff2, unit) {\n    return roundFn(Math.abs(diff2) / unit.value);\n  }\n  function format(diff2, unit) {\n    const val = getValue2(diff2, unit);\n    const past = diff2 > 0;\n    const str = applyFormat(unit.name, val, past);\n    return applyFormat(past ? \"past\" : \"future\", str, past);\n  }\n  function applyFormat(name, val, isPast) {\n    const formatter = messages[name];\n    if (typeof formatter === \"function\")\n      return formatter(val, isPast);\n    return formatter.replace(\"{0}\", val.toString());\n  }\n  if (absDiff < 6e4 && !showSecond)\n    return messages.justNow;\n  if (typeof max === \"number\" && absDiff > max)\n    return fullDateFormatter(new Date(from));\n  if (typeof max === \"string\") {\n    const unitMax = (_a = units.find((i) => i.name === max)) == null ? void 0 : _a.max;\n    if (unitMax && absDiff > unitMax)\n      return fullDateFormatter(new Date(from));\n  }\n  for (const [idx, unit] of units.entries()) {\n    const val = getValue2(diff, unit);\n    if (val <= 0 && units[idx - 1])\n      return format(diff, units[idx - 1]);\n    if (absDiff < unit.max)\n      return format(diff, unit);\n  }\n  return messages.invalid;\n}\nfunction useTimeoutPoll(fn, interval, options = {}) {\n  const {\n    immediate = true\n  } = options;\n  const { start } = useTimeoutFn(loop, interval, { immediate: false });\n  const isActive = ref(false);\n  async function loop() {\n    if (!isActive.value)\n      return;\n    await fn();\n    start();\n  }\n  function resume() {\n    if (!isActive.value) {\n      isActive.value = true;\n      loop();\n    }\n  }\n  function pause() {\n    isActive.value = false;\n  }\n  if (immediate && isClient)\n    resume();\n  tryOnScopeDispose(pause);\n  return {\n    isActive,\n    pause,\n    resume\n  };\n}\nfunction useTimestamp(options = {}) {\n  const {\n    controls: exposeControls = false,\n    offset = 0,\n    immediate = true,\n    interval = \"requestAnimationFrame\",\n    callback\n  } = options;\n  const ts = ref(timestamp() + offset);\n  const update = () => ts.value = timestamp() + offset;\n  const cb = callback ? () => {\n    update();\n    callback(ts.value);\n  } : update;\n  const controls = interval === \"requestAnimationFrame\" ? useRafFn(cb, { immediate }) : useIntervalFn(cb, interval, { immediate });\n  if (exposeControls) {\n    return {\n      timestamp: ts,\n      ...controls\n    };\n  } else {\n    return ts;\n  }\n}\nfunction useTitle(newTitle = null, options = {}) {\n  var _a, _b, _c;\n  const {\n    document: document2 = defaultDocument,\n    restoreOnUnmount = (t) => t\n  } = options;\n  const originalTitle = (_a = document2 == null ? void 0 : document2.title) != null ? _a : \"\";\n  const title = toRef2((_b = newTitle != null ? newTitle : document2 == null ? void 0 : document2.title) != null ? _b : null);\n  const isReadonly2 = !!(newTitle && typeof newTitle === \"function\");\n  function format(t) {\n    if (!(\"titleTemplate\" in options))\n      return t;\n    const template = options.titleTemplate || \"%s\";\n    return typeof template === \"function\" ? template(t) : toValue(template).replace(/%s/g, t);\n  }\n  watch(\n    title,\n    (newValue, oldValue) => {\n      if (newValue !== oldValue && document2)\n        document2.title = format(newValue != null ? newValue : \"\");\n    },\n    { immediate: true }\n  );\n  if (options.observe && !options.titleTemplate && document2 && !isReadonly2) {\n    useMutationObserver(\n      (_c = document2.head) == null ? void 0 : _c.querySelector(\"title\"),\n      () => {\n        if (document2 && document2.title !== title.value)\n          title.value = format(document2.title);\n      },\n      { childList: true }\n    );\n  }\n  tryOnBeforeUnmount(() => {\n    if (restoreOnUnmount) {\n      const restoredTitle = restoreOnUnmount(originalTitle, title.value || \"\");\n      if (restoredTitle != null && document2)\n        document2.title = restoredTitle;\n    }\n  });\n  return title;\n}\nvar _TransitionPresets = {\n  easeInSine: [0.12, 0, 0.39, 0],\n  easeOutSine: [0.61, 1, 0.88, 1],\n  easeInOutSine: [0.37, 0, 0.63, 1],\n  easeInQuad: [0.11, 0, 0.5, 0],\n  easeOutQuad: [0.5, 1, 0.89, 1],\n  easeInOutQuad: [0.45, 0, 0.55, 1],\n  easeInCubic: [0.32, 0, 0.67, 0],\n  easeOutCubic: [0.33, 1, 0.68, 1],\n  easeInOutCubic: [0.65, 0, 0.35, 1],\n  easeInQuart: [0.5, 0, 0.75, 0],\n  easeOutQuart: [0.25, 1, 0.5, 1],\n  easeInOutQuart: [0.76, 0, 0.24, 1],\n  easeInQuint: [0.64, 0, 0.78, 0],\n  easeOutQuint: [0.22, 1, 0.36, 1],\n  easeInOutQuint: [0.83, 0, 0.17, 1],\n  easeInExpo: [0.7, 0, 0.84, 0],\n  easeOutExpo: [0.16, 1, 0.3, 1],\n  easeInOutExpo: [0.87, 0, 0.13, 1],\n  easeInCirc: [0.55, 0, 1, 0.45],\n  easeOutCirc: [0, 0.55, 0.45, 1],\n  easeInOutCirc: [0.85, 0, 0.15, 1],\n  easeInBack: [0.36, 0, 0.66, -0.56],\n  easeOutBack: [0.34, 1.56, 0.64, 1],\n  easeInOutBack: [0.68, -0.6, 0.32, 1.6]\n};\nvar TransitionPresets = Object.assign({}, { linear: identity }, _TransitionPresets);\nfunction createEasingFunction([p0, p1, p2, p3]) {\n  const a = (a1, a2) => 1 - 3 * a2 + 3 * a1;\n  const b = (a1, a2) => 3 * a2 - 6 * a1;\n  const c = (a1) => 3 * a1;\n  const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;\n  const getSlope = (t, a1, a2) => 3 * a(a1, a2) * t * t + 2 * b(a1, a2) * t + c(a1);\n  const getTforX = (x) => {\n    let aGuessT = x;\n    for (let i = 0; i < 4; ++i) {\n      const currentSlope = getSlope(aGuessT, p0, p2);\n      if (currentSlope === 0)\n        return aGuessT;\n      const currentX = calcBezier(aGuessT, p0, p2) - x;\n      aGuessT -= currentX / currentSlope;\n    }\n    return aGuessT;\n  };\n  return (x) => p0 === p1 && p2 === p3 ? x : calcBezier(getTforX(x), p1, p3);\n}\nfunction lerp(a, b, alpha) {\n  return a + alpha * (b - a);\n}\nfunction toVec(t) {\n  return (typeof t === \"number\" ? [t] : t) || [];\n}\nfunction executeTransition(source, from, to, options = {}) {\n  var _a, _b;\n  const fromVal = toValue(from);\n  const toVal = toValue(to);\n  const v1 = toVec(fromVal);\n  const v2 = toVec(toVal);\n  const duration = (_a = toValue(options.duration)) != null ? _a : 1e3;\n  const startedAt = Date.now();\n  const endAt = Date.now() + duration;\n  const trans = typeof options.transition === \"function\" ? options.transition : (_b = toValue(options.transition)) != null ? _b : identity;\n  const ease = typeof trans === \"function\" ? trans : createEasingFunction(trans);\n  return new Promise((resolve) => {\n    source.value = fromVal;\n    const tick = () => {\n      var _a2;\n      if ((_a2 = options.abort) == null ? void 0 : _a2.call(options)) {\n        resolve();\n        return;\n      }\n      const now2 = Date.now();\n      const alpha = ease((now2 - startedAt) / duration);\n      const arr = toVec(source.value).map((n, i) => lerp(v1[i], v2[i], alpha));\n      if (Array.isArray(source.value))\n        source.value = arr.map((n, i) => {\n          var _a3, _b2;\n          return lerp((_a3 = v1[i]) != null ? _a3 : 0, (_b2 = v2[i]) != null ? _b2 : 0, alpha);\n        });\n      else if (typeof source.value === \"number\")\n        source.value = arr[0];\n      if (now2 < endAt) {\n        requestAnimationFrame(tick);\n      } else {\n        source.value = toVal;\n        resolve();\n      }\n    };\n    tick();\n  });\n}\nfunction useTransition(source, options = {}) {\n  let currentId = 0;\n  const sourceVal = () => {\n    const v = toValue(source);\n    return typeof v === \"number\" ? v : v.map(toValue);\n  };\n  const outputRef = ref(sourceVal());\n  watch(sourceVal, async (to) => {\n    var _a, _b;\n    if (toValue(options.disabled))\n      return;\n    const id = ++currentId;\n    if (options.delay)\n      await promiseTimeout(toValue(options.delay));\n    if (id !== currentId)\n      return;\n    const toVal = Array.isArray(to) ? to.map(toValue) : toValue(to);\n    (_a = options.onStarted) == null ? void 0 : _a.call(options);\n    await executeTransition(outputRef, outputRef.value, toVal, {\n      ...options,\n      abort: () => {\n        var _a2;\n        return id !== currentId || ((_a2 = options.abort) == null ? void 0 : _a2.call(options));\n      }\n    });\n    (_b = options.onFinished) == null ? void 0 : _b.call(options);\n  }, { deep: true });\n  watch(() => toValue(options.disabled), (disabled) => {\n    if (disabled) {\n      currentId++;\n      outputRef.value = sourceVal();\n    }\n  });\n  tryOnScopeDispose(() => {\n    currentId++;\n  });\n  return computed(() => toValue(options.disabled) ? sourceVal() : outputRef.value);\n}\nfunction useUrlSearchParams(mode = \"history\", options = {}) {\n  const {\n    initialValue = {},\n    removeNullishValues = true,\n    removeFalsyValues = false,\n    write: enableWrite = true,\n    writeMode = \"replace\",\n    window: window2 = defaultWindow\n  } = options;\n  if (!window2)\n    return reactive(initialValue);\n  const state = reactive({});\n  function getRawParams() {\n    if (mode === \"history\") {\n      return window2.location.search || \"\";\n    } else if (mode === \"hash\") {\n      const hash = window2.location.hash || \"\";\n      const index = hash.indexOf(\"?\");\n      return index > 0 ? hash.slice(index) : \"\";\n    } else {\n      return (window2.location.hash || \"\").replace(/^#/, \"\");\n    }\n  }\n  function constructQuery(params) {\n    const stringified = params.toString();\n    if (mode === \"history\")\n      return `${stringified ? `?${stringified}` : \"\"}${window2.location.hash || \"\"}`;\n    if (mode === \"hash-params\")\n      return `${window2.location.search || \"\"}${stringified ? `#${stringified}` : \"\"}`;\n    const hash = window2.location.hash || \"#\";\n    const index = hash.indexOf(\"?\");\n    if (index > 0)\n      return `${window2.location.search || \"\"}${hash.slice(0, index)}${stringified ? `?${stringified}` : \"\"}`;\n    return `${window2.location.search || \"\"}${hash}${stringified ? `?${stringified}` : \"\"}`;\n  }\n  function read() {\n    return new URLSearchParams(getRawParams());\n  }\n  function updateState(params) {\n    const unusedKeys = new Set(Object.keys(state));\n    for (const key of params.keys()) {\n      const paramsForKey = params.getAll(key);\n      state[key] = paramsForKey.length > 1 ? paramsForKey : params.get(key) || \"\";\n      unusedKeys.delete(key);\n    }\n    Array.from(unusedKeys).forEach((key) => delete state[key]);\n  }\n  const { pause, resume } = watchPausable(\n    state,\n    () => {\n      const params = new URLSearchParams(\"\");\n      Object.keys(state).forEach((key) => {\n        const mapEntry = state[key];\n        if (Array.isArray(mapEntry))\n          mapEntry.forEach((value) => params.append(key, value));\n        else if (removeNullishValues && mapEntry == null)\n          params.delete(key);\n        else if (removeFalsyValues && !mapEntry)\n          params.delete(key);\n        else\n          params.set(key, mapEntry);\n      });\n      write(params, false);\n    },\n    { deep: true }\n  );\n  function write(params, shouldUpdate) {\n    pause();\n    if (shouldUpdate)\n      updateState(params);\n    if (writeMode === \"replace\") {\n      window2.history.replaceState(\n        window2.history.state,\n        window2.document.title,\n        window2.location.pathname + constructQuery(params)\n      );\n    } else {\n      window2.history.pushState(\n        window2.history.state,\n        window2.document.title,\n        window2.location.pathname + constructQuery(params)\n      );\n    }\n    resume();\n  }\n  function onChanged() {\n    if (!enableWrite)\n      return;\n    write(read(), true);\n  }\n  const listenerOptions = { passive: true };\n  useEventListener(window2, \"popstate\", onChanged, listenerOptions);\n  if (mode !== \"history\")\n    useEventListener(window2, \"hashchange\", onChanged, listenerOptions);\n  const initial = read();\n  if (initial.keys().next().value)\n    updateState(initial);\n  else\n    Object.assign(state, initialValue);\n  return state;\n}\nfunction useUserMedia(options = {}) {\n  var _a, _b;\n  const enabled = ref((_a = options.enabled) != null ? _a : false);\n  const autoSwitch = ref((_b = options.autoSwitch) != null ? _b : true);\n  const constraints = ref(options.constraints);\n  const { navigator: navigator2 = defaultNavigator } = options;\n  const isSupported = useSupported(() => {\n    var _a2;\n    return (_a2 = navigator2 == null ? void 0 : navigator2.mediaDevices) == null ? void 0 : _a2.getUserMedia;\n  });\n  const stream = shallowRef();\n  function getDeviceOptions(type) {\n    switch (type) {\n      case \"video\": {\n        if (constraints.value)\n          return constraints.value.video || false;\n        break;\n      }\n      case \"audio\": {\n        if (constraints.value)\n          return constraints.value.audio || false;\n        break;\n      }\n    }\n  }\n  async function _start() {\n    if (!isSupported.value || stream.value)\n      return;\n    stream.value = await navigator2.mediaDevices.getUserMedia({\n      video: getDeviceOptions(\"video\"),\n      audio: getDeviceOptions(\"audio\")\n    });\n    return stream.value;\n  }\n  function _stop() {\n    var _a2;\n    (_a2 = stream.value) == null ? void 0 : _a2.getTracks().forEach((t) => t.stop());\n    stream.value = void 0;\n  }\n  function stop() {\n    _stop();\n    enabled.value = false;\n  }\n  async function start() {\n    await _start();\n    if (stream.value)\n      enabled.value = true;\n    return stream.value;\n  }\n  async function restart() {\n    _stop();\n    return await start();\n  }\n  watch(\n    enabled,\n    (v) => {\n      if (v)\n        _start();\n      else _stop();\n    },\n    { immediate: true }\n  );\n  watch(\n    constraints,\n    () => {\n      if (autoSwitch.value && stream.value)\n        restart();\n    },\n    { immediate: true }\n  );\n  tryOnScopeDispose(() => {\n    stop();\n  });\n  return {\n    isSupported,\n    stream,\n    start,\n    stop,\n    restart,\n    constraints,\n    enabled,\n    autoSwitch\n  };\n}\nfunction useVModel(props, key, emit, options = {}) {\n  var _a, _b, _c;\n  const {\n    clone = false,\n    passive = false,\n    eventName,\n    deep = false,\n    defaultValue,\n    shouldEmit\n  } = options;\n  const vm = getCurrentInstance();\n  const _emit = emit || (vm == null ? void 0 : vm.emit) || ((_a = vm == null ? void 0 : vm.$emit) == null ? void 0 : _a.bind(vm)) || ((_c = (_b = vm == null ? void 0 : vm.proxy) == null ? void 0 : _b.$emit) == null ? void 0 : _c.bind(vm == null ? void 0 : vm.proxy));\n  let event = eventName;\n  if (!key) {\n    key = \"modelValue\";\n  }\n  event = event || `update:${key.toString()}`;\n  const cloneFn = (val) => !clone ? val : typeof clone === \"function\" ? clone(val) : cloneFnJSON(val);\n  const getValue2 = () => isDef(props[key]) ? cloneFn(props[key]) : defaultValue;\n  const triggerEmit = (value) => {\n    if (shouldEmit) {\n      if (shouldEmit(value))\n        _emit(event, value);\n    } else {\n      _emit(event, value);\n    }\n  };\n  if (passive) {\n    const initialValue = getValue2();\n    const proxy = ref(initialValue);\n    let isUpdating = false;\n    watch(\n      () => props[key],\n      (v) => {\n        if (!isUpdating) {\n          isUpdating = true;\n          proxy.value = cloneFn(v);\n          nextTick(() => isUpdating = false);\n        }\n      }\n    );\n    watch(\n      proxy,\n      (v) => {\n        if (!isUpdating && (v !== props[key] || deep))\n          triggerEmit(v);\n      },\n      { deep }\n    );\n    return proxy;\n  } else {\n    return computed({\n      get() {\n        return getValue2();\n      },\n      set(value) {\n        triggerEmit(value);\n      }\n    });\n  }\n}\nfunction useVModels(props, emit, options = {}) {\n  const ret = {};\n  for (const key in props) {\n    ret[key] = useVModel(\n      props,\n      key,\n      emit,\n      options\n    );\n  }\n  return ret;\n}\nfunction useVibrate(options) {\n  const {\n    pattern = [],\n    interval = 0,\n    navigator: navigator2 = defaultNavigator\n  } = options || {};\n  const isSupported = useSupported(() => typeof navigator2 !== \"undefined\" && \"vibrate\" in navigator2);\n  const patternRef = toRef2(pattern);\n  let intervalControls;\n  const vibrate = (pattern2 = patternRef.value) => {\n    if (isSupported.value)\n      navigator2.vibrate(pattern2);\n  };\n  const stop = () => {\n    if (isSupported.value)\n      navigator2.vibrate(0);\n    intervalControls == null ? void 0 : intervalControls.pause();\n  };\n  if (interval > 0) {\n    intervalControls = useIntervalFn(\n      vibrate,\n      interval,\n      {\n        immediate: false,\n        immediateCallback: false\n      }\n    );\n  }\n  return {\n    isSupported,\n    pattern,\n    intervalControls,\n    vibrate,\n    stop\n  };\n}\nfunction useVirtualList(list, options) {\n  const { containerStyle, wrapperProps, scrollTo, calculateRange, currentList, containerRef } = \"itemHeight\" in options ? useVerticalVirtualList(options, list) : useHorizontalVirtualList(options, list);\n  return {\n    list: currentList,\n    scrollTo,\n    containerProps: {\n      ref: containerRef,\n      onScroll: () => {\n        calculateRange();\n      },\n      style: containerStyle\n    },\n    wrapperProps\n  };\n}\nfunction useVirtualListResources(list) {\n  const containerRef = ref(null);\n  const size = useElementSize(containerRef);\n  const currentList = ref([]);\n  const source = shallowRef(list);\n  const state = ref({ start: 0, end: 10 });\n  return { state, source, currentList, size, containerRef };\n}\nfunction createGetViewCapacity(state, source, itemSize) {\n  return (containerSize) => {\n    if (typeof itemSize === \"number\")\n      return Math.ceil(containerSize / itemSize);\n    const { start = 0 } = state.value;\n    let sum = 0;\n    let capacity = 0;\n    for (let i = start; i < source.value.length; i++) {\n      const size = itemSize(i);\n      sum += size;\n      capacity = i;\n      if (sum > containerSize)\n        break;\n    }\n    return capacity - start;\n  };\n}\nfunction createGetOffset(source, itemSize) {\n  return (scrollDirection) => {\n    if (typeof itemSize === \"number\")\n      return Math.floor(scrollDirection / itemSize) + 1;\n    let sum = 0;\n    let offset = 0;\n    for (let i = 0; i < source.value.length; i++) {\n      const size = itemSize(i);\n      sum += size;\n      if (sum >= scrollDirection) {\n        offset = i;\n        break;\n      }\n    }\n    return offset + 1;\n  };\n}\nfunction createCalculateRange(type, overscan, getOffset, getViewCapacity, { containerRef, state, currentList, source }) {\n  return () => {\n    const element = containerRef.value;\n    if (element) {\n      const offset = getOffset(type === \"vertical\" ? element.scrollTop : element.scrollLeft);\n      const viewCapacity = getViewCapacity(type === \"vertical\" ? element.clientHeight : element.clientWidth);\n      const from = offset - overscan;\n      const to = offset + viewCapacity + overscan;\n      state.value = {\n        start: from < 0 ? 0 : from,\n        end: to > source.value.length ? source.value.length : to\n      };\n      currentList.value = source.value.slice(state.value.start, state.value.end).map((ele, index) => ({\n        data: ele,\n        index: index + state.value.start\n      }));\n    }\n  };\n}\nfunction createGetDistance(itemSize, source) {\n  return (index) => {\n    if (typeof itemSize === \"number\") {\n      const size2 = index * itemSize;\n      return size2;\n    }\n    const size = source.value.slice(0, index).reduce((sum, _, i) => sum + itemSize(i), 0);\n    return size;\n  };\n}\nfunction useWatchForSizes(size, list, containerRef, calculateRange) {\n  watch([size.width, size.height, list, containerRef], () => {\n    calculateRange();\n  });\n}\nfunction createComputedTotalSize(itemSize, source) {\n  return computed(() => {\n    if (typeof itemSize === \"number\")\n      return source.value.length * itemSize;\n    return source.value.reduce((sum, _, index) => sum + itemSize(index), 0);\n  });\n}\nvar scrollToDictionaryForElementScrollKey = {\n  horizontal: \"scrollLeft\",\n  vertical: \"scrollTop\"\n};\nfunction createScrollTo(type, calculateRange, getDistance, containerRef) {\n  return (index) => {\n    if (containerRef.value) {\n      containerRef.value[scrollToDictionaryForElementScrollKey[type]] = getDistance(index);\n      calculateRange();\n    }\n  };\n}\nfunction useHorizontalVirtualList(options, list) {\n  const resources = useVirtualListResources(list);\n  const { state, source, currentList, size, containerRef } = resources;\n  const containerStyle = { overflowX: \"auto\" };\n  const { itemWidth, overscan = 5 } = options;\n  const getViewCapacity = createGetViewCapacity(state, source, itemWidth);\n  const getOffset = createGetOffset(source, itemWidth);\n  const calculateRange = createCalculateRange(\"horizontal\", overscan, getOffset, getViewCapacity, resources);\n  const getDistanceLeft = createGetDistance(itemWidth, source);\n  const offsetLeft = computed(() => getDistanceLeft(state.value.start));\n  const totalWidth = createComputedTotalSize(itemWidth, source);\n  useWatchForSizes(size, list, containerRef, calculateRange);\n  const scrollTo = createScrollTo(\"horizontal\", calculateRange, getDistanceLeft, containerRef);\n  const wrapperProps = computed(() => {\n    return {\n      style: {\n        height: \"100%\",\n        width: `${totalWidth.value - offsetLeft.value}px`,\n        marginLeft: `${offsetLeft.value}px`,\n        display: \"flex\"\n      }\n    };\n  });\n  return {\n    scrollTo,\n    calculateRange,\n    wrapperProps,\n    containerStyle,\n    currentList,\n    containerRef\n  };\n}\nfunction useVerticalVirtualList(options, list) {\n  const resources = useVirtualListResources(list);\n  const { state, source, currentList, size, containerRef } = resources;\n  const containerStyle = { overflowY: \"auto\" };\n  const { itemHeight, overscan = 5 } = options;\n  const getViewCapacity = createGetViewCapacity(state, source, itemHeight);\n  const getOffset = createGetOffset(source, itemHeight);\n  const calculateRange = createCalculateRange(\"vertical\", overscan, getOffset, getViewCapacity, resources);\n  const getDistanceTop = createGetDistance(itemHeight, source);\n  const offsetTop = computed(() => getDistanceTop(state.value.start));\n  const totalHeight = createComputedTotalSize(itemHeight, source);\n  useWatchForSizes(size, list, containerRef, calculateRange);\n  const scrollTo = createScrollTo(\"vertical\", calculateRange, getDistanceTop, containerRef);\n  const wrapperProps = computed(() => {\n    return {\n      style: {\n        width: \"100%\",\n        height: `${totalHeight.value - offsetTop.value}px`,\n        marginTop: `${offsetTop.value}px`\n      }\n    };\n  });\n  return {\n    calculateRange,\n    scrollTo,\n    containerStyle,\n    wrapperProps,\n    currentList,\n    containerRef\n  };\n}\nfunction useWakeLock(options = {}) {\n  const {\n    navigator: navigator2 = defaultNavigator,\n    document: document2 = defaultDocument\n  } = options;\n  const requestedType = ref(false);\n  const sentinel = shallowRef(null);\n  const documentVisibility = useDocumentVisibility({ document: document2 });\n  const isSupported = useSupported(() => navigator2 && \"wakeLock\" in navigator2);\n  const isActive = computed(() => !!sentinel.value && documentVisibility.value === \"visible\");\n  if (isSupported.value) {\n    useEventListener(sentinel, \"release\", () => {\n      var _a, _b;\n      requestedType.value = (_b = (_a = sentinel.value) == null ? void 0 : _a.type) != null ? _b : false;\n    }, { passive: true });\n    whenever(\n      () => documentVisibility.value === \"visible\" && (document2 == null ? void 0 : document2.visibilityState) === \"visible\" && requestedType.value,\n      (type) => {\n        requestedType.value = false;\n        forceRequest(type);\n      }\n    );\n  }\n  async function forceRequest(type) {\n    var _a;\n    await ((_a = sentinel.value) == null ? void 0 : _a.release());\n    sentinel.value = isSupported.value ? await navigator2.wakeLock.request(type) : null;\n  }\n  async function request(type) {\n    if (documentVisibility.value === \"visible\")\n      await forceRequest(type);\n    else\n      requestedType.value = type;\n  }\n  async function release() {\n    requestedType.value = false;\n    const s = sentinel.value;\n    sentinel.value = null;\n    await (s == null ? void 0 : s.release());\n  }\n  return {\n    sentinel,\n    isSupported,\n    isActive,\n    request,\n    forceRequest,\n    release\n  };\n}\nfunction useWebNotification(options = {}) {\n  const {\n    window: window2 = defaultWindow,\n    requestPermissions: _requestForPermissions = true\n  } = options;\n  const defaultWebNotificationOptions = options;\n  const isSupported = useSupported(() => {\n    if (!window2 || !(\"Notification\" in window2))\n      return false;\n    if (Notification.permission === \"granted\")\n      return true;\n    try {\n      const notification2 = new Notification(\"\");\n      notification2.onshow = () => {\n        notification2.close();\n      };\n    } catch (e) {\n      if (e.name === \"TypeError\")\n        return false;\n    }\n    return true;\n  });\n  const permissionGranted = ref(isSupported.value && \"permission\" in Notification && Notification.permission === \"granted\");\n  const notification = ref(null);\n  const ensurePermissions = async () => {\n    if (!isSupported.value)\n      return;\n    if (!permissionGranted.value && Notification.permission !== \"denied\") {\n      const result = await Notification.requestPermission();\n      if (result === \"granted\")\n        permissionGranted.value = true;\n    }\n    return permissionGranted.value;\n  };\n  const { on: onClick, trigger: clickTrigger } = createEventHook();\n  const { on: onShow, trigger: showTrigger } = createEventHook();\n  const { on: onError, trigger: errorTrigger } = createEventHook();\n  const { on: onClose, trigger: closeTrigger } = createEventHook();\n  const show = async (overrides) => {\n    if (!isSupported.value || !permissionGranted.value)\n      return;\n    const options2 = Object.assign({}, defaultWebNotificationOptions, overrides);\n    notification.value = new Notification(options2.title || \"\", options2);\n    notification.value.onclick = clickTrigger;\n    notification.value.onshow = showTrigger;\n    notification.value.onerror = errorTrigger;\n    notification.value.onclose = closeTrigger;\n    return notification.value;\n  };\n  const close = () => {\n    if (notification.value)\n      notification.value.close();\n    notification.value = null;\n  };\n  if (_requestForPermissions)\n    tryOnMounted(ensurePermissions);\n  tryOnScopeDispose(close);\n  if (isSupported.value && window2) {\n    const document2 = window2.document;\n    useEventListener(document2, \"visibilitychange\", (e) => {\n      e.preventDefault();\n      if (document2.visibilityState === \"visible\") {\n        close();\n      }\n    });\n  }\n  return {\n    isSupported,\n    notification,\n    ensurePermissions,\n    permissionGranted,\n    show,\n    close,\n    onClick,\n    onShow,\n    onError,\n    onClose\n  };\n}\nvar DEFAULT_PING_MESSAGE = \"ping\";\nfunction resolveNestedOptions(options) {\n  if (options === true)\n    return {};\n  return options;\n}\nfunction useWebSocket(url, options = {}) {\n  const {\n    onConnected,\n    onDisconnected,\n    onError,\n    onMessage,\n    immediate = true,\n    autoConnect = true,\n    autoClose = true,\n    protocols = []\n  } = options;\n  const data = ref(null);\n  const status = ref(\"CLOSED\");\n  const wsRef = ref();\n  const urlRef = toRef2(url);\n  let heartbeatPause;\n  let heartbeatResume;\n  let explicitlyClosed = false;\n  let retried = 0;\n  let bufferedData = [];\n  let retryTimeout;\n  let pongTimeoutWait;\n  const _sendBuffer = () => {\n    if (bufferedData.length && wsRef.value && status.value === \"OPEN\") {\n      for (const buffer of bufferedData)\n        wsRef.value.send(buffer);\n      bufferedData = [];\n    }\n  };\n  const resetRetry = () => {\n    if (retryTimeout != null) {\n      clearTimeout(retryTimeout);\n      retryTimeout = void 0;\n    }\n  };\n  const resetHeartbeat = () => {\n    clearTimeout(pongTimeoutWait);\n    pongTimeoutWait = void 0;\n  };\n  const close = (code = 1e3, reason) => {\n    resetRetry();\n    if (!isClient && !isWorker || !wsRef.value)\n      return;\n    explicitlyClosed = true;\n    resetHeartbeat();\n    heartbeatPause == null ? void 0 : heartbeatPause();\n    wsRef.value.close(code, reason);\n    wsRef.value = void 0;\n  };\n  const send = (data2, useBuffer = true) => {\n    if (!wsRef.value || status.value !== \"OPEN\") {\n      if (useBuffer)\n        bufferedData.push(data2);\n      return false;\n    }\n    _sendBuffer();\n    wsRef.value.send(data2);\n    return true;\n  };\n  const _init = () => {\n    if (explicitlyClosed || typeof urlRef.value === \"undefined\")\n      return;\n    const ws = new WebSocket(urlRef.value, protocols);\n    wsRef.value = ws;\n    status.value = \"CONNECTING\";\n    ws.onopen = () => {\n      status.value = \"OPEN\";\n      retried = 0;\n      onConnected == null ? void 0 : onConnected(ws);\n      heartbeatResume == null ? void 0 : heartbeatResume();\n      _sendBuffer();\n    };\n    ws.onclose = (ev) => {\n      status.value = \"CLOSED\";\n      onDisconnected == null ? void 0 : onDisconnected(ws, ev);\n      if (!explicitlyClosed && options.autoReconnect && (wsRef.value == null || ws === wsRef.value)) {\n        const {\n          retries = -1,\n          delay = 1e3,\n          onFailed\n        } = resolveNestedOptions(options.autoReconnect);\n        if (typeof retries === \"number\" && (retries < 0 || retried < retries)) {\n          retried += 1;\n          retryTimeout = setTimeout(_init, delay);\n        } else if (typeof retries === \"function\" && retries()) {\n          retryTimeout = setTimeout(_init, delay);\n        } else {\n          onFailed == null ? void 0 : onFailed();\n        }\n      }\n    };\n    ws.onerror = (e) => {\n      onError == null ? void 0 : onError(ws, e);\n    };\n    ws.onmessage = (e) => {\n      if (options.heartbeat) {\n        resetHeartbeat();\n        const {\n          message = DEFAULT_PING_MESSAGE,\n          responseMessage = message\n        } = resolveNestedOptions(options.heartbeat);\n        if (e.data === toValue(responseMessage))\n          return;\n      }\n      data.value = e.data;\n      onMessage == null ? void 0 : onMessage(ws, e);\n    };\n  };\n  if (options.heartbeat) {\n    const {\n      message = DEFAULT_PING_MESSAGE,\n      interval = 1e3,\n      pongTimeout = 1e3\n    } = resolveNestedOptions(options.heartbeat);\n    const { pause, resume } = useIntervalFn(\n      () => {\n        send(toValue(message), false);\n        if (pongTimeoutWait != null)\n          return;\n        pongTimeoutWait = setTimeout(() => {\n          close();\n          explicitlyClosed = false;\n        }, pongTimeout);\n      },\n      interval,\n      { immediate: false }\n    );\n    heartbeatPause = pause;\n    heartbeatResume = resume;\n  }\n  if (autoClose) {\n    if (isClient)\n      useEventListener(\"beforeunload\", () => close(), { passive: true });\n    tryOnScopeDispose(close);\n  }\n  const open = () => {\n    if (!isClient && !isWorker)\n      return;\n    close();\n    explicitlyClosed = false;\n    retried = 0;\n    _init();\n  };\n  if (immediate)\n    open();\n  if (autoConnect)\n    watch(urlRef, open);\n  return {\n    data,\n    status,\n    close,\n    send,\n    open,\n    ws: wsRef\n  };\n}\nfunction useWebWorker(arg0, workerOptions, options) {\n  const {\n    window: window2 = defaultWindow\n  } = options != null ? options : {};\n  const data = ref(null);\n  const worker = shallowRef();\n  const post = (...args) => {\n    if (!worker.value)\n      return;\n    worker.value.postMessage(...args);\n  };\n  const terminate = function terminate2() {\n    if (!worker.value)\n      return;\n    worker.value.terminate();\n  };\n  if (window2) {\n    if (typeof arg0 === \"string\")\n      worker.value = new Worker(arg0, workerOptions);\n    else if (typeof arg0 === \"function\")\n      worker.value = arg0();\n    else\n      worker.value = arg0;\n    worker.value.onmessage = (e) => {\n      data.value = e.data;\n    };\n    tryOnScopeDispose(() => {\n      if (worker.value)\n        worker.value.terminate();\n    });\n  }\n  return {\n    data,\n    post,\n    terminate,\n    worker\n  };\n}\nfunction depsParser(deps, localDeps) {\n  if (deps.length === 0 && localDeps.length === 0)\n    return \"\";\n  const depsString = deps.map((dep) => `'${dep}'`).toString();\n  const depsFunctionString = localDeps.filter((dep) => typeof dep === \"function\").map((fn) => {\n    const str = fn.toString();\n    if (str.trim().startsWith(\"function\")) {\n      return str;\n    } else {\n      const name = fn.name;\n      return `const ${name} = ${str}`;\n    }\n  }).join(\";\");\n  const importString = `importScripts(${depsString});`;\n  return `${depsString.trim() === \"\" ? \"\" : importString} ${depsFunctionString}`;\n}\nfunction jobRunner(userFunc) {\n  return (e) => {\n    const userFuncArgs = e.data[0];\n    return Promise.resolve(userFunc.apply(void 0, userFuncArgs)).then((result) => {\n      postMessage([\"SUCCESS\", result]);\n    }).catch((error) => {\n      postMessage([\"ERROR\", error]);\n    });\n  };\n}\nfunction createWorkerBlobUrl(fn, deps, localDeps) {\n  const blobCode = `${depsParser(deps, localDeps)}; onmessage=(${jobRunner})(${fn})`;\n  const blob = new Blob([blobCode], { type: \"text/javascript\" });\n  const url = URL.createObjectURL(blob);\n  return url;\n}\nfunction useWebWorkerFn(fn, options = {}) {\n  const {\n    dependencies = [],\n    localDependencies = [],\n    timeout,\n    window: window2 = defaultWindow\n  } = options;\n  const worker = ref();\n  const workerStatus = ref(\"PENDING\");\n  const promise = ref({});\n  const timeoutId = ref();\n  const workerTerminate = (status = \"PENDING\") => {\n    if (worker.value && worker.value._url && window2) {\n      worker.value.terminate();\n      URL.revokeObjectURL(worker.value._url);\n      promise.value = {};\n      worker.value = void 0;\n      window2.clearTimeout(timeoutId.value);\n      workerStatus.value = status;\n    }\n  };\n  workerTerminate();\n  tryOnScopeDispose(workerTerminate);\n  const generateWorker = () => {\n    const blobUrl = createWorkerBlobUrl(fn, dependencies, localDependencies);\n    const newWorker = new Worker(blobUrl);\n    newWorker._url = blobUrl;\n    newWorker.onmessage = (e) => {\n      const { resolve = () => {\n      }, reject = () => {\n      } } = promise.value;\n      const [status, result] = e.data;\n      switch (status) {\n        case \"SUCCESS\":\n          resolve(result);\n          workerTerminate(status);\n          break;\n        default:\n          reject(result);\n          workerTerminate(\"ERROR\");\n          break;\n      }\n    };\n    newWorker.onerror = (e) => {\n      const { reject = () => {\n      } } = promise.value;\n      e.preventDefault();\n      reject(e);\n      workerTerminate(\"ERROR\");\n    };\n    if (timeout) {\n      timeoutId.value = setTimeout(\n        () => workerTerminate(\"TIMEOUT_EXPIRED\"),\n        timeout\n      );\n    }\n    return newWorker;\n  };\n  const callWorker = (...fnArgs) => new Promise((resolve, reject) => {\n    var _a;\n    promise.value = {\n      resolve,\n      reject\n    };\n    (_a = worker.value) == null ? void 0 : _a.postMessage([[...fnArgs]]);\n    workerStatus.value = \"RUNNING\";\n  });\n  const workerFn = (...fnArgs) => {\n    if (workerStatus.value === \"RUNNING\") {\n      console.error(\n        \"[useWebWorkerFn] You can only run one instance of the worker at a time.\"\n      );\n      return Promise.reject();\n    }\n    worker.value = generateWorker();\n    return callWorker(...fnArgs);\n  };\n  return {\n    workerFn,\n    workerStatus,\n    workerTerminate\n  };\n}\nfunction useWindowFocus(options = {}) {\n  const { window: window2 = defaultWindow } = options;\n  if (!window2)\n    return ref(false);\n  const focused = ref(window2.document.hasFocus());\n  const listenerOptions = { passive: true };\n  useEventListener(window2, \"blur\", () => {\n    focused.value = false;\n  }, listenerOptions);\n  useEventListener(window2, \"focus\", () => {\n    focused.value = true;\n  }, listenerOptions);\n  return focused;\n}\nfunction useWindowScroll(options = {}) {\n  const { window: window2 = defaultWindow, ...rest } = options;\n  return useScroll(window2, rest);\n}\nfunction useWindowSize(options = {}) {\n  const {\n    window: window2 = defaultWindow,\n    initialWidth = Number.POSITIVE_INFINITY,\n    initialHeight = Number.POSITIVE_INFINITY,\n    listenOrientation = true,\n    includeScrollbar = true,\n    type = \"inner\"\n  } = options;\n  const width = ref(initialWidth);\n  const height = ref(initialHeight);\n  const update = () => {\n    if (window2) {\n      if (type === \"outer\") {\n        width.value = window2.outerWidth;\n        height.value = window2.outerHeight;\n      } else if (type === \"visual\" && window2.visualViewport) {\n        const { width: visualViewportWidth, height: visualViewportHeight, scale } = window2.visualViewport;\n        width.value = Math.round(visualViewportWidth * scale);\n        height.value = Math.round(visualViewportHeight * scale);\n      } else if (includeScrollbar) {\n        width.value = window2.innerWidth;\n        height.value = window2.innerHeight;\n      } else {\n        width.value = window2.document.documentElement.clientWidth;\n        height.value = window2.document.documentElement.clientHeight;\n      }\n    }\n  };\n  update();\n  tryOnMounted(update);\n  const listenerOptions = { passive: true };\n  useEventListener(\"resize\", update, listenerOptions);\n  if (window2 && type === \"visual\" && window2.visualViewport) {\n    useEventListener(window2.visualViewport, \"resize\", update, listenerOptions);\n  }\n  if (listenOrientation) {\n    const matches = useMediaQuery(\"(orientation: portrait)\");\n    watch(matches, () => update());\n  }\n  return { width, height };\n}\n\nexport {\n  computedEager,\n  computedWithControl,\n  tryOnScopeDispose,\n  createEventHook,\n  createGlobalState,\n  injectLocal,\n  provideLocal,\n  createInjectionState,\n  createSharedComposable,\n  extendRef,\n  get,\n  isDefined,\n  makeDestructurable,\n  reactify,\n  reactifyObject,\n  toReactive,\n  reactiveComputed,\n  reactiveOmit,\n  isClient,\n  isWorker,\n  isDef,\n  notNullish,\n  assert,\n  isObject,\n  now,\n  timestamp,\n  clamp,\n  noop,\n  rand,\n  hasOwn,\n  isIOS,\n  createFilterWrapper,\n  bypassFilter,\n  debounceFilter,\n  throttleFilter,\n  pausableFilter,\n  hyphenate,\n  camelize,\n  promiseTimeout,\n  identity,\n  createSingletonPromise,\n  invoke,\n  containsProp,\n  increaseWithUnit,\n  pxValue,\n  objectPick,\n  objectOmit,\n  objectEntries,\n  getLifeCycleTarget,\n  toArray,\n  toRef2 as toRef,\n  resolveRef,\n  reactivePick,\n  refAutoReset,\n  useDebounceFn,\n  refDebounced,\n  refDefault,\n  useThrottleFn,\n  refThrottled,\n  refWithControl,\n  controlledRef,\n  set,\n  watchWithFilter,\n  watchPausable,\n  syncRef,\n  syncRefs,\n  toRefs2 as toRefs,\n  toValue2 as toValue,\n  resolveUnref,\n  tryOnBeforeMount,\n  tryOnBeforeUnmount,\n  tryOnMounted,\n  tryOnUnmounted,\n  until,\n  useArrayDifference,\n  useArrayEvery,\n  useArrayFilter,\n  useArrayFind,\n  useArrayFindIndex,\n  useArrayFindLast,\n  useArrayIncludes,\n  useArrayJoin,\n  useArrayMap,\n  useArrayReduce,\n  useArraySome,\n  useArrayUnique,\n  useCounter,\n  formatDate,\n  normalizeDate,\n  useDateFormat,\n  useIntervalFn,\n  useInterval,\n  useLastChanged,\n  useTimeoutFn,\n  useTimeout,\n  useToNumber,\n  useToString,\n  useToggle,\n  watchArray,\n  watchAtMost,\n  watchDebounced,\n  watchDeep,\n  watchIgnorable,\n  watchImmediate,\n  watchOnce,\n  watchThrottled,\n  watchTriggerable,\n  whenever,\n  computedAsync,\n  computedInject,\n  createReusableTemplate,\n  createTemplatePromise,\n  createUnrefFn,\n  defaultWindow,\n  defaultDocument,\n  defaultNavigator,\n  defaultLocation,\n  unrefElement,\n  useEventListener,\n  onClickOutside,\n  useMounted,\n  useSupported,\n  useMutationObserver,\n  onElementRemoval,\n  onKeyStroke,\n  onKeyDown,\n  onKeyPressed,\n  onKeyUp,\n  onLongPress,\n  onStartTyping,\n  templateRef,\n  useActiveElement,\n  useRafFn,\n  useAnimate,\n  useAsyncQueue,\n  useAsyncState,\n  useBase64,\n  useBattery,\n  useBluetooth,\n  useSSRWidth,\n  provideSSRWidth,\n  useMediaQuery,\n  breakpointsTailwind,\n  breakpointsBootstrapV5,\n  breakpointsVuetifyV2,\n  breakpointsVuetifyV3,\n  breakpointsVuetify,\n  breakpointsAntDesign,\n  breakpointsQuasar,\n  breakpointsSematic,\n  breakpointsMasterCss,\n  breakpointsPrimeFlex,\n  breakpointsElement,\n  useBreakpoints,\n  useBroadcastChannel,\n  useBrowserLocation,\n  useCached,\n  usePermission,\n  useClipboard,\n  useClipboardItems,\n  cloneFnJSON,\n  useCloned,\n  getSSRHandler,\n  setSSRHandler,\n  usePreferredDark,\n  StorageSerializers,\n  customStorageEventName,\n  useStorage,\n  useColorMode,\n  useConfirmDialog,\n  useCountdown,\n  useCssVar,\n  useCurrentElement,\n  useCycleList,\n  useDark,\n  useManualRefHistory,\n  useRefHistory,\n  useDebouncedRefHistory,\n  useDeviceMotion,\n  useDeviceOrientation,\n  useDevicePixelRatio,\n  useDevicesList,\n  useDisplayMedia,\n  useDocumentVisibility,\n  useDraggable,\n  useDropZone,\n  useResizeObserver,\n  useElementBounding,\n  useElementByPoint,\n  useElementHover,\n  useElementSize,\n  useIntersectionObserver,\n  useElementVisibility,\n  useEventBus,\n  useEventSource,\n  useEyeDropper,\n  useFavicon,\n  createFetch,\n  useFetch,\n  useFileDialog,\n  useFileSystemAccess,\n  useFocus,\n  useFocusWithin,\n  useFps,\n  useFullscreen,\n  mapGamepadToXbox360Controller,\n  useGamepad,\n  useGeolocation,\n  useIdle,\n  useImage,\n  useScroll,\n  useInfiniteScroll,\n  useKeyModifier,\n  useLocalStorage,\n  DefaultMagicKeysAliasMap,\n  useMagicKeys,\n  useMediaControls,\n  useMemoize,\n  useMemory,\n  useMouse,\n  useMouseInElement,\n  useMousePressed,\n  useNavigatorLanguage,\n  useNetwork,\n  useNow,\n  useObjectUrl,\n  useOffsetPagination,\n  useOnline,\n  usePageLeave,\n  useScreenOrientation,\n  useParallax,\n  useParentElement,\n  usePerformanceObserver,\n  usePointer,\n  usePointerLock,\n  usePointerSwipe,\n  usePreferredColorScheme,\n  usePreferredContrast,\n  usePreferredLanguages,\n  usePreferredReducedMotion,\n  usePreferredReducedTransparency,\n  usePrevious,\n  useScreenSafeArea,\n  useScriptTag,\n  useScrollLock,\n  useSessionStorage,\n  useShare,\n  useSorted,\n  useSpeechRecognition,\n  useSpeechSynthesis,\n  useStepper,\n  useStorageAsync,\n  useStyleTag,\n  useSwipe,\n  useTemplateRefsList,\n  useTextDirection,\n  useTextSelection,\n  useTextareaAutosize,\n  useThrottledRefHistory,\n  useTimeAgo,\n  formatTimeAgo,\n  useTimeoutPoll,\n  useTimestamp,\n  useTitle,\n  TransitionPresets,\n  executeTransition,\n  useTransition,\n  useUrlSearchParams,\n  useUserMedia,\n  useVModel,\n  useVModels,\n  useVibrate,\n  useVirtualList,\n  useWakeLock,\n  useWebNotification,\n  useWebSocket,\n  useWebWorker,\n  useWebWorkerFn,\n  useWindowFocus,\n  useWindowScroll,\n  useWindowSize\n};\n//# sourceMappingURL=chunk-KT7LHMJ2.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/package.json",
    "content": "{\n  \"type\": \"module\"\n}\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js",
    "content": "// node_modules/.pnpm/@vue+devtools-shared@7.7.1/node_modules/@vue/devtools-shared/dist/index.js\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __esm = (fn, res) => function __init() {\n  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;\n};\nvar __commonJS = (cb, mod) => function __require() {\n  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;\n};\nvar __copyProps = (to, from, except, desc) => {\n  if (from && typeof from === \"object\" || typeof from === \"function\") {\n    for (let key of __getOwnPropNames(from))\n      if (!__hasOwnProp.call(to, key) && key !== except)\n        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n  }\n  return to;\n};\nvar __toESM = (mod, isNodeMode, target2) => (target2 = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n  // If the importer is in node compatibility mode or this is not an ESM\n  // file that has been converted to a CommonJS file using a Babel-\n  // compatible transform (i.e. \"__esModule\" has not been set), then set\n  // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n  isNodeMode || !mod || !mod.__esModule ? __defProp(target2, \"default\", { value: mod, enumerable: true }) : target2,\n  mod\n));\nvar init_esm_shims = __esm({\n  \"../../node_modules/.pnpm/tsup@8.3.5_@microsoft+api-extractor@7.48.1_@types+node@22.10.5__jiti@2.4.2_postcss@8.4.49_tsx_s7k37zks4wtn7x2grzma6lrsfa/node_modules/tsup/assets/esm_shims.js\"() {\n    \"use strict\";\n  }\n});\nvar require_rfdc = __commonJS({\n  \"../../node_modules/.pnpm/rfdc@1.4.1/node_modules/rfdc/index.js\"(exports, module) {\n    \"use strict\";\n    init_esm_shims();\n    module.exports = rfdc2;\n    function copyBuffer(cur) {\n      if (cur instanceof Buffer) {\n        return Buffer.from(cur);\n      }\n      return new cur.constructor(cur.buffer.slice(), cur.byteOffset, cur.length);\n    }\n    function rfdc2(opts) {\n      opts = opts || {};\n      if (opts.circles) return rfdcCircles(opts);\n      const constructorHandlers = /* @__PURE__ */ new Map();\n      constructorHandlers.set(Date, (o) => new Date(o));\n      constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)));\n      constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)));\n      if (opts.constructorHandlers) {\n        for (const handler2 of opts.constructorHandlers) {\n          constructorHandlers.set(handler2[0], handler2[1]);\n        }\n      }\n      let handler = null;\n      return opts.proto ? cloneProto : clone;\n      function cloneArray(a, fn) {\n        const keys = Object.keys(a);\n        const a2 = new Array(keys.length);\n        for (let i = 0; i < keys.length; i++) {\n          const k = keys[i];\n          const cur = a[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            a2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            a2[k] = handler(cur, fn);\n          } else if (ArrayBuffer.isView(cur)) {\n            a2[k] = copyBuffer(cur);\n          } else {\n            a2[k] = fn(cur);\n          }\n        }\n        return a2;\n      }\n      function clone(o) {\n        if (typeof o !== \"object\" || o === null) return o;\n        if (Array.isArray(o)) return cloneArray(o, clone);\n        if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {\n          return handler(o, clone);\n        }\n        const o2 = {};\n        for (const k in o) {\n          if (Object.hasOwnProperty.call(o, k) === false) continue;\n          const cur = o[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            o2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            o2[k] = handler(cur, clone);\n          } else if (ArrayBuffer.isView(cur)) {\n            o2[k] = copyBuffer(cur);\n          } else {\n            o2[k] = clone(cur);\n          }\n        }\n        return o2;\n      }\n      function cloneProto(o) {\n        if (typeof o !== \"object\" || o === null) return o;\n        if (Array.isArray(o)) return cloneArray(o, cloneProto);\n        if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {\n          return handler(o, cloneProto);\n        }\n        const o2 = {};\n        for (const k in o) {\n          const cur = o[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            o2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            o2[k] = handler(cur, cloneProto);\n          } else if (ArrayBuffer.isView(cur)) {\n            o2[k] = copyBuffer(cur);\n          } else {\n            o2[k] = cloneProto(cur);\n          }\n        }\n        return o2;\n      }\n    }\n    function rfdcCircles(opts) {\n      const refs = [];\n      const refsNew = [];\n      const constructorHandlers = /* @__PURE__ */ new Map();\n      constructorHandlers.set(Date, (o) => new Date(o));\n      constructorHandlers.set(Map, (o, fn) => new Map(cloneArray(Array.from(o), fn)));\n      constructorHandlers.set(Set, (o, fn) => new Set(cloneArray(Array.from(o), fn)));\n      if (opts.constructorHandlers) {\n        for (const handler2 of opts.constructorHandlers) {\n          constructorHandlers.set(handler2[0], handler2[1]);\n        }\n      }\n      let handler = null;\n      return opts.proto ? cloneProto : clone;\n      function cloneArray(a, fn) {\n        const keys = Object.keys(a);\n        const a2 = new Array(keys.length);\n        for (let i = 0; i < keys.length; i++) {\n          const k = keys[i];\n          const cur = a[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            a2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            a2[k] = handler(cur, fn);\n          } else if (ArrayBuffer.isView(cur)) {\n            a2[k] = copyBuffer(cur);\n          } else {\n            const index = refs.indexOf(cur);\n            if (index !== -1) {\n              a2[k] = refsNew[index];\n            } else {\n              a2[k] = fn(cur);\n            }\n          }\n        }\n        return a2;\n      }\n      function clone(o) {\n        if (typeof o !== \"object\" || o === null) return o;\n        if (Array.isArray(o)) return cloneArray(o, clone);\n        if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {\n          return handler(o, clone);\n        }\n        const o2 = {};\n        refs.push(o);\n        refsNew.push(o2);\n        for (const k in o) {\n          if (Object.hasOwnProperty.call(o, k) === false) continue;\n          const cur = o[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            o2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            o2[k] = handler(cur, clone);\n          } else if (ArrayBuffer.isView(cur)) {\n            o2[k] = copyBuffer(cur);\n          } else {\n            const i = refs.indexOf(cur);\n            if (i !== -1) {\n              o2[k] = refsNew[i];\n            } else {\n              o2[k] = clone(cur);\n            }\n          }\n        }\n        refs.pop();\n        refsNew.pop();\n        return o2;\n      }\n      function cloneProto(o) {\n        if (typeof o !== \"object\" || o === null) return o;\n        if (Array.isArray(o)) return cloneArray(o, cloneProto);\n        if (o.constructor !== Object && (handler = constructorHandlers.get(o.constructor))) {\n          return handler(o, cloneProto);\n        }\n        const o2 = {};\n        refs.push(o);\n        refsNew.push(o2);\n        for (const k in o) {\n          const cur = o[k];\n          if (typeof cur !== \"object\" || cur === null) {\n            o2[k] = cur;\n          } else if (cur.constructor !== Object && (handler = constructorHandlers.get(cur.constructor))) {\n            o2[k] = handler(cur, cloneProto);\n          } else if (ArrayBuffer.isView(cur)) {\n            o2[k] = copyBuffer(cur);\n          } else {\n            const i = refs.indexOf(cur);\n            if (i !== -1) {\n              o2[k] = refsNew[i];\n            } else {\n              o2[k] = cloneProto(cur);\n            }\n          }\n        }\n        refs.pop();\n        refsNew.pop();\n        return o2;\n      }\n    }\n  }\n});\ninit_esm_shims();\ninit_esm_shims();\ninit_esm_shims();\nvar isBrowser = typeof navigator !== \"undefined\";\nvar target = typeof window !== \"undefined\" ? window : typeof globalThis !== \"undefined\" ? globalThis : typeof global !== \"undefined\" ? global : {};\nvar isInChromePanel = typeof target.chrome !== \"undefined\" && !!target.chrome.devtools;\nvar isInIframe = isBrowser && target.self !== target.top;\nvar _a;\nvar isInElectron = typeof navigator !== \"undefined\" && ((_a = navigator.userAgent) == null ? void 0 : _a.toLowerCase().includes(\"electron\"));\nvar isNuxtApp = typeof window !== \"undefined\" && !!window.__NUXT__;\ninit_esm_shims();\nvar import_rfdc = __toESM(require_rfdc(), 1);\nvar classifyRE = /(?:^|[-_/])(\\w)/g;\nfunction toUpper(_, c) {\n  return c ? c.toUpperCase() : \"\";\n}\nfunction classify(str) {\n  return str && `${str}`.replace(classifyRE, toUpper);\n}\nfunction basename(filename, ext) {\n  let normalizedFilename = filename.replace(/^[a-z]:/i, \"\").replace(/\\\\/g, \"/\");\n  if (normalizedFilename.endsWith(`index${ext}`)) {\n    normalizedFilename = normalizedFilename.replace(`/index${ext}`, ext);\n  }\n  const lastSlashIndex = normalizedFilename.lastIndexOf(\"/\");\n  const baseNameWithExt = normalizedFilename.substring(lastSlashIndex + 1);\n  if (ext) {\n    const extIndex = baseNameWithExt.lastIndexOf(ext);\n    return baseNameWithExt.substring(0, extIndex);\n  }\n  return \"\";\n}\nvar HTTP_URL_RE = /^https?:\\/\\//;\nfunction isUrlString(str) {\n  return str.startsWith(\"/\") || HTTP_URL_RE.test(str);\n}\nvar deepClone = (0, import_rfdc.default)({ circles: true });\n\n// node_modules/.pnpm/perfect-debounce@1.0.0/node_modules/perfect-debounce/dist/index.mjs\nvar DEBOUNCE_DEFAULTS = {\n  trailing: true\n};\nfunction debounce(fn, wait = 25, options = {}) {\n  options = { ...DEBOUNCE_DEFAULTS, ...options };\n  if (!Number.isFinite(wait)) {\n    throw new TypeError(\"Expected `wait` to be a finite number\");\n  }\n  let leadingValue;\n  let timeout;\n  let resolveList = [];\n  let currentPromise;\n  let trailingArgs;\n  const applyFn = (_this, args) => {\n    currentPromise = _applyPromised(fn, _this, args);\n    currentPromise.finally(() => {\n      currentPromise = null;\n      if (options.trailing && trailingArgs && !timeout) {\n        const promise = applyFn(_this, trailingArgs);\n        trailingArgs = null;\n        return promise;\n      }\n    });\n    return currentPromise;\n  };\n  return function(...args) {\n    if (currentPromise) {\n      if (options.trailing) {\n        trailingArgs = args;\n      }\n      return currentPromise;\n    }\n    return new Promise((resolve) => {\n      const shouldCallNow = !timeout && options.leading;\n      clearTimeout(timeout);\n      timeout = setTimeout(() => {\n        timeout = null;\n        const promise = options.leading ? leadingValue : applyFn(this, args);\n        for (const _resolve of resolveList) {\n          _resolve(promise);\n        }\n        resolveList = [];\n      }, wait);\n      if (shouldCallNow) {\n        leadingValue = applyFn(this, args);\n        resolve(leadingValue);\n      } else {\n        resolveList.push(resolve);\n      }\n    });\n  };\n}\nasync function _applyPromised(fn, _this, args) {\n  return await fn.apply(_this, args);\n}\n\n// node_modules/.pnpm/hookable@5.5.3/node_modules/hookable/dist/index.mjs\nfunction flatHooks(configHooks, hooks2 = {}, parentName) {\n  for (const key in configHooks) {\n    const subHook = configHooks[key];\n    const name = parentName ? `${parentName}:${key}` : key;\n    if (typeof subHook === \"object\" && subHook !== null) {\n      flatHooks(subHook, hooks2, name);\n    } else if (typeof subHook === \"function\") {\n      hooks2[name] = subHook;\n    }\n  }\n  return hooks2;\n}\nvar defaultTask = { run: (function_) => function_() };\nvar _createTask = () => defaultTask;\nvar createTask = typeof console.createTask !== \"undefined\" ? console.createTask : _createTask;\nfunction serialTaskCaller(hooks2, args) {\n  const name = args.shift();\n  const task = createTask(name);\n  return hooks2.reduce(\n    (promise, hookFunction) => promise.then(() => task.run(() => hookFunction(...args))),\n    Promise.resolve()\n  );\n}\nfunction parallelTaskCaller(hooks2, args) {\n  const name = args.shift();\n  const task = createTask(name);\n  return Promise.all(hooks2.map((hook2) => task.run(() => hook2(...args))));\n}\nfunction callEachWith(callbacks, arg0) {\n  for (const callback of [...callbacks]) {\n    callback(arg0);\n  }\n}\nvar Hookable = class {\n  constructor() {\n    this._hooks = {};\n    this._before = void 0;\n    this._after = void 0;\n    this._deprecatedMessages = void 0;\n    this._deprecatedHooks = {};\n    this.hook = this.hook.bind(this);\n    this.callHook = this.callHook.bind(this);\n    this.callHookWith = this.callHookWith.bind(this);\n  }\n  hook(name, function_, options = {}) {\n    if (!name || typeof function_ !== \"function\") {\n      return () => {\n      };\n    }\n    const originalName = name;\n    let dep;\n    while (this._deprecatedHooks[name]) {\n      dep = this._deprecatedHooks[name];\n      name = dep.to;\n    }\n    if (dep && !options.allowDeprecated) {\n      let message = dep.message;\n      if (!message) {\n        message = `${originalName} hook has been deprecated` + (dep.to ? `, please use ${dep.to}` : \"\");\n      }\n      if (!this._deprecatedMessages) {\n        this._deprecatedMessages = /* @__PURE__ */ new Set();\n      }\n      if (!this._deprecatedMessages.has(message)) {\n        console.warn(message);\n        this._deprecatedMessages.add(message);\n      }\n    }\n    if (!function_.name) {\n      try {\n        Object.defineProperty(function_, \"name\", {\n          get: () => \"_\" + name.replace(/\\W+/g, \"_\") + \"_hook_cb\",\n          configurable: true\n        });\n      } catch {\n      }\n    }\n    this._hooks[name] = this._hooks[name] || [];\n    this._hooks[name].push(function_);\n    return () => {\n      if (function_) {\n        this.removeHook(name, function_);\n        function_ = void 0;\n      }\n    };\n  }\n  hookOnce(name, function_) {\n    let _unreg;\n    let _function = (...arguments_) => {\n      if (typeof _unreg === \"function\") {\n        _unreg();\n      }\n      _unreg = void 0;\n      _function = void 0;\n      return function_(...arguments_);\n    };\n    _unreg = this.hook(name, _function);\n    return _unreg;\n  }\n  removeHook(name, function_) {\n    if (this._hooks[name]) {\n      const index = this._hooks[name].indexOf(function_);\n      if (index !== -1) {\n        this._hooks[name].splice(index, 1);\n      }\n      if (this._hooks[name].length === 0) {\n        delete this._hooks[name];\n      }\n    }\n  }\n  deprecateHook(name, deprecated) {\n    this._deprecatedHooks[name] = typeof deprecated === \"string\" ? { to: deprecated } : deprecated;\n    const _hooks = this._hooks[name] || [];\n    delete this._hooks[name];\n    for (const hook2 of _hooks) {\n      this.hook(name, hook2);\n    }\n  }\n  deprecateHooks(deprecatedHooks) {\n    Object.assign(this._deprecatedHooks, deprecatedHooks);\n    for (const name in deprecatedHooks) {\n      this.deprecateHook(name, deprecatedHooks[name]);\n    }\n  }\n  addHooks(configHooks) {\n    const hooks2 = flatHooks(configHooks);\n    const removeFns = Object.keys(hooks2).map(\n      (key) => this.hook(key, hooks2[key])\n    );\n    return () => {\n      for (const unreg of removeFns.splice(0, removeFns.length)) {\n        unreg();\n      }\n    };\n  }\n  removeHooks(configHooks) {\n    const hooks2 = flatHooks(configHooks);\n    for (const key in hooks2) {\n      this.removeHook(key, hooks2[key]);\n    }\n  }\n  removeAllHooks() {\n    for (const key in this._hooks) {\n      delete this._hooks[key];\n    }\n  }\n  callHook(name, ...arguments_) {\n    arguments_.unshift(name);\n    return this.callHookWith(serialTaskCaller, name, ...arguments_);\n  }\n  callHookParallel(name, ...arguments_) {\n    arguments_.unshift(name);\n    return this.callHookWith(parallelTaskCaller, name, ...arguments_);\n  }\n  callHookWith(caller, name, ...arguments_) {\n    const event = this._before || this._after ? { name, args: arguments_, context: {} } : void 0;\n    if (this._before) {\n      callEachWith(this._before, event);\n    }\n    const result = caller(\n      name in this._hooks ? [...this._hooks[name]] : [],\n      arguments_\n    );\n    if (result instanceof Promise) {\n      return result.finally(() => {\n        if (this._after && event) {\n          callEachWith(this._after, event);\n        }\n      });\n    }\n    if (this._after && event) {\n      callEachWith(this._after, event);\n    }\n    return result;\n  }\n  beforeEach(function_) {\n    this._before = this._before || [];\n    this._before.push(function_);\n    return () => {\n      if (this._before !== void 0) {\n        const index = this._before.indexOf(function_);\n        if (index !== -1) {\n          this._before.splice(index, 1);\n        }\n      }\n    };\n  }\n  afterEach(function_) {\n    this._after = this._after || [];\n    this._after.push(function_);\n    return () => {\n      if (this._after !== void 0) {\n        const index = this._after.indexOf(function_);\n        if (index !== -1) {\n          this._after.splice(index, 1);\n        }\n      }\n    };\n  }\n};\nfunction createHooks() {\n  return new Hookable();\n}\n\n// node_modules/.pnpm/birpc@0.2.19/node_modules/birpc/dist/index.mjs\nvar { clearTimeout: clearTimeout2, setTimeout: setTimeout2 } = globalThis;\nvar random = Math.random.bind(Math);\n\n// node_modules/.pnpm/@vue+devtools-kit@7.7.1/node_modules/@vue/devtools-kit/dist/index.js\nvar __create2 = Object.create;\nvar __defProp2 = Object.defineProperty;\nvar __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames2 = Object.getOwnPropertyNames;\nvar __getProtoOf2 = Object.getPrototypeOf;\nvar __hasOwnProp2 = Object.prototype.hasOwnProperty;\nvar __esm2 = (fn, res) => function __init() {\n  return fn && (res = (0, fn[__getOwnPropNames2(fn)[0]])(fn = 0)), res;\n};\nvar __commonJS2 = (cb, mod) => function __require() {\n  return mod || (0, cb[__getOwnPropNames2(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;\n};\nvar __copyProps2 = (to, from, except, desc) => {\n  if (from && typeof from === \"object\" || typeof from === \"function\") {\n    for (let key of __getOwnPropNames2(from))\n      if (!__hasOwnProp2.call(to, key) && key !== except)\n        __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });\n  }\n  return to;\n};\nvar __toESM2 = (mod, isNodeMode, target22) => (target22 = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps2(\n  // If the importer is in node compatibility mode or this is not an ESM\n  // file that has been converted to a CommonJS file using a Babel-\n  // compatible transform (i.e. \"__esModule\" has not been set), then set\n  // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n  isNodeMode || !mod || !mod.__esModule ? __defProp2(target22, \"default\", { value: mod, enumerable: true }) : target22,\n  mod\n));\nvar init_esm_shims2 = __esm2({\n  \"../../node_modules/.pnpm/tsup@8.3.5_@microsoft+api-extractor@7.48.1_@types+node@22.10.5__jiti@2.4.2_postcss@8.4.49_tsx_s7k37zks4wtn7x2grzma6lrsfa/node_modules/tsup/assets/esm_shims.js\"() {\n    \"use strict\";\n  }\n});\nvar require_speakingurl = __commonJS2({\n  \"../../node_modules/.pnpm/speakingurl@14.0.1/node_modules/speakingurl/lib/speakingurl.js\"(exports, module) {\n    \"use strict\";\n    init_esm_shims2();\n    (function(root) {\n      \"use strict\";\n      var charMap = {\n        // latin\n        \"À\": \"A\",\n        \"Á\": \"A\",\n        \"Â\": \"A\",\n        \"Ã\": \"A\",\n        \"Ä\": \"Ae\",\n        \"Å\": \"A\",\n        \"Æ\": \"AE\",\n        \"Ç\": \"C\",\n        \"È\": \"E\",\n        \"É\": \"E\",\n        \"Ê\": \"E\",\n        \"Ë\": \"E\",\n        \"Ì\": \"I\",\n        \"Í\": \"I\",\n        \"Î\": \"I\",\n        \"Ï\": \"I\",\n        \"Ð\": \"D\",\n        \"Ñ\": \"N\",\n        \"Ò\": \"O\",\n        \"Ó\": \"O\",\n        \"Ô\": \"O\",\n        \"Õ\": \"O\",\n        \"Ö\": \"Oe\",\n        \"Ő\": \"O\",\n        \"Ø\": \"O\",\n        \"Ù\": \"U\",\n        \"Ú\": \"U\",\n        \"Û\": \"U\",\n        \"Ü\": \"Ue\",\n        \"Ű\": \"U\",\n        \"Ý\": \"Y\",\n        \"Þ\": \"TH\",\n        \"ß\": \"ss\",\n        \"à\": \"a\",\n        \"á\": \"a\",\n        \"â\": \"a\",\n        \"ã\": \"a\",\n        \"ä\": \"ae\",\n        \"å\": \"a\",\n        \"æ\": \"ae\",\n        \"ç\": \"c\",\n        \"è\": \"e\",\n        \"é\": \"e\",\n        \"ê\": \"e\",\n        \"ë\": \"e\",\n        \"ì\": \"i\",\n        \"í\": \"i\",\n        \"î\": \"i\",\n        \"ï\": \"i\",\n        \"ð\": \"d\",\n        \"ñ\": \"n\",\n        \"ò\": \"o\",\n        \"ó\": \"o\",\n        \"ô\": \"o\",\n        \"õ\": \"o\",\n        \"ö\": \"oe\",\n        \"ő\": \"o\",\n        \"ø\": \"o\",\n        \"ù\": \"u\",\n        \"ú\": \"u\",\n        \"û\": \"u\",\n        \"ü\": \"ue\",\n        \"ű\": \"u\",\n        \"ý\": \"y\",\n        \"þ\": \"th\",\n        \"ÿ\": \"y\",\n        \"ẞ\": \"SS\",\n        // language specific\n        // Arabic\n        \"ا\": \"a\",\n        \"أ\": \"a\",\n        \"إ\": \"i\",\n        \"آ\": \"aa\",\n        \"ؤ\": \"u\",\n        \"ئ\": \"e\",\n        \"ء\": \"a\",\n        \"ب\": \"b\",\n        \"ت\": \"t\",\n        \"ث\": \"th\",\n        \"ج\": \"j\",\n        \"ح\": \"h\",\n        \"خ\": \"kh\",\n        \"د\": \"d\",\n        \"ذ\": \"th\",\n        \"ر\": \"r\",\n        \"ز\": \"z\",\n        \"س\": \"s\",\n        \"ش\": \"sh\",\n        \"ص\": \"s\",\n        \"ض\": \"dh\",\n        \"ط\": \"t\",\n        \"ظ\": \"z\",\n        \"ع\": \"a\",\n        \"غ\": \"gh\",\n        \"ف\": \"f\",\n        \"ق\": \"q\",\n        \"ك\": \"k\",\n        \"ل\": \"l\",\n        \"م\": \"m\",\n        \"ن\": \"n\",\n        \"ه\": \"h\",\n        \"و\": \"w\",\n        \"ي\": \"y\",\n        \"ى\": \"a\",\n        \"ة\": \"h\",\n        \"ﻻ\": \"la\",\n        \"ﻷ\": \"laa\",\n        \"ﻹ\": \"lai\",\n        \"ﻵ\": \"laa\",\n        // Persian additional characters than Arabic\n        \"گ\": \"g\",\n        \"چ\": \"ch\",\n        \"پ\": \"p\",\n        \"ژ\": \"zh\",\n        \"ک\": \"k\",\n        \"ی\": \"y\",\n        // Arabic diactrics\n        \"َ\": \"a\",\n        \"ً\": \"an\",\n        \"ِ\": \"e\",\n        \"ٍ\": \"en\",\n        \"ُ\": \"u\",\n        \"ٌ\": \"on\",\n        \"ْ\": \"\",\n        // Arabic numbers\n        \"٠\": \"0\",\n        \"١\": \"1\",\n        \"٢\": \"2\",\n        \"٣\": \"3\",\n        \"٤\": \"4\",\n        \"٥\": \"5\",\n        \"٦\": \"6\",\n        \"٧\": \"7\",\n        \"٨\": \"8\",\n        \"٩\": \"9\",\n        // Persian numbers\n        \"۰\": \"0\",\n        \"۱\": \"1\",\n        \"۲\": \"2\",\n        \"۳\": \"3\",\n        \"۴\": \"4\",\n        \"۵\": \"5\",\n        \"۶\": \"6\",\n        \"۷\": \"7\",\n        \"۸\": \"8\",\n        \"۹\": \"9\",\n        // Burmese consonants\n        \"က\": \"k\",\n        \"ခ\": \"kh\",\n        \"ဂ\": \"g\",\n        \"ဃ\": \"ga\",\n        \"င\": \"ng\",\n        \"စ\": \"s\",\n        \"ဆ\": \"sa\",\n        \"ဇ\": \"z\",\n        \"စျ\": \"za\",\n        \"ည\": \"ny\",\n        \"ဋ\": \"t\",\n        \"ဌ\": \"ta\",\n        \"ဍ\": \"d\",\n        \"ဎ\": \"da\",\n        \"ဏ\": \"na\",\n        \"တ\": \"t\",\n        \"ထ\": \"ta\",\n        \"ဒ\": \"d\",\n        \"ဓ\": \"da\",\n        \"န\": \"n\",\n        \"ပ\": \"p\",\n        \"ဖ\": \"pa\",\n        \"ဗ\": \"b\",\n        \"ဘ\": \"ba\",\n        \"မ\": \"m\",\n        \"ယ\": \"y\",\n        \"ရ\": \"ya\",\n        \"လ\": \"l\",\n        \"ဝ\": \"w\",\n        \"သ\": \"th\",\n        \"ဟ\": \"h\",\n        \"ဠ\": \"la\",\n        \"အ\": \"a\",\n        // consonant character combos\n        \"ြ\": \"y\",\n        \"ျ\": \"ya\",\n        \"ွ\": \"w\",\n        \"ြွ\": \"yw\",\n        \"ျွ\": \"ywa\",\n        \"ှ\": \"h\",\n        // independent vowels\n        \"ဧ\": \"e\",\n        \"၏\": \"-e\",\n        \"ဣ\": \"i\",\n        \"ဤ\": \"-i\",\n        \"ဉ\": \"u\",\n        \"ဦ\": \"-u\",\n        \"ဩ\": \"aw\",\n        \"သြော\": \"aw\",\n        \"ဪ\": \"aw\",\n        // numbers\n        \"၀\": \"0\",\n        \"၁\": \"1\",\n        \"၂\": \"2\",\n        \"၃\": \"3\",\n        \"၄\": \"4\",\n        \"၅\": \"5\",\n        \"၆\": \"6\",\n        \"၇\": \"7\",\n        \"၈\": \"8\",\n        \"၉\": \"9\",\n        // virama and tone marks which are silent in transliteration\n        \"္\": \"\",\n        \"့\": \"\",\n        \"း\": \"\",\n        // Czech\n        \"č\": \"c\",\n        \"ď\": \"d\",\n        \"ě\": \"e\",\n        \"ň\": \"n\",\n        \"ř\": \"r\",\n        \"š\": \"s\",\n        \"ť\": \"t\",\n        \"ů\": \"u\",\n        \"ž\": \"z\",\n        \"Č\": \"C\",\n        \"Ď\": \"D\",\n        \"Ě\": \"E\",\n        \"Ň\": \"N\",\n        \"Ř\": \"R\",\n        \"Š\": \"S\",\n        \"Ť\": \"T\",\n        \"Ů\": \"U\",\n        \"Ž\": \"Z\",\n        // Dhivehi\n        \"ހ\": \"h\",\n        \"ށ\": \"sh\",\n        \"ނ\": \"n\",\n        \"ރ\": \"r\",\n        \"ބ\": \"b\",\n        \"ޅ\": \"lh\",\n        \"ކ\": \"k\",\n        \"އ\": \"a\",\n        \"ވ\": \"v\",\n        \"މ\": \"m\",\n        \"ފ\": \"f\",\n        \"ދ\": \"dh\",\n        \"ތ\": \"th\",\n        \"ލ\": \"l\",\n        \"ގ\": \"g\",\n        \"ޏ\": \"gn\",\n        \"ސ\": \"s\",\n        \"ޑ\": \"d\",\n        \"ޒ\": \"z\",\n        \"ޓ\": \"t\",\n        \"ޔ\": \"y\",\n        \"ޕ\": \"p\",\n        \"ޖ\": \"j\",\n        \"ޗ\": \"ch\",\n        \"ޘ\": \"tt\",\n        \"ޙ\": \"hh\",\n        \"ޚ\": \"kh\",\n        \"ޛ\": \"th\",\n        \"ޜ\": \"z\",\n        \"ޝ\": \"sh\",\n        \"ޞ\": \"s\",\n        \"ޟ\": \"d\",\n        \"ޠ\": \"t\",\n        \"ޡ\": \"z\",\n        \"ޢ\": \"a\",\n        \"ޣ\": \"gh\",\n        \"ޤ\": \"q\",\n        \"ޥ\": \"w\",\n        \"ަ\": \"a\",\n        \"ާ\": \"aa\",\n        \"ި\": \"i\",\n        \"ީ\": \"ee\",\n        \"ު\": \"u\",\n        \"ޫ\": \"oo\",\n        \"ެ\": \"e\",\n        \"ޭ\": \"ey\",\n        \"ޮ\": \"o\",\n        \"ޯ\": \"oa\",\n        \"ް\": \"\",\n        // Georgian https://en.wikipedia.org/wiki/Romanization_of_Georgian\n        // National system (2002)\n        \"ა\": \"a\",\n        \"ბ\": \"b\",\n        \"გ\": \"g\",\n        \"დ\": \"d\",\n        \"ე\": \"e\",\n        \"ვ\": \"v\",\n        \"ზ\": \"z\",\n        \"თ\": \"t\",\n        \"ი\": \"i\",\n        \"კ\": \"k\",\n        \"ლ\": \"l\",\n        \"მ\": \"m\",\n        \"ნ\": \"n\",\n        \"ო\": \"o\",\n        \"პ\": \"p\",\n        \"ჟ\": \"zh\",\n        \"რ\": \"r\",\n        \"ს\": \"s\",\n        \"ტ\": \"t\",\n        \"უ\": \"u\",\n        \"ფ\": \"p\",\n        \"ქ\": \"k\",\n        \"ღ\": \"gh\",\n        \"ყ\": \"q\",\n        \"შ\": \"sh\",\n        \"ჩ\": \"ch\",\n        \"ც\": \"ts\",\n        \"ძ\": \"dz\",\n        \"წ\": \"ts\",\n        \"ჭ\": \"ch\",\n        \"ხ\": \"kh\",\n        \"ჯ\": \"j\",\n        \"ჰ\": \"h\",\n        // Greek\n        \"α\": \"a\",\n        \"β\": \"v\",\n        \"γ\": \"g\",\n        \"δ\": \"d\",\n        \"ε\": \"e\",\n        \"ζ\": \"z\",\n        \"η\": \"i\",\n        \"θ\": \"th\",\n        \"ι\": \"i\",\n        \"κ\": \"k\",\n        \"λ\": \"l\",\n        \"μ\": \"m\",\n        \"ν\": \"n\",\n        \"ξ\": \"ks\",\n        \"ο\": \"o\",\n        \"π\": \"p\",\n        \"ρ\": \"r\",\n        \"σ\": \"s\",\n        \"τ\": \"t\",\n        \"υ\": \"y\",\n        \"φ\": \"f\",\n        \"χ\": \"x\",\n        \"ψ\": \"ps\",\n        \"ω\": \"o\",\n        \"ά\": \"a\",\n        \"έ\": \"e\",\n        \"ί\": \"i\",\n        \"ό\": \"o\",\n        \"ύ\": \"y\",\n        \"ή\": \"i\",\n        \"ώ\": \"o\",\n        \"ς\": \"s\",\n        \"ϊ\": \"i\",\n        \"ΰ\": \"y\",\n        \"ϋ\": \"y\",\n        \"ΐ\": \"i\",\n        \"Α\": \"A\",\n        \"Β\": \"B\",\n        \"Γ\": \"G\",\n        \"Δ\": \"D\",\n        \"Ε\": \"E\",\n        \"Ζ\": \"Z\",\n        \"Η\": \"I\",\n        \"Θ\": \"TH\",\n        \"Ι\": \"I\",\n        \"Κ\": \"K\",\n        \"Λ\": \"L\",\n        \"Μ\": \"M\",\n        \"Ν\": \"N\",\n        \"Ξ\": \"KS\",\n        \"Ο\": \"O\",\n        \"Π\": \"P\",\n        \"Ρ\": \"R\",\n        \"Σ\": \"S\",\n        \"Τ\": \"T\",\n        \"Υ\": \"Y\",\n        \"Φ\": \"F\",\n        \"Χ\": \"X\",\n        \"Ψ\": \"PS\",\n        \"Ω\": \"O\",\n        \"Ά\": \"A\",\n        \"Έ\": \"E\",\n        \"Ί\": \"I\",\n        \"Ό\": \"O\",\n        \"Ύ\": \"Y\",\n        \"Ή\": \"I\",\n        \"Ώ\": \"O\",\n        \"Ϊ\": \"I\",\n        \"Ϋ\": \"Y\",\n        // Latvian\n        \"ā\": \"a\",\n        // 'č': 'c', // duplicate\n        \"ē\": \"e\",\n        \"ģ\": \"g\",\n        \"ī\": \"i\",\n        \"ķ\": \"k\",\n        \"ļ\": \"l\",\n        \"ņ\": \"n\",\n        // 'š': 's', // duplicate\n        \"ū\": \"u\",\n        // 'ž': 'z', // duplicate\n        \"Ā\": \"A\",\n        // 'Č': 'C', // duplicate\n        \"Ē\": \"E\",\n        \"Ģ\": \"G\",\n        \"Ī\": \"I\",\n        \"Ķ\": \"k\",\n        \"Ļ\": \"L\",\n        \"Ņ\": \"N\",\n        // 'Š': 'S', // duplicate\n        \"Ū\": \"U\",\n        // 'Ž': 'Z', // duplicate\n        // Macedonian\n        \"Ќ\": \"Kj\",\n        \"ќ\": \"kj\",\n        \"Љ\": \"Lj\",\n        \"љ\": \"lj\",\n        \"Њ\": \"Nj\",\n        \"њ\": \"nj\",\n        \"Тс\": \"Ts\",\n        \"тс\": \"ts\",\n        // Polish\n        \"ą\": \"a\",\n        \"ć\": \"c\",\n        \"ę\": \"e\",\n        \"ł\": \"l\",\n        \"ń\": \"n\",\n        // 'ó': 'o', // duplicate\n        \"ś\": \"s\",\n        \"ź\": \"z\",\n        \"ż\": \"z\",\n        \"Ą\": \"A\",\n        \"Ć\": \"C\",\n        \"Ę\": \"E\",\n        \"Ł\": \"L\",\n        \"Ń\": \"N\",\n        \"Ś\": \"S\",\n        \"Ź\": \"Z\",\n        \"Ż\": \"Z\",\n        // Ukranian\n        \"Є\": \"Ye\",\n        \"І\": \"I\",\n        \"Ї\": \"Yi\",\n        \"Ґ\": \"G\",\n        \"є\": \"ye\",\n        \"і\": \"i\",\n        \"ї\": \"yi\",\n        \"ґ\": \"g\",\n        // Romanian\n        \"ă\": \"a\",\n        \"Ă\": \"A\",\n        \"ș\": \"s\",\n        \"Ș\": \"S\",\n        // 'ş': 's', // duplicate\n        // 'Ş': 'S', // duplicate\n        \"ț\": \"t\",\n        \"Ț\": \"T\",\n        \"ţ\": \"t\",\n        \"Ţ\": \"T\",\n        // Russian https://en.wikipedia.org/wiki/Romanization_of_Russian\n        // ICAO\n        \"а\": \"a\",\n        \"б\": \"b\",\n        \"в\": \"v\",\n        \"г\": \"g\",\n        \"д\": \"d\",\n        \"е\": \"e\",\n        \"ё\": \"yo\",\n        \"ж\": \"zh\",\n        \"з\": \"z\",\n        \"и\": \"i\",\n        \"й\": \"i\",\n        \"к\": \"k\",\n        \"л\": \"l\",\n        \"м\": \"m\",\n        \"н\": \"n\",\n        \"о\": \"o\",\n        \"п\": \"p\",\n        \"р\": \"r\",\n        \"с\": \"s\",\n        \"т\": \"t\",\n        \"у\": \"u\",\n        \"ф\": \"f\",\n        \"х\": \"kh\",\n        \"ц\": \"c\",\n        \"ч\": \"ch\",\n        \"ш\": \"sh\",\n        \"щ\": \"sh\",\n        \"ъ\": \"\",\n        \"ы\": \"y\",\n        \"ь\": \"\",\n        \"э\": \"e\",\n        \"ю\": \"yu\",\n        \"я\": \"ya\",\n        \"А\": \"A\",\n        \"Б\": \"B\",\n        \"В\": \"V\",\n        \"Г\": \"G\",\n        \"Д\": \"D\",\n        \"Е\": \"E\",\n        \"Ё\": \"Yo\",\n        \"Ж\": \"Zh\",\n        \"З\": \"Z\",\n        \"И\": \"I\",\n        \"Й\": \"I\",\n        \"К\": \"K\",\n        \"Л\": \"L\",\n        \"М\": \"M\",\n        \"Н\": \"N\",\n        \"О\": \"O\",\n        \"П\": \"P\",\n        \"Р\": \"R\",\n        \"С\": \"S\",\n        \"Т\": \"T\",\n        \"У\": \"U\",\n        \"Ф\": \"F\",\n        \"Х\": \"Kh\",\n        \"Ц\": \"C\",\n        \"Ч\": \"Ch\",\n        \"Ш\": \"Sh\",\n        \"Щ\": \"Sh\",\n        \"Ъ\": \"\",\n        \"Ы\": \"Y\",\n        \"Ь\": \"\",\n        \"Э\": \"E\",\n        \"Ю\": \"Yu\",\n        \"Я\": \"Ya\",\n        // Serbian\n        \"ђ\": \"dj\",\n        \"ј\": \"j\",\n        // 'љ': 'lj',  // duplicate\n        // 'њ': 'nj', // duplicate\n        \"ћ\": \"c\",\n        \"џ\": \"dz\",\n        \"Ђ\": \"Dj\",\n        \"Ј\": \"j\",\n        // 'Љ': 'Lj', // duplicate\n        // 'Њ': 'Nj', // duplicate\n        \"Ћ\": \"C\",\n        \"Џ\": \"Dz\",\n        // Slovak\n        \"ľ\": \"l\",\n        \"ĺ\": \"l\",\n        \"ŕ\": \"r\",\n        \"Ľ\": \"L\",\n        \"Ĺ\": \"L\",\n        \"Ŕ\": \"R\",\n        // Turkish\n        \"ş\": \"s\",\n        \"Ş\": \"S\",\n        \"ı\": \"i\",\n        \"İ\": \"I\",\n        // 'ç': 'c', // duplicate\n        // 'Ç': 'C', // duplicate\n        // 'ü': 'u', // duplicate, see langCharMap\n        // 'Ü': 'U', // duplicate, see langCharMap\n        // 'ö': 'o', // duplicate, see langCharMap\n        // 'Ö': 'O', // duplicate, see langCharMap\n        \"ğ\": \"g\",\n        \"Ğ\": \"G\",\n        // Vietnamese\n        \"ả\": \"a\",\n        \"Ả\": \"A\",\n        \"ẳ\": \"a\",\n        \"Ẳ\": \"A\",\n        \"ẩ\": \"a\",\n        \"Ẩ\": \"A\",\n        \"đ\": \"d\",\n        \"Đ\": \"D\",\n        \"ẹ\": \"e\",\n        \"Ẹ\": \"E\",\n        \"ẽ\": \"e\",\n        \"Ẽ\": \"E\",\n        \"ẻ\": \"e\",\n        \"Ẻ\": \"E\",\n        \"ế\": \"e\",\n        \"Ế\": \"E\",\n        \"ề\": \"e\",\n        \"Ề\": \"E\",\n        \"ệ\": \"e\",\n        \"Ệ\": \"E\",\n        \"ễ\": \"e\",\n        \"Ễ\": \"E\",\n        \"ể\": \"e\",\n        \"Ể\": \"E\",\n        \"ỏ\": \"o\",\n        \"ọ\": \"o\",\n        \"Ọ\": \"o\",\n        \"ố\": \"o\",\n        \"Ố\": \"O\",\n        \"ồ\": \"o\",\n        \"Ồ\": \"O\",\n        \"ổ\": \"o\",\n        \"Ổ\": \"O\",\n        \"ộ\": \"o\",\n        \"Ộ\": \"O\",\n        \"ỗ\": \"o\",\n        \"Ỗ\": \"O\",\n        \"ơ\": \"o\",\n        \"Ơ\": \"O\",\n        \"ớ\": \"o\",\n        \"Ớ\": \"O\",\n        \"ờ\": \"o\",\n        \"Ờ\": \"O\",\n        \"ợ\": \"o\",\n        \"Ợ\": \"O\",\n        \"ỡ\": \"o\",\n        \"Ỡ\": \"O\",\n        \"Ở\": \"o\",\n        \"ở\": \"o\",\n        \"ị\": \"i\",\n        \"Ị\": \"I\",\n        \"ĩ\": \"i\",\n        \"Ĩ\": \"I\",\n        \"ỉ\": \"i\",\n        \"Ỉ\": \"i\",\n        \"ủ\": \"u\",\n        \"Ủ\": \"U\",\n        \"ụ\": \"u\",\n        \"Ụ\": \"U\",\n        \"ũ\": \"u\",\n        \"Ũ\": \"U\",\n        \"ư\": \"u\",\n        \"Ư\": \"U\",\n        \"ứ\": \"u\",\n        \"Ứ\": \"U\",\n        \"ừ\": \"u\",\n        \"Ừ\": \"U\",\n        \"ự\": \"u\",\n        \"Ự\": \"U\",\n        \"ữ\": \"u\",\n        \"Ữ\": \"U\",\n        \"ử\": \"u\",\n        \"Ử\": \"ư\",\n        \"ỷ\": \"y\",\n        \"Ỷ\": \"y\",\n        \"ỳ\": \"y\",\n        \"Ỳ\": \"Y\",\n        \"ỵ\": \"y\",\n        \"Ỵ\": \"Y\",\n        \"ỹ\": \"y\",\n        \"Ỹ\": \"Y\",\n        \"ạ\": \"a\",\n        \"Ạ\": \"A\",\n        \"ấ\": \"a\",\n        \"Ấ\": \"A\",\n        \"ầ\": \"a\",\n        \"Ầ\": \"A\",\n        \"ậ\": \"a\",\n        \"Ậ\": \"A\",\n        \"ẫ\": \"a\",\n        \"Ẫ\": \"A\",\n        // 'ă': 'a', // duplicate\n        // 'Ă': 'A', // duplicate\n        \"ắ\": \"a\",\n        \"Ắ\": \"A\",\n        \"ằ\": \"a\",\n        \"Ằ\": \"A\",\n        \"ặ\": \"a\",\n        \"Ặ\": \"A\",\n        \"ẵ\": \"a\",\n        \"Ẵ\": \"A\",\n        \"⓪\": \"0\",\n        \"①\": \"1\",\n        \"②\": \"2\",\n        \"③\": \"3\",\n        \"④\": \"4\",\n        \"⑤\": \"5\",\n        \"⑥\": \"6\",\n        \"⑦\": \"7\",\n        \"⑧\": \"8\",\n        \"⑨\": \"9\",\n        \"⑩\": \"10\",\n        \"⑪\": \"11\",\n        \"⑫\": \"12\",\n        \"⑬\": \"13\",\n        \"⑭\": \"14\",\n        \"⑮\": \"15\",\n        \"⑯\": \"16\",\n        \"⑰\": \"17\",\n        \"⑱\": \"18\",\n        \"⑲\": \"18\",\n        \"⑳\": \"18\",\n        \"⓵\": \"1\",\n        \"⓶\": \"2\",\n        \"⓷\": \"3\",\n        \"⓸\": \"4\",\n        \"⓹\": \"5\",\n        \"⓺\": \"6\",\n        \"⓻\": \"7\",\n        \"⓼\": \"8\",\n        \"⓽\": \"9\",\n        \"⓾\": \"10\",\n        \"⓿\": \"0\",\n        \"⓫\": \"11\",\n        \"⓬\": \"12\",\n        \"⓭\": \"13\",\n        \"⓮\": \"14\",\n        \"⓯\": \"15\",\n        \"⓰\": \"16\",\n        \"⓱\": \"17\",\n        \"⓲\": \"18\",\n        \"⓳\": \"19\",\n        \"⓴\": \"20\",\n        \"Ⓐ\": \"A\",\n        \"Ⓑ\": \"B\",\n        \"Ⓒ\": \"C\",\n        \"Ⓓ\": \"D\",\n        \"Ⓔ\": \"E\",\n        \"Ⓕ\": \"F\",\n        \"Ⓖ\": \"G\",\n        \"Ⓗ\": \"H\",\n        \"Ⓘ\": \"I\",\n        \"Ⓙ\": \"J\",\n        \"Ⓚ\": \"K\",\n        \"Ⓛ\": \"L\",\n        \"Ⓜ\": \"M\",\n        \"Ⓝ\": \"N\",\n        \"Ⓞ\": \"O\",\n        \"Ⓟ\": \"P\",\n        \"Ⓠ\": \"Q\",\n        \"Ⓡ\": \"R\",\n        \"Ⓢ\": \"S\",\n        \"Ⓣ\": \"T\",\n        \"Ⓤ\": \"U\",\n        \"Ⓥ\": \"V\",\n        \"Ⓦ\": \"W\",\n        \"Ⓧ\": \"X\",\n        \"Ⓨ\": \"Y\",\n        \"Ⓩ\": \"Z\",\n        \"ⓐ\": \"a\",\n        \"ⓑ\": \"b\",\n        \"ⓒ\": \"c\",\n        \"ⓓ\": \"d\",\n        \"ⓔ\": \"e\",\n        \"ⓕ\": \"f\",\n        \"ⓖ\": \"g\",\n        \"ⓗ\": \"h\",\n        \"ⓘ\": \"i\",\n        \"ⓙ\": \"j\",\n        \"ⓚ\": \"k\",\n        \"ⓛ\": \"l\",\n        \"ⓜ\": \"m\",\n        \"ⓝ\": \"n\",\n        \"ⓞ\": \"o\",\n        \"ⓟ\": \"p\",\n        \"ⓠ\": \"q\",\n        \"ⓡ\": \"r\",\n        \"ⓢ\": \"s\",\n        \"ⓣ\": \"t\",\n        \"ⓤ\": \"u\",\n        \"ⓦ\": \"v\",\n        \"ⓥ\": \"w\",\n        \"ⓧ\": \"x\",\n        \"ⓨ\": \"y\",\n        \"ⓩ\": \"z\",\n        // symbols\n        \"“\": '\"',\n        \"”\": '\"',\n        \"‘\": \"'\",\n        \"’\": \"'\",\n        \"∂\": \"d\",\n        \"ƒ\": \"f\",\n        \"™\": \"(TM)\",\n        \"©\": \"(C)\",\n        \"œ\": \"oe\",\n        \"Œ\": \"OE\",\n        \"®\": \"(R)\",\n        \"†\": \"+\",\n        \"℠\": \"(SM)\",\n        \"…\": \"...\",\n        \"˚\": \"o\",\n        \"º\": \"o\",\n        \"ª\": \"a\",\n        \"•\": \"*\",\n        \"၊\": \",\",\n        \"။\": \".\",\n        // currency\n        \"$\": \"USD\",\n        \"€\": \"EUR\",\n        \"₢\": \"BRN\",\n        \"₣\": \"FRF\",\n        \"£\": \"GBP\",\n        \"₤\": \"ITL\",\n        \"₦\": \"NGN\",\n        \"₧\": \"ESP\",\n        \"₩\": \"KRW\",\n        \"₪\": \"ILS\",\n        \"₫\": \"VND\",\n        \"₭\": \"LAK\",\n        \"₮\": \"MNT\",\n        \"₯\": \"GRD\",\n        \"₱\": \"ARS\",\n        \"₲\": \"PYG\",\n        \"₳\": \"ARA\",\n        \"₴\": \"UAH\",\n        \"₵\": \"GHS\",\n        \"¢\": \"cent\",\n        \"¥\": \"CNY\",\n        \"元\": \"CNY\",\n        \"円\": \"YEN\",\n        \"﷼\": \"IRR\",\n        \"₠\": \"EWE\",\n        \"฿\": \"THB\",\n        \"₨\": \"INR\",\n        \"₹\": \"INR\",\n        \"₰\": \"PF\",\n        \"₺\": \"TRY\",\n        \"؋\": \"AFN\",\n        \"₼\": \"AZN\",\n        \"лв\": \"BGN\",\n        \"៛\": \"KHR\",\n        \"₡\": \"CRC\",\n        \"₸\": \"KZT\",\n        \"ден\": \"MKD\",\n        \"zł\": \"PLN\",\n        \"₽\": \"RUB\",\n        \"₾\": \"GEL\"\n      };\n      var lookAheadCharArray = [\n        // burmese\n        \"်\",\n        // Dhivehi\n        \"ް\"\n      ];\n      var diatricMap = {\n        // Burmese\n        // dependent vowels\n        \"ာ\": \"a\",\n        \"ါ\": \"a\",\n        \"ေ\": \"e\",\n        \"ဲ\": \"e\",\n        \"ိ\": \"i\",\n        \"ီ\": \"i\",\n        \"ို\": \"o\",\n        \"ု\": \"u\",\n        \"ူ\": \"u\",\n        \"ေါင်\": \"aung\",\n        \"ော\": \"aw\",\n        \"ော်\": \"aw\",\n        \"ေါ\": \"aw\",\n        \"ေါ်\": \"aw\",\n        \"်\": \"်\",\n        // this is special case but the character will be converted to latin in the code\n        \"က်\": \"et\",\n        \"ိုက်\": \"aik\",\n        \"ောက်\": \"auk\",\n        \"င်\": \"in\",\n        \"ိုင်\": \"aing\",\n        \"ောင်\": \"aung\",\n        \"စ်\": \"it\",\n        \"ည်\": \"i\",\n        \"တ်\": \"at\",\n        \"ိတ်\": \"eik\",\n        \"ုတ်\": \"ok\",\n        \"ွတ်\": \"ut\",\n        \"ေတ်\": \"it\",\n        \"ဒ်\": \"d\",\n        \"ိုဒ်\": \"ok\",\n        \"ုဒ်\": \"ait\",\n        \"န်\": \"an\",\n        \"ာန်\": \"an\",\n        \"ိန်\": \"ein\",\n        \"ုန်\": \"on\",\n        \"ွန်\": \"un\",\n        \"ပ်\": \"at\",\n        \"ိပ်\": \"eik\",\n        \"ုပ်\": \"ok\",\n        \"ွပ်\": \"ut\",\n        \"န်ုပ်\": \"nub\",\n        \"မ်\": \"an\",\n        \"ိမ်\": \"ein\",\n        \"ုမ်\": \"on\",\n        \"ွမ်\": \"un\",\n        \"ယ်\": \"e\",\n        \"ိုလ်\": \"ol\",\n        \"ဉ်\": \"in\",\n        \"ံ\": \"an\",\n        \"ိံ\": \"ein\",\n        \"ုံ\": \"on\",\n        // Dhivehi\n        \"ައް\": \"ah\",\n        \"ަށް\": \"ah\"\n      };\n      var langCharMap = {\n        \"en\": {},\n        // default language\n        \"az\": {\n          // Azerbaijani\n          \"ç\": \"c\",\n          \"ə\": \"e\",\n          \"ğ\": \"g\",\n          \"ı\": \"i\",\n          \"ö\": \"o\",\n          \"ş\": \"s\",\n          \"ü\": \"u\",\n          \"Ç\": \"C\",\n          \"Ə\": \"E\",\n          \"Ğ\": \"G\",\n          \"İ\": \"I\",\n          \"Ö\": \"O\",\n          \"Ş\": \"S\",\n          \"Ü\": \"U\"\n        },\n        \"cs\": {\n          // Czech\n          \"č\": \"c\",\n          \"ď\": \"d\",\n          \"ě\": \"e\",\n          \"ň\": \"n\",\n          \"ř\": \"r\",\n          \"š\": \"s\",\n          \"ť\": \"t\",\n          \"ů\": \"u\",\n          \"ž\": \"z\",\n          \"Č\": \"C\",\n          \"Ď\": \"D\",\n          \"Ě\": \"E\",\n          \"Ň\": \"N\",\n          \"Ř\": \"R\",\n          \"Š\": \"S\",\n          \"Ť\": \"T\",\n          \"Ů\": \"U\",\n          \"Ž\": \"Z\"\n        },\n        \"fi\": {\n          // Finnish\n          // 'å': 'a', duplicate see charMap/latin\n          // 'Å': 'A', duplicate see charMap/latin\n          \"ä\": \"a\",\n          // ok\n          \"Ä\": \"A\",\n          // ok\n          \"ö\": \"o\",\n          // ok\n          \"Ö\": \"O\"\n          // ok\n        },\n        \"hu\": {\n          // Hungarian\n          \"ä\": \"a\",\n          // ok\n          \"Ä\": \"A\",\n          // ok\n          // 'á': 'a', duplicate see charMap/latin\n          // 'Á': 'A', duplicate see charMap/latin\n          \"ö\": \"o\",\n          // ok\n          \"Ö\": \"O\",\n          // ok\n          // 'ő': 'o', duplicate see charMap/latin\n          // 'Ő': 'O', duplicate see charMap/latin\n          \"ü\": \"u\",\n          \"Ü\": \"U\",\n          \"ű\": \"u\",\n          \"Ű\": \"U\"\n        },\n        \"lt\": {\n          // Lithuanian\n          \"ą\": \"a\",\n          \"č\": \"c\",\n          \"ę\": \"e\",\n          \"ė\": \"e\",\n          \"į\": \"i\",\n          \"š\": \"s\",\n          \"ų\": \"u\",\n          \"ū\": \"u\",\n          \"ž\": \"z\",\n          \"Ą\": \"A\",\n          \"Č\": \"C\",\n          \"Ę\": \"E\",\n          \"Ė\": \"E\",\n          \"Į\": \"I\",\n          \"Š\": \"S\",\n          \"Ų\": \"U\",\n          \"Ū\": \"U\"\n        },\n        \"lv\": {\n          // Latvian\n          \"ā\": \"a\",\n          \"č\": \"c\",\n          \"ē\": \"e\",\n          \"ģ\": \"g\",\n          \"ī\": \"i\",\n          \"ķ\": \"k\",\n          \"ļ\": \"l\",\n          \"ņ\": \"n\",\n          \"š\": \"s\",\n          \"ū\": \"u\",\n          \"ž\": \"z\",\n          \"Ā\": \"A\",\n          \"Č\": \"C\",\n          \"Ē\": \"E\",\n          \"Ģ\": \"G\",\n          \"Ī\": \"i\",\n          \"Ķ\": \"k\",\n          \"Ļ\": \"L\",\n          \"Ņ\": \"N\",\n          \"Š\": \"S\",\n          \"Ū\": \"u\",\n          \"Ž\": \"Z\"\n        },\n        \"pl\": {\n          // Polish\n          \"ą\": \"a\",\n          \"ć\": \"c\",\n          \"ę\": \"e\",\n          \"ł\": \"l\",\n          \"ń\": \"n\",\n          \"ó\": \"o\",\n          \"ś\": \"s\",\n          \"ź\": \"z\",\n          \"ż\": \"z\",\n          \"Ą\": \"A\",\n          \"Ć\": \"C\",\n          \"Ę\": \"e\",\n          \"Ł\": \"L\",\n          \"Ń\": \"N\",\n          \"Ó\": \"O\",\n          \"Ś\": \"S\",\n          \"Ź\": \"Z\",\n          \"Ż\": \"Z\"\n        },\n        \"sv\": {\n          // Swedish\n          // 'å': 'a', duplicate see charMap/latin\n          // 'Å': 'A', duplicate see charMap/latin\n          \"ä\": \"a\",\n          // ok\n          \"Ä\": \"A\",\n          // ok\n          \"ö\": \"o\",\n          // ok\n          \"Ö\": \"O\"\n          // ok\n        },\n        \"sk\": {\n          // Slovak\n          \"ä\": \"a\",\n          \"Ä\": \"A\"\n        },\n        \"sr\": {\n          // Serbian\n          \"љ\": \"lj\",\n          \"њ\": \"nj\",\n          \"Љ\": \"Lj\",\n          \"Њ\": \"Nj\",\n          \"đ\": \"dj\",\n          \"Đ\": \"Dj\"\n        },\n        \"tr\": {\n          // Turkish\n          \"Ü\": \"U\",\n          \"Ö\": \"O\",\n          \"ü\": \"u\",\n          \"ö\": \"o\"\n        }\n      };\n      var symbolMap = {\n        \"ar\": {\n          \"∆\": \"delta\",\n          \"∞\": \"la-nihaya\",\n          \"♥\": \"hob\",\n          \"&\": \"wa\",\n          \"|\": \"aw\",\n          \"<\": \"aqal-men\",\n          \">\": \"akbar-men\",\n          \"∑\": \"majmou\",\n          \"¤\": \"omla\"\n        },\n        \"az\": {},\n        \"ca\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinit\",\n          \"♥\": \"amor\",\n          \"&\": \"i\",\n          \"|\": \"o\",\n          \"<\": \"menys que\",\n          \">\": \"mes que\",\n          \"∑\": \"suma dels\",\n          \"¤\": \"moneda\"\n        },\n        \"cs\": {\n          \"∆\": \"delta\",\n          \"∞\": \"nekonecno\",\n          \"♥\": \"laska\",\n          \"&\": \"a\",\n          \"|\": \"nebo\",\n          \"<\": \"mensi nez\",\n          \">\": \"vetsi nez\",\n          \"∑\": \"soucet\",\n          \"¤\": \"mena\"\n        },\n        \"de\": {\n          \"∆\": \"delta\",\n          \"∞\": \"unendlich\",\n          \"♥\": \"Liebe\",\n          \"&\": \"und\",\n          \"|\": \"oder\",\n          \"<\": \"kleiner als\",\n          \">\": \"groesser als\",\n          \"∑\": \"Summe von\",\n          \"¤\": \"Waehrung\"\n        },\n        \"dv\": {\n          \"∆\": \"delta\",\n          \"∞\": \"kolunulaa\",\n          \"♥\": \"loabi\",\n          \"&\": \"aai\",\n          \"|\": \"noonee\",\n          \"<\": \"ah vure kuda\",\n          \">\": \"ah vure bodu\",\n          \"∑\": \"jumula\",\n          \"¤\": \"faisaa\"\n        },\n        \"en\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinity\",\n          \"♥\": \"love\",\n          \"&\": \"and\",\n          \"|\": \"or\",\n          \"<\": \"less than\",\n          \">\": \"greater than\",\n          \"∑\": \"sum\",\n          \"¤\": \"currency\"\n        },\n        \"es\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinito\",\n          \"♥\": \"amor\",\n          \"&\": \"y\",\n          \"|\": \"u\",\n          \"<\": \"menos que\",\n          \">\": \"mas que\",\n          \"∑\": \"suma de los\",\n          \"¤\": \"moneda\"\n        },\n        \"fa\": {\n          \"∆\": \"delta\",\n          \"∞\": \"bi-nahayat\",\n          \"♥\": \"eshgh\",\n          \"&\": \"va\",\n          \"|\": \"ya\",\n          \"<\": \"kamtar-az\",\n          \">\": \"bishtar-az\",\n          \"∑\": \"majmooe\",\n          \"¤\": \"vahed\"\n        },\n        \"fi\": {\n          \"∆\": \"delta\",\n          \"∞\": \"aarettomyys\",\n          \"♥\": \"rakkaus\",\n          \"&\": \"ja\",\n          \"|\": \"tai\",\n          \"<\": \"pienempi kuin\",\n          \">\": \"suurempi kuin\",\n          \"∑\": \"summa\",\n          \"¤\": \"valuutta\"\n        },\n        \"fr\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infiniment\",\n          \"♥\": \"Amour\",\n          \"&\": \"et\",\n          \"|\": \"ou\",\n          \"<\": \"moins que\",\n          \">\": \"superieure a\",\n          \"∑\": \"somme des\",\n          \"¤\": \"monnaie\"\n        },\n        \"ge\": {\n          \"∆\": \"delta\",\n          \"∞\": \"usasruloba\",\n          \"♥\": \"siqvaruli\",\n          \"&\": \"da\",\n          \"|\": \"an\",\n          \"<\": \"naklebi\",\n          \">\": \"meti\",\n          \"∑\": \"jami\",\n          \"¤\": \"valuta\"\n        },\n        \"gr\": {},\n        \"hu\": {\n          \"∆\": \"delta\",\n          \"∞\": \"vegtelen\",\n          \"♥\": \"szerelem\",\n          \"&\": \"es\",\n          \"|\": \"vagy\",\n          \"<\": \"kisebb mint\",\n          \">\": \"nagyobb mint\",\n          \"∑\": \"szumma\",\n          \"¤\": \"penznem\"\n        },\n        \"it\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinito\",\n          \"♥\": \"amore\",\n          \"&\": \"e\",\n          \"|\": \"o\",\n          \"<\": \"minore di\",\n          \">\": \"maggiore di\",\n          \"∑\": \"somma\",\n          \"¤\": \"moneta\"\n        },\n        \"lt\": {\n          \"∆\": \"delta\",\n          \"∞\": \"begalybe\",\n          \"♥\": \"meile\",\n          \"&\": \"ir\",\n          \"|\": \"ar\",\n          \"<\": \"maziau nei\",\n          \">\": \"daugiau nei\",\n          \"∑\": \"suma\",\n          \"¤\": \"valiuta\"\n        },\n        \"lv\": {\n          \"∆\": \"delta\",\n          \"∞\": \"bezgaliba\",\n          \"♥\": \"milestiba\",\n          \"&\": \"un\",\n          \"|\": \"vai\",\n          \"<\": \"mazak neka\",\n          \">\": \"lielaks neka\",\n          \"∑\": \"summa\",\n          \"¤\": \"valuta\"\n        },\n        \"my\": {\n          \"∆\": \"kwahkhyaet\",\n          \"∞\": \"asaonasme\",\n          \"♥\": \"akhyait\",\n          \"&\": \"nhin\",\n          \"|\": \"tho\",\n          \"<\": \"ngethaw\",\n          \">\": \"kyithaw\",\n          \"∑\": \"paungld\",\n          \"¤\": \"ngwekye\"\n        },\n        \"mk\": {},\n        \"nl\": {\n          \"∆\": \"delta\",\n          \"∞\": \"oneindig\",\n          \"♥\": \"liefde\",\n          \"&\": \"en\",\n          \"|\": \"of\",\n          \"<\": \"kleiner dan\",\n          \">\": \"groter dan\",\n          \"∑\": \"som\",\n          \"¤\": \"valuta\"\n        },\n        \"pl\": {\n          \"∆\": \"delta\",\n          \"∞\": \"nieskonczonosc\",\n          \"♥\": \"milosc\",\n          \"&\": \"i\",\n          \"|\": \"lub\",\n          \"<\": \"mniejsze niz\",\n          \">\": \"wieksze niz\",\n          \"∑\": \"suma\",\n          \"¤\": \"waluta\"\n        },\n        \"pt\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinito\",\n          \"♥\": \"amor\",\n          \"&\": \"e\",\n          \"|\": \"ou\",\n          \"<\": \"menor que\",\n          \">\": \"maior que\",\n          \"∑\": \"soma\",\n          \"¤\": \"moeda\"\n        },\n        \"ro\": {\n          \"∆\": \"delta\",\n          \"∞\": \"infinit\",\n          \"♥\": \"dragoste\",\n          \"&\": \"si\",\n          \"|\": \"sau\",\n          \"<\": \"mai mic ca\",\n          \">\": \"mai mare ca\",\n          \"∑\": \"suma\",\n          \"¤\": \"valuta\"\n        },\n        \"ru\": {\n          \"∆\": \"delta\",\n          \"∞\": \"beskonechno\",\n          \"♥\": \"lubov\",\n          \"&\": \"i\",\n          \"|\": \"ili\",\n          \"<\": \"menshe\",\n          \">\": \"bolshe\",\n          \"∑\": \"summa\",\n          \"¤\": \"valjuta\"\n        },\n        \"sk\": {\n          \"∆\": \"delta\",\n          \"∞\": \"nekonecno\",\n          \"♥\": \"laska\",\n          \"&\": \"a\",\n          \"|\": \"alebo\",\n          \"<\": \"menej ako\",\n          \">\": \"viac ako\",\n          \"∑\": \"sucet\",\n          \"¤\": \"mena\"\n        },\n        \"sr\": {},\n        \"tr\": {\n          \"∆\": \"delta\",\n          \"∞\": \"sonsuzluk\",\n          \"♥\": \"ask\",\n          \"&\": \"ve\",\n          \"|\": \"veya\",\n          \"<\": \"kucuktur\",\n          \">\": \"buyuktur\",\n          \"∑\": \"toplam\",\n          \"¤\": \"para birimi\"\n        },\n        \"uk\": {\n          \"∆\": \"delta\",\n          \"∞\": \"bezkinechnist\",\n          \"♥\": \"lubov\",\n          \"&\": \"i\",\n          \"|\": \"abo\",\n          \"<\": \"menshe\",\n          \">\": \"bilshe\",\n          \"∑\": \"suma\",\n          \"¤\": \"valjuta\"\n        },\n        \"vn\": {\n          \"∆\": \"delta\",\n          \"∞\": \"vo cuc\",\n          \"♥\": \"yeu\",\n          \"&\": \"va\",\n          \"|\": \"hoac\",\n          \"<\": \"nho hon\",\n          \">\": \"lon hon\",\n          \"∑\": \"tong\",\n          \"¤\": \"tien te\"\n        }\n      };\n      var uricChars = [\";\", \"?\", \":\", \"@\", \"&\", \"=\", \"+\", \"$\", \",\", \"/\"].join(\"\");\n      var uricNoSlashChars = [\";\", \"?\", \":\", \"@\", \"&\", \"=\", \"+\", \"$\", \",\"].join(\"\");\n      var markChars = [\".\", \"!\", \"~\", \"*\", \"'\", \"(\", \")\"].join(\"\");\n      var getSlug = function getSlug2(input, opts) {\n        var separator = \"-\";\n        var result = \"\";\n        var diatricString = \"\";\n        var convertSymbols = true;\n        var customReplacements = {};\n        var maintainCase;\n        var titleCase;\n        var truncate;\n        var uricFlag;\n        var uricNoSlashFlag;\n        var markFlag;\n        var symbol;\n        var langChar;\n        var lucky;\n        var i;\n        var ch;\n        var l;\n        var lastCharWasSymbol;\n        var lastCharWasDiatric;\n        var allowedChars = \"\";\n        if (typeof input !== \"string\") {\n          return \"\";\n        }\n        if (typeof opts === \"string\") {\n          separator = opts;\n        }\n        symbol = symbolMap.en;\n        langChar = langCharMap.en;\n        if (typeof opts === \"object\") {\n          maintainCase = opts.maintainCase || false;\n          customReplacements = opts.custom && typeof opts.custom === \"object\" ? opts.custom : customReplacements;\n          truncate = +opts.truncate > 1 && opts.truncate || false;\n          uricFlag = opts.uric || false;\n          uricNoSlashFlag = opts.uricNoSlash || false;\n          markFlag = opts.mark || false;\n          convertSymbols = opts.symbols === false || opts.lang === false ? false : true;\n          separator = opts.separator || separator;\n          if (uricFlag) {\n            allowedChars += uricChars;\n          }\n          if (uricNoSlashFlag) {\n            allowedChars += uricNoSlashChars;\n          }\n          if (markFlag) {\n            allowedChars += markChars;\n          }\n          symbol = opts.lang && symbolMap[opts.lang] && convertSymbols ? symbolMap[opts.lang] : convertSymbols ? symbolMap.en : {};\n          langChar = opts.lang && langCharMap[opts.lang] ? langCharMap[opts.lang] : opts.lang === false || opts.lang === true ? {} : langCharMap.en;\n          if (opts.titleCase && typeof opts.titleCase.length === \"number\" && Array.prototype.toString.call(opts.titleCase)) {\n            opts.titleCase.forEach(function(v) {\n              customReplacements[v + \"\"] = v + \"\";\n            });\n            titleCase = true;\n          } else {\n            titleCase = !!opts.titleCase;\n          }\n          if (opts.custom && typeof opts.custom.length === \"number\" && Array.prototype.toString.call(opts.custom)) {\n            opts.custom.forEach(function(v) {\n              customReplacements[v + \"\"] = v + \"\";\n            });\n          }\n          Object.keys(customReplacements).forEach(function(v) {\n            var r;\n            if (v.length > 1) {\n              r = new RegExp(\"\\\\b\" + escapeChars(v) + \"\\\\b\", \"gi\");\n            } else {\n              r = new RegExp(escapeChars(v), \"gi\");\n            }\n            input = input.replace(r, customReplacements[v]);\n          });\n          for (ch in customReplacements) {\n            allowedChars += ch;\n          }\n        }\n        allowedChars += separator;\n        allowedChars = escapeChars(allowedChars);\n        input = input.replace(/(^\\s+|\\s+$)/g, \"\");\n        lastCharWasSymbol = false;\n        lastCharWasDiatric = false;\n        for (i = 0, l = input.length; i < l; i++) {\n          ch = input[i];\n          if (isReplacedCustomChar(ch, customReplacements)) {\n            lastCharWasSymbol = false;\n          } else if (langChar[ch]) {\n            ch = lastCharWasSymbol && langChar[ch].match(/[A-Za-z0-9]/) ? \" \" + langChar[ch] : langChar[ch];\n            lastCharWasSymbol = false;\n          } else if (ch in charMap) {\n            if (i + 1 < l && lookAheadCharArray.indexOf(input[i + 1]) >= 0) {\n              diatricString += ch;\n              ch = \"\";\n            } else if (lastCharWasDiatric === true) {\n              ch = diatricMap[diatricString] + charMap[ch];\n              diatricString = \"\";\n            } else {\n              ch = lastCharWasSymbol && charMap[ch].match(/[A-Za-z0-9]/) ? \" \" + charMap[ch] : charMap[ch];\n            }\n            lastCharWasSymbol = false;\n            lastCharWasDiatric = false;\n          } else if (ch in diatricMap) {\n            diatricString += ch;\n            ch = \"\";\n            if (i === l - 1) {\n              ch = diatricMap[diatricString];\n            }\n            lastCharWasDiatric = true;\n          } else if (\n            // process symbol chars\n            symbol[ch] && !(uricFlag && uricChars.indexOf(ch) !== -1) && !(uricNoSlashFlag && uricNoSlashChars.indexOf(ch) !== -1)\n          ) {\n            ch = lastCharWasSymbol || result.substr(-1).match(/[A-Za-z0-9]/) ? separator + symbol[ch] : symbol[ch];\n            ch += input[i + 1] !== void 0 && input[i + 1].match(/[A-Za-z0-9]/) ? separator : \"\";\n            lastCharWasSymbol = true;\n          } else {\n            if (lastCharWasDiatric === true) {\n              ch = diatricMap[diatricString] + ch;\n              diatricString = \"\";\n              lastCharWasDiatric = false;\n            } else if (lastCharWasSymbol && (/[A-Za-z0-9]/.test(ch) || result.substr(-1).match(/A-Za-z0-9]/))) {\n              ch = \" \" + ch;\n            }\n            lastCharWasSymbol = false;\n          }\n          result += ch.replace(new RegExp(\"[^\\\\w\\\\s\" + allowedChars + \"_-]\", \"g\"), separator);\n        }\n        if (titleCase) {\n          result = result.replace(/(\\w)(\\S*)/g, function(_, i2, r) {\n            var j = i2.toUpperCase() + (r !== null ? r : \"\");\n            return Object.keys(customReplacements).indexOf(j.toLowerCase()) < 0 ? j : j.toLowerCase();\n          });\n        }\n        result = result.replace(/\\s+/g, separator).replace(new RegExp(\"\\\\\" + separator + \"+\", \"g\"), separator).replace(new RegExp(\"(^\\\\\" + separator + \"+|\\\\\" + separator + \"+$)\", \"g\"), \"\");\n        if (truncate && result.length > truncate) {\n          lucky = result.charAt(truncate) === separator;\n          result = result.slice(0, truncate);\n          if (!lucky) {\n            result = result.slice(0, result.lastIndexOf(separator));\n          }\n        }\n        if (!maintainCase && !titleCase) {\n          result = result.toLowerCase();\n        }\n        return result;\n      };\n      var createSlug = function createSlug2(opts) {\n        return function getSlugWithConfig(input) {\n          return getSlug(input, opts);\n        };\n      };\n      var escapeChars = function escapeChars2(input) {\n        return input.replace(/[-\\\\^$*+?.()|[\\]{}\\/]/g, \"\\\\$&\");\n      };\n      var isReplacedCustomChar = function(ch, customReplacements) {\n        for (var c in customReplacements) {\n          if (customReplacements[c] === ch) {\n            return true;\n          }\n        }\n      };\n      if (typeof module !== \"undefined\" && module.exports) {\n        module.exports = getSlug;\n        module.exports.createSlug = createSlug;\n      } else if (typeof define !== \"undefined\" && define.amd) {\n        define([], function() {\n          return getSlug;\n        });\n      } else {\n        try {\n          if (root.getSlug || root.createSlug) {\n            throw \"speakingurl: globals exists /(getSlug|createSlug)/\";\n          } else {\n            root.getSlug = getSlug;\n            root.createSlug = createSlug;\n          }\n        } catch (e) {\n        }\n      }\n    })(exports);\n  }\n});\nvar require_speakingurl2 = __commonJS2({\n  \"../../node_modules/.pnpm/speakingurl@14.0.1/node_modules/speakingurl/index.js\"(exports, module) {\n    \"use strict\";\n    init_esm_shims2();\n    module.exports = require_speakingurl();\n  }\n});\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nfunction getComponentTypeName(options) {\n  var _a25;\n  const name = options.name || options._componentTag || options.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__ || options.__name;\n  if (name === \"index\" && ((_a25 = options.__file) == null ? void 0 : _a25.endsWith(\"index.vue\"))) {\n    return \"\";\n  }\n  return name;\n}\nfunction getComponentFileName(options) {\n  const file = options.__file;\n  if (file)\n    return classify(basename(file, \".vue\"));\n}\nfunction saveComponentGussedName(instance, name) {\n  instance.type.__VUE_DEVTOOLS_COMPONENT_GUSSED_NAME__ = name;\n  return name;\n}\nfunction getAppRecord(instance) {\n  if (instance.__VUE_DEVTOOLS_NEXT_APP_RECORD__)\n    return instance.__VUE_DEVTOOLS_NEXT_APP_RECORD__;\n  else if (instance.root)\n    return instance.appContext.app.__VUE_DEVTOOLS_NEXT_APP_RECORD__;\n}\nfunction isFragment(instance) {\n  var _a25, _b25;\n  const subTreeType = (_a25 = instance.subTree) == null ? void 0 : _a25.type;\n  const appRecord = getAppRecord(instance);\n  if (appRecord) {\n    return ((_b25 = appRecord == null ? void 0 : appRecord.types) == null ? void 0 : _b25.Fragment) === subTreeType;\n  }\n  return false;\n}\nfunction getInstanceName(instance) {\n  var _a25, _b25, _c;\n  const name = getComponentTypeName((instance == null ? void 0 : instance.type) || {});\n  if (name)\n    return name;\n  if ((instance == null ? void 0 : instance.root) === instance)\n    return \"Root\";\n  for (const key in (_b25 = (_a25 = instance.parent) == null ? void 0 : _a25.type) == null ? void 0 : _b25.components) {\n    if (instance.parent.type.components[key] === (instance == null ? void 0 : instance.type))\n      return saveComponentGussedName(instance, key);\n  }\n  for (const key in (_c = instance.appContext) == null ? void 0 : _c.components) {\n    if (instance.appContext.components[key] === (instance == null ? void 0 : instance.type))\n      return saveComponentGussedName(instance, key);\n  }\n  const fileName = getComponentFileName((instance == null ? void 0 : instance.type) || {});\n  if (fileName)\n    return fileName;\n  return \"Anonymous Component\";\n}\nfunction getUniqueComponentId(instance) {\n  var _a25, _b25, _c;\n  const appId = (_c = (_b25 = (_a25 = instance == null ? void 0 : instance.appContext) == null ? void 0 : _a25.app) == null ? void 0 : _b25.__VUE_DEVTOOLS_NEXT_APP_RECORD_ID__) != null ? _c : 0;\n  const instanceId = instance === (instance == null ? void 0 : instance.root) ? \"root\" : instance.uid;\n  return `${appId}:${instanceId}`;\n}\nfunction getComponentInstance(appRecord, instanceId) {\n  instanceId = instanceId || `${appRecord.id}:root`;\n  const instance = appRecord.instanceMap.get(instanceId);\n  return instance || appRecord.instanceMap.get(\":root\");\n}\nfunction createRect() {\n  const rect = {\n    top: 0,\n    bottom: 0,\n    left: 0,\n    right: 0,\n    get width() {\n      return rect.right - rect.left;\n    },\n    get height() {\n      return rect.bottom - rect.top;\n    }\n  };\n  return rect;\n}\nvar range;\nfunction getTextRect(node) {\n  if (!range)\n    range = document.createRange();\n  range.selectNode(node);\n  return range.getBoundingClientRect();\n}\nfunction getFragmentRect(vnode) {\n  const rect = createRect();\n  if (!vnode.children)\n    return rect;\n  for (let i = 0, l = vnode.children.length; i < l; i++) {\n    const childVnode = vnode.children[i];\n    let childRect;\n    if (childVnode.component) {\n      childRect = getComponentBoundingRect(childVnode.component);\n    } else if (childVnode.el) {\n      const el = childVnode.el;\n      if (el.nodeType === 1 || el.getBoundingClientRect)\n        childRect = el.getBoundingClientRect();\n      else if (el.nodeType === 3 && el.data.trim())\n        childRect = getTextRect(el);\n    }\n    if (childRect)\n      mergeRects(rect, childRect);\n  }\n  return rect;\n}\nfunction mergeRects(a, b) {\n  if (!a.top || b.top < a.top)\n    a.top = b.top;\n  if (!a.bottom || b.bottom > a.bottom)\n    a.bottom = b.bottom;\n  if (!a.left || b.left < a.left)\n    a.left = b.left;\n  if (!a.right || b.right > a.right)\n    a.right = b.right;\n  return a;\n}\nvar DEFAULT_RECT = {\n  top: 0,\n  left: 0,\n  right: 0,\n  bottom: 0,\n  width: 0,\n  height: 0\n};\nfunction getComponentBoundingRect(instance) {\n  const el = instance.subTree.el;\n  if (typeof window === \"undefined\") {\n    return DEFAULT_RECT;\n  }\n  if (isFragment(instance))\n    return getFragmentRect(instance.subTree);\n  else if ((el == null ? void 0 : el.nodeType) === 1)\n    return el == null ? void 0 : el.getBoundingClientRect();\n  else if (instance.subTree.component)\n    return getComponentBoundingRect(instance.subTree.component);\n  else\n    return DEFAULT_RECT;\n}\ninit_esm_shims2();\nfunction getRootElementsFromComponentInstance(instance) {\n  if (isFragment(instance))\n    return getFragmentRootElements(instance.subTree);\n  if (!instance.subTree)\n    return [];\n  return [instance.subTree.el];\n}\nfunction getFragmentRootElements(vnode) {\n  if (!vnode.children)\n    return [];\n  const list = [];\n  vnode.children.forEach((childVnode) => {\n    if (childVnode.component)\n      list.push(...getRootElementsFromComponentInstance(childVnode.component));\n    else if (childVnode == null ? void 0 : childVnode.el)\n      list.push(childVnode.el);\n  });\n  return list;\n}\nvar CONTAINER_ELEMENT_ID = \"__vue-devtools-component-inspector__\";\nvar CARD_ELEMENT_ID = \"__vue-devtools-component-inspector__card__\";\nvar COMPONENT_NAME_ELEMENT_ID = \"__vue-devtools-component-inspector__name__\";\nvar INDICATOR_ELEMENT_ID = \"__vue-devtools-component-inspector__indicator__\";\nvar containerStyles = {\n  display: \"block\",\n  zIndex: 2147483640,\n  position: \"fixed\",\n  backgroundColor: \"#42b88325\",\n  border: \"1px solid #42b88350\",\n  borderRadius: \"5px\",\n  transition: \"all 0.1s ease-in\",\n  pointerEvents: \"none\"\n};\nvar cardStyles = {\n  fontFamily: \"Arial, Helvetica, sans-serif\",\n  padding: \"5px 8px\",\n  borderRadius: \"4px\",\n  textAlign: \"left\",\n  position: \"absolute\",\n  left: 0,\n  color: \"#e9e9e9\",\n  fontSize: \"14px\",\n  fontWeight: 600,\n  lineHeight: \"24px\",\n  backgroundColor: \"#42b883\",\n  boxShadow: \"0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)\"\n};\nvar indicatorStyles = {\n  display: \"inline-block\",\n  fontWeight: 400,\n  fontStyle: \"normal\",\n  fontSize: \"12px\",\n  opacity: 0.7\n};\nfunction getContainerElement() {\n  return document.getElementById(CONTAINER_ELEMENT_ID);\n}\nfunction getCardElement() {\n  return document.getElementById(CARD_ELEMENT_ID);\n}\nfunction getIndicatorElement() {\n  return document.getElementById(INDICATOR_ELEMENT_ID);\n}\nfunction getNameElement() {\n  return document.getElementById(COMPONENT_NAME_ELEMENT_ID);\n}\nfunction getStyles(bounds) {\n  return {\n    left: `${Math.round(bounds.left * 100) / 100}px`,\n    top: `${Math.round(bounds.top * 100) / 100}px`,\n    width: `${Math.round(bounds.width * 100) / 100}px`,\n    height: `${Math.round(bounds.height * 100) / 100}px`\n  };\n}\nfunction create(options) {\n  var _a25;\n  const containerEl = document.createElement(\"div\");\n  containerEl.id = (_a25 = options.elementId) != null ? _a25 : CONTAINER_ELEMENT_ID;\n  Object.assign(containerEl.style, {\n    ...containerStyles,\n    ...getStyles(options.bounds),\n    ...options.style\n  });\n  const cardEl = document.createElement(\"span\");\n  cardEl.id = CARD_ELEMENT_ID;\n  Object.assign(cardEl.style, {\n    ...cardStyles,\n    top: options.bounds.top < 35 ? 0 : \"-35px\"\n  });\n  const nameEl = document.createElement(\"span\");\n  nameEl.id = COMPONENT_NAME_ELEMENT_ID;\n  nameEl.innerHTML = `&lt;${options.name}&gt;&nbsp;&nbsp;`;\n  const indicatorEl = document.createElement(\"i\");\n  indicatorEl.id = INDICATOR_ELEMENT_ID;\n  indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}`;\n  Object.assign(indicatorEl.style, indicatorStyles);\n  cardEl.appendChild(nameEl);\n  cardEl.appendChild(indicatorEl);\n  containerEl.appendChild(cardEl);\n  document.body.appendChild(containerEl);\n  return containerEl;\n}\nfunction update(options) {\n  const containerEl = getContainerElement();\n  const cardEl = getCardElement();\n  const nameEl = getNameElement();\n  const indicatorEl = getIndicatorElement();\n  if (containerEl) {\n    Object.assign(containerEl.style, {\n      ...containerStyles,\n      ...getStyles(options.bounds)\n    });\n    Object.assign(cardEl.style, {\n      top: options.bounds.top < 35 ? 0 : \"-35px\"\n    });\n    nameEl.innerHTML = `&lt;${options.name}&gt;&nbsp;&nbsp;`;\n    indicatorEl.innerHTML = `${Math.round(options.bounds.width * 100) / 100} x ${Math.round(options.bounds.height * 100) / 100}`;\n  }\n}\nfunction highlight(instance) {\n  const bounds = getComponentBoundingRect(instance);\n  if (!bounds.width && !bounds.height)\n    return;\n  const name = getInstanceName(instance);\n  const container = getContainerElement();\n  container ? update({ bounds, name }) : create({ bounds, name });\n}\nfunction unhighlight() {\n  const el = getContainerElement();\n  if (el)\n    el.style.display = \"none\";\n}\nvar inspectInstance = null;\nfunction inspectFn(e) {\n  const target22 = e.target;\n  if (target22) {\n    const instance = target22.__vueParentComponent;\n    if (instance) {\n      inspectInstance = instance;\n      const el = instance.vnode.el;\n      if (el) {\n        const bounds = getComponentBoundingRect(instance);\n        const name = getInstanceName(instance);\n        const container = getContainerElement();\n        container ? update({ bounds, name }) : create({ bounds, name });\n      }\n    }\n  }\n}\nfunction selectComponentFn(e, cb) {\n  e.preventDefault();\n  e.stopPropagation();\n  if (inspectInstance) {\n    const uniqueComponentId = getUniqueComponentId(inspectInstance);\n    cb(uniqueComponentId);\n  }\n}\nvar inspectComponentHighLighterSelectFn = null;\nfunction cancelInspectComponentHighLighter() {\n  unhighlight();\n  window.removeEventListener(\"mouseover\", inspectFn);\n  window.removeEventListener(\"click\", inspectComponentHighLighterSelectFn, true);\n  inspectComponentHighLighterSelectFn = null;\n}\nfunction inspectComponentHighLighter() {\n  window.addEventListener(\"mouseover\", inspectFn);\n  return new Promise((resolve) => {\n    function onSelect(e) {\n      e.preventDefault();\n      e.stopPropagation();\n      selectComponentFn(e, (id) => {\n        window.removeEventListener(\"click\", onSelect, true);\n        inspectComponentHighLighterSelectFn = null;\n        window.removeEventListener(\"mouseover\", inspectFn);\n        const el = getContainerElement();\n        if (el)\n          el.style.display = \"none\";\n        resolve(JSON.stringify({ id }));\n      });\n    }\n    inspectComponentHighLighterSelectFn = onSelect;\n    window.addEventListener(\"click\", onSelect, true);\n  });\n}\nfunction scrollToComponent(options) {\n  const instance = getComponentInstance(activeAppRecord.value, options.id);\n  if (instance) {\n    const [el] = getRootElementsFromComponentInstance(instance);\n    if (typeof el.scrollIntoView === \"function\") {\n      el.scrollIntoView({\n        behavior: \"smooth\"\n      });\n    } else {\n      const bounds = getComponentBoundingRect(instance);\n      const scrollTarget = document.createElement(\"div\");\n      const styles = {\n        ...getStyles(bounds),\n        position: \"absolute\"\n      };\n      Object.assign(scrollTarget.style, styles);\n      document.body.appendChild(scrollTarget);\n      scrollTarget.scrollIntoView({\n        behavior: \"smooth\"\n      });\n      setTimeout(() => {\n        document.body.removeChild(scrollTarget);\n      }, 2e3);\n    }\n    setTimeout(() => {\n      const bounds = getComponentBoundingRect(instance);\n      if (bounds.width || bounds.height) {\n        const name = getInstanceName(instance);\n        const el2 = getContainerElement();\n        el2 ? update({ ...options, name, bounds }) : create({ ...options, name, bounds });\n        setTimeout(() => {\n          if (el2)\n            el2.style.display = \"none\";\n        }, 1500);\n      }\n    }, 1200);\n  }\n}\ninit_esm_shims2();\nvar _a2;\nvar _b;\n(_b = (_a2 = target).__VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__) != null ? _b : _a2.__VUE_DEVTOOLS_COMPONENT_INSPECTOR_ENABLED__ = true;\nfunction waitForInspectorInit(cb) {\n  let total = 0;\n  const timer = setInterval(() => {\n    if (target.__VUE_INSPECTOR__) {\n      clearInterval(timer);\n      total += 30;\n      cb();\n    }\n    if (total >= /* 5s */\n    5e3)\n      clearInterval(timer);\n  }, 30);\n}\nfunction setupInspector() {\n  const inspector = target.__VUE_INSPECTOR__;\n  const _openInEditor = inspector.openInEditor;\n  inspector.openInEditor = async (...params) => {\n    inspector.disable();\n    _openInEditor(...params);\n  };\n}\nfunction getComponentInspector() {\n  return new Promise((resolve) => {\n    function setup() {\n      setupInspector();\n      resolve(target.__VUE_INSPECTOR__);\n    }\n    if (!target.__VUE_INSPECTOR__) {\n      waitForInspectorInit(() => {\n        setup();\n      });\n    } else {\n      setup();\n    }\n  });\n}\ninit_esm_shims2();\ninit_esm_shims2();\nfunction isReadonly(value) {\n  return !!(value && value[\n    \"__v_isReadonly\"\n    /* IS_READONLY */\n  ]);\n}\nfunction isReactive(value) {\n  if (isReadonly(value)) {\n    return isReactive(value[\n      \"__v_raw\"\n      /* RAW */\n    ]);\n  }\n  return !!(value && value[\n    \"__v_isReactive\"\n    /* IS_REACTIVE */\n  ]);\n}\nfunction isRef(r) {\n  return !!(r && r.__v_isRef === true);\n}\nfunction toRaw(observed) {\n  const raw = observed && observed[\n    \"__v_raw\"\n    /* RAW */\n  ];\n  return raw ? toRaw(raw) : observed;\n}\nvar Fragment = Symbol.for(\"v-fgt\");\nvar StateEditor = class {\n  constructor() {\n    this.refEditor = new RefStateEditor();\n  }\n  set(object, path, value, cb) {\n    const sections = Array.isArray(path) ? path : path.split(\".\");\n    const markRef = false;\n    while (sections.length > 1) {\n      const section = sections.shift();\n      if (object instanceof Map)\n        object = object.get(section);\n      if (object instanceof Set)\n        object = Array.from(object.values())[section];\n      else object = object[section];\n      if (this.refEditor.isRef(object))\n        object = this.refEditor.get(object);\n    }\n    const field = sections[0];\n    const item = this.refEditor.get(object)[field];\n    if (cb) {\n      cb(object, field, value);\n    } else {\n      if (this.refEditor.isRef(item))\n        this.refEditor.set(item, value);\n      else if (markRef)\n        object[field] = value;\n      else\n        object[field] = value;\n    }\n  }\n  get(object, path) {\n    const sections = Array.isArray(path) ? path : path.split(\".\");\n    for (let i = 0; i < sections.length; i++) {\n      if (object instanceof Map)\n        object = object.get(sections[i]);\n      else\n        object = object[sections[i]];\n      if (this.refEditor.isRef(object))\n        object = this.refEditor.get(object);\n      if (!object)\n        return void 0;\n    }\n    return object;\n  }\n  has(object, path, parent = false) {\n    if (typeof object === \"undefined\")\n      return false;\n    const sections = Array.isArray(path) ? path.slice() : path.split(\".\");\n    const size = !parent ? 1 : 2;\n    while (object && sections.length > size) {\n      const section = sections.shift();\n      object = object[section];\n      if (this.refEditor.isRef(object))\n        object = this.refEditor.get(object);\n    }\n    return object != null && Object.prototype.hasOwnProperty.call(object, sections[0]);\n  }\n  createDefaultSetCallback(state) {\n    return (object, field, value) => {\n      if (state.remove || state.newKey) {\n        if (Array.isArray(object))\n          object.splice(field, 1);\n        else if (toRaw(object) instanceof Map)\n          object.delete(field);\n        else if (toRaw(object) instanceof Set)\n          object.delete(Array.from(object.values())[field]);\n        else Reflect.deleteProperty(object, field);\n      }\n      if (!state.remove) {\n        const target22 = object[state.newKey || field];\n        if (this.refEditor.isRef(target22))\n          this.refEditor.set(target22, value);\n        else if (toRaw(object) instanceof Map)\n          object.set(state.newKey || field, value);\n        else if (toRaw(object) instanceof Set)\n          object.add(value);\n        else\n          object[state.newKey || field] = value;\n      }\n    };\n  }\n};\nvar RefStateEditor = class {\n  set(ref, value) {\n    if (isRef(ref)) {\n      ref.value = value;\n    } else {\n      if (ref instanceof Set && Array.isArray(value)) {\n        ref.clear();\n        value.forEach((v) => ref.add(v));\n        return;\n      }\n      const currentKeys = Object.keys(value);\n      if (ref instanceof Map) {\n        const previousKeysSet2 = new Set(ref.keys());\n        currentKeys.forEach((key) => {\n          ref.set(key, Reflect.get(value, key));\n          previousKeysSet2.delete(key);\n        });\n        previousKeysSet2.forEach((key) => ref.delete(key));\n        return;\n      }\n      const previousKeysSet = new Set(Object.keys(ref));\n      currentKeys.forEach((key) => {\n        Reflect.set(ref, key, Reflect.get(value, key));\n        previousKeysSet.delete(key);\n      });\n      previousKeysSet.forEach((key) => Reflect.deleteProperty(ref, key));\n    }\n  }\n  get(ref) {\n    return isRef(ref) ? ref.value : ref;\n  }\n  isRef(ref) {\n    return isRef(ref) || isReactive(ref);\n  }\n};\nvar stateEditor = new StateEditor();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar TIMELINE_LAYERS_STATE_STORAGE_ID = \"__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS_STATE__\";\nfunction getTimelineLayersStateFromStorage() {\n  if (!isBrowser || typeof localStorage === \"undefined\" || localStorage === null) {\n    return {\n      recordingState: false,\n      mouseEventEnabled: false,\n      keyboardEventEnabled: false,\n      componentEventEnabled: false,\n      performanceEventEnabled: false,\n      selected: \"\"\n    };\n  }\n  const state = localStorage.getItem(TIMELINE_LAYERS_STATE_STORAGE_ID);\n  return state ? JSON.parse(state) : {\n    recordingState: false,\n    mouseEventEnabled: false,\n    keyboardEventEnabled: false,\n    componentEventEnabled: false,\n    performanceEventEnabled: false,\n    selected: \"\"\n  };\n}\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar _a22;\nvar _b2;\n(_b2 = (_a22 = target).__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS) != null ? _b2 : _a22.__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS = [];\nvar devtoolsTimelineLayers = new Proxy(target.__VUE_DEVTOOLS_KIT_TIMELINE_LAYERS, {\n  get(target22, prop, receiver) {\n    return Reflect.get(target22, prop, receiver);\n  }\n});\nfunction addTimelineLayer(options, descriptor) {\n  devtoolsState.timelineLayersState[descriptor.id] = false;\n  devtoolsTimelineLayers.push({\n    ...options,\n    descriptorId: descriptor.id,\n    appRecord: getAppRecord(descriptor.app)\n  });\n}\nvar _a3;\nvar _b3;\n(_b3 = (_a3 = target).__VUE_DEVTOOLS_KIT_INSPECTOR__) != null ? _b3 : _a3.__VUE_DEVTOOLS_KIT_INSPECTOR__ = [];\nvar devtoolsInspector = new Proxy(target.__VUE_DEVTOOLS_KIT_INSPECTOR__, {\n  get(target22, prop, receiver) {\n    return Reflect.get(target22, prop, receiver);\n  }\n});\nvar callInspectorUpdatedHook = debounce(() => {\n  devtoolsContext.hooks.callHook(\"sendInspectorToClient\", getActiveInspectors());\n});\nfunction addInspector(inspector, descriptor) {\n  var _a25, _b25;\n  devtoolsInspector.push({\n    options: inspector,\n    descriptor,\n    treeFilterPlaceholder: (_a25 = inspector.treeFilterPlaceholder) != null ? _a25 : \"Search tree...\",\n    stateFilterPlaceholder: (_b25 = inspector.stateFilterPlaceholder) != null ? _b25 : \"Search state...\",\n    treeFilter: \"\",\n    selectedNodeId: \"\",\n    appRecord: getAppRecord(descriptor.app)\n  });\n  callInspectorUpdatedHook();\n}\nfunction getActiveInspectors() {\n  return devtoolsInspector.filter((inspector) => inspector.descriptor.app === activeAppRecord.value.app).filter((inspector) => inspector.descriptor.id !== \"components\").map((inspector) => {\n    var _a25;\n    const descriptor = inspector.descriptor;\n    const options = inspector.options;\n    return {\n      id: options.id,\n      label: options.label,\n      logo: descriptor.logo,\n      icon: `custom-ic-baseline-${(_a25 = options == null ? void 0 : options.icon) == null ? void 0 : _a25.replace(/_/g, \"-\")}`,\n      packageName: descriptor.packageName,\n      homepage: descriptor.homepage,\n      pluginId: descriptor.id\n    };\n  });\n}\nfunction getInspector(id, app) {\n  return devtoolsInspector.find((inspector) => inspector.options.id === id && (app ? inspector.descriptor.app === app : true));\n}\nvar DevToolsV6PluginAPIHookKeys = ((DevToolsV6PluginAPIHookKeys2) => {\n  DevToolsV6PluginAPIHookKeys2[\"VISIT_COMPONENT_TREE\"] = \"visitComponentTree\";\n  DevToolsV6PluginAPIHookKeys2[\"INSPECT_COMPONENT\"] = \"inspectComponent\";\n  DevToolsV6PluginAPIHookKeys2[\"EDIT_COMPONENT_STATE\"] = \"editComponentState\";\n  DevToolsV6PluginAPIHookKeys2[\"GET_INSPECTOR_TREE\"] = \"getInspectorTree\";\n  DevToolsV6PluginAPIHookKeys2[\"GET_INSPECTOR_STATE\"] = \"getInspectorState\";\n  DevToolsV6PluginAPIHookKeys2[\"EDIT_INSPECTOR_STATE\"] = \"editInspectorState\";\n  DevToolsV6PluginAPIHookKeys2[\"INSPECT_TIMELINE_EVENT\"] = \"inspectTimelineEvent\";\n  DevToolsV6PluginAPIHookKeys2[\"TIMELINE_CLEARED\"] = \"timelineCleared\";\n  DevToolsV6PluginAPIHookKeys2[\"SET_PLUGIN_SETTINGS\"] = \"setPluginSettings\";\n  return DevToolsV6PluginAPIHookKeys2;\n})(DevToolsV6PluginAPIHookKeys || {});\nvar DevToolsContextHookKeys = ((DevToolsContextHookKeys2) => {\n  DevToolsContextHookKeys2[\"ADD_INSPECTOR\"] = \"addInspector\";\n  DevToolsContextHookKeys2[\"SEND_INSPECTOR_TREE\"] = \"sendInspectorTree\";\n  DevToolsContextHookKeys2[\"SEND_INSPECTOR_STATE\"] = \"sendInspectorState\";\n  DevToolsContextHookKeys2[\"CUSTOM_INSPECTOR_SELECT_NODE\"] = \"customInspectorSelectNode\";\n  DevToolsContextHookKeys2[\"TIMELINE_LAYER_ADDED\"] = \"timelineLayerAdded\";\n  DevToolsContextHookKeys2[\"TIMELINE_EVENT_ADDED\"] = \"timelineEventAdded\";\n  DevToolsContextHookKeys2[\"GET_COMPONENT_INSTANCES\"] = \"getComponentInstances\";\n  DevToolsContextHookKeys2[\"GET_COMPONENT_BOUNDS\"] = \"getComponentBounds\";\n  DevToolsContextHookKeys2[\"GET_COMPONENT_NAME\"] = \"getComponentName\";\n  DevToolsContextHookKeys2[\"COMPONENT_HIGHLIGHT\"] = \"componentHighlight\";\n  DevToolsContextHookKeys2[\"COMPONENT_UNHIGHLIGHT\"] = \"componentUnhighlight\";\n  return DevToolsContextHookKeys2;\n})(DevToolsContextHookKeys || {});\nvar DevToolsMessagingHookKeys = ((DevToolsMessagingHookKeys2) => {\n  DevToolsMessagingHookKeys2[\"SEND_INSPECTOR_TREE_TO_CLIENT\"] = \"sendInspectorTreeToClient\";\n  DevToolsMessagingHookKeys2[\"SEND_INSPECTOR_STATE_TO_CLIENT\"] = \"sendInspectorStateToClient\";\n  DevToolsMessagingHookKeys2[\"SEND_TIMELINE_EVENT_TO_CLIENT\"] = \"sendTimelineEventToClient\";\n  DevToolsMessagingHookKeys2[\"SEND_INSPECTOR_TO_CLIENT\"] = \"sendInspectorToClient\";\n  DevToolsMessagingHookKeys2[\"SEND_ACTIVE_APP_UNMOUNTED_TO_CLIENT\"] = \"sendActiveAppUpdatedToClient\";\n  DevToolsMessagingHookKeys2[\"DEVTOOLS_STATE_UPDATED\"] = \"devtoolsStateUpdated\";\n  DevToolsMessagingHookKeys2[\"DEVTOOLS_CONNECTED_UPDATED\"] = \"devtoolsConnectedUpdated\";\n  DevToolsMessagingHookKeys2[\"ROUTER_INFO_UPDATED\"] = \"routerInfoUpdated\";\n  return DevToolsMessagingHookKeys2;\n})(DevToolsMessagingHookKeys || {});\nfunction createDevToolsCtxHooks() {\n  const hooks2 = createHooks();\n  hooks2.hook(\"addInspector\", ({ inspector, plugin }) => {\n    addInspector(inspector, plugin.descriptor);\n  });\n  const debounceSendInspectorTree = debounce(async ({ inspectorId, plugin }) => {\n    var _a25;\n    if (!inspectorId || !((_a25 = plugin == null ? void 0 : plugin.descriptor) == null ? void 0 : _a25.app) || devtoolsState.highPerfModeEnabled)\n      return;\n    const inspector = getInspector(inspectorId, plugin.descriptor.app);\n    const _payload = {\n      app: plugin.descriptor.app,\n      inspectorId,\n      filter: (inspector == null ? void 0 : inspector.treeFilter) || \"\",\n      rootNodes: []\n    };\n    await new Promise((resolve) => {\n      hooks2.callHookWith(\n        async (callbacks) => {\n          await Promise.all(callbacks.map((cb) => cb(_payload)));\n          resolve();\n        },\n        \"getInspectorTree\"\n        /* GET_INSPECTOR_TREE */\n      );\n    });\n    hooks2.callHookWith(\n      async (callbacks) => {\n        await Promise.all(callbacks.map((cb) => cb({\n          inspectorId,\n          rootNodes: _payload.rootNodes\n        })));\n      },\n      \"sendInspectorTreeToClient\"\n      /* SEND_INSPECTOR_TREE_TO_CLIENT */\n    );\n  }, 120);\n  hooks2.hook(\"sendInspectorTree\", debounceSendInspectorTree);\n  const debounceSendInspectorState = debounce(async ({ inspectorId, plugin }) => {\n    var _a25;\n    if (!inspectorId || !((_a25 = plugin == null ? void 0 : plugin.descriptor) == null ? void 0 : _a25.app) || devtoolsState.highPerfModeEnabled)\n      return;\n    const inspector = getInspector(inspectorId, plugin.descriptor.app);\n    const _payload = {\n      app: plugin.descriptor.app,\n      inspectorId,\n      nodeId: (inspector == null ? void 0 : inspector.selectedNodeId) || \"\",\n      state: null\n    };\n    const ctx = {\n      currentTab: `custom-inspector:${inspectorId}`\n    };\n    if (_payload.nodeId) {\n      await new Promise((resolve) => {\n        hooks2.callHookWith(\n          async (callbacks) => {\n            await Promise.all(callbacks.map((cb) => cb(_payload, ctx)));\n            resolve();\n          },\n          \"getInspectorState\"\n          /* GET_INSPECTOR_STATE */\n        );\n      });\n    }\n    hooks2.callHookWith(\n      async (callbacks) => {\n        await Promise.all(callbacks.map((cb) => cb({\n          inspectorId,\n          nodeId: _payload.nodeId,\n          state: _payload.state\n        })));\n      },\n      \"sendInspectorStateToClient\"\n      /* SEND_INSPECTOR_STATE_TO_CLIENT */\n    );\n  }, 120);\n  hooks2.hook(\"sendInspectorState\", debounceSendInspectorState);\n  hooks2.hook(\"customInspectorSelectNode\", ({ inspectorId, nodeId, plugin }) => {\n    const inspector = getInspector(inspectorId, plugin.descriptor.app);\n    if (!inspector)\n      return;\n    inspector.selectedNodeId = nodeId;\n  });\n  hooks2.hook(\"timelineLayerAdded\", ({ options, plugin }) => {\n    addTimelineLayer(options, plugin.descriptor);\n  });\n  hooks2.hook(\"timelineEventAdded\", ({ options, plugin }) => {\n    var _a25;\n    const internalLayerIds = [\"performance\", \"component-event\", \"keyboard\", \"mouse\"];\n    if (devtoolsState.highPerfModeEnabled || !((_a25 = devtoolsState.timelineLayersState) == null ? void 0 : _a25[plugin.descriptor.id]) && !internalLayerIds.includes(options.layerId))\n      return;\n    hooks2.callHookWith(\n      async (callbacks) => {\n        await Promise.all(callbacks.map((cb) => cb(options)));\n      },\n      \"sendTimelineEventToClient\"\n      /* SEND_TIMELINE_EVENT_TO_CLIENT */\n    );\n  });\n  hooks2.hook(\"getComponentInstances\", async ({ app }) => {\n    const appRecord = app.__VUE_DEVTOOLS_NEXT_APP_RECORD__;\n    if (!appRecord)\n      return null;\n    const appId = appRecord.id.toString();\n    const instances = [...appRecord.instanceMap].filter(([key]) => key.split(\":\")[0] === appId).map(([, instance]) => instance);\n    return instances;\n  });\n  hooks2.hook(\"getComponentBounds\", async ({ instance }) => {\n    const bounds = getComponentBoundingRect(instance);\n    return bounds;\n  });\n  hooks2.hook(\"getComponentName\", ({ instance }) => {\n    const name = getInstanceName(instance);\n    return name;\n  });\n  hooks2.hook(\"componentHighlight\", ({ uid }) => {\n    const instance = activeAppRecord.value.instanceMap.get(uid);\n    if (instance) {\n      highlight(instance);\n    }\n  });\n  hooks2.hook(\"componentUnhighlight\", () => {\n    unhighlight();\n  });\n  return hooks2;\n}\nvar _a4;\nvar _b4;\n(_b4 = (_a4 = target).__VUE_DEVTOOLS_KIT_APP_RECORDS__) != null ? _b4 : _a4.__VUE_DEVTOOLS_KIT_APP_RECORDS__ = [];\nvar _a5;\nvar _b5;\n(_b5 = (_a5 = target).__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__) != null ? _b5 : _a5.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__ = {};\nvar _a6;\nvar _b6;\n(_b6 = (_a6 = target).__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__) != null ? _b6 : _a6.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__ = \"\";\nvar _a7;\nvar _b7;\n(_b7 = (_a7 = target).__VUE_DEVTOOLS_KIT_CUSTOM_TABS__) != null ? _b7 : _a7.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__ = [];\nvar _a8;\nvar _b8;\n(_b8 = (_a8 = target).__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__) != null ? _b8 : _a8.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__ = [];\nvar STATE_KEY = \"__VUE_DEVTOOLS_KIT_GLOBAL_STATE__\";\nfunction initStateFactory() {\n  return {\n    connected: false,\n    clientConnected: false,\n    vitePluginDetected: true,\n    appRecords: [],\n    activeAppRecordId: \"\",\n    tabs: [],\n    commands: [],\n    highPerfModeEnabled: true,\n    devtoolsClientDetected: {},\n    perfUniqueGroupId: 0,\n    timelineLayersState: getTimelineLayersStateFromStorage()\n  };\n}\nvar _a9;\nvar _b9;\n(_b9 = (_a9 = target)[STATE_KEY]) != null ? _b9 : _a9[STATE_KEY] = initStateFactory();\nvar callStateUpdatedHook = debounce((state) => {\n  devtoolsContext.hooks.callHook(\"devtoolsStateUpdated\", { state });\n});\nvar callConnectedUpdatedHook = debounce((state, oldState) => {\n  devtoolsContext.hooks.callHook(\"devtoolsConnectedUpdated\", { state, oldState });\n});\nvar devtoolsAppRecords = new Proxy(target.__VUE_DEVTOOLS_KIT_APP_RECORDS__, {\n  get(_target, prop, receiver) {\n    if (prop === \"value\")\n      return target.__VUE_DEVTOOLS_KIT_APP_RECORDS__;\n    return target.__VUE_DEVTOOLS_KIT_APP_RECORDS__[prop];\n  }\n});\nvar activeAppRecord = new Proxy(target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__, {\n  get(_target, prop, receiver) {\n    if (prop === \"value\")\n      return target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__;\n    else if (prop === \"id\")\n      return target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__;\n    return target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__[prop];\n  }\n});\nfunction updateAllStates() {\n  callStateUpdatedHook({\n    ...target[STATE_KEY],\n    appRecords: devtoolsAppRecords.value,\n    activeAppRecordId: activeAppRecord.id,\n    tabs: target.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__,\n    commands: target.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__\n  });\n}\nfunction setActiveAppRecord(app) {\n  target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD__ = app;\n  updateAllStates();\n}\nfunction setActiveAppRecordId(id) {\n  target.__VUE_DEVTOOLS_KIT_ACTIVE_APP_RECORD_ID__ = id;\n  updateAllStates();\n}\nvar devtoolsState = new Proxy(target[STATE_KEY], {\n  get(target22, property) {\n    if (property === \"appRecords\") {\n      return devtoolsAppRecords;\n    } else if (property === \"activeAppRecordId\") {\n      return activeAppRecord.id;\n    } else if (property === \"tabs\") {\n      return target.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__;\n    } else if (property === \"commands\") {\n      return target.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__;\n    }\n    return target[STATE_KEY][property];\n  },\n  deleteProperty(target22, property) {\n    delete target22[property];\n    return true;\n  },\n  set(target22, property, value) {\n    const oldState = { ...target[STATE_KEY] };\n    target22[property] = value;\n    target[STATE_KEY][property] = value;\n    return true;\n  }\n});\nfunction onDevToolsConnected(fn) {\n  return new Promise((resolve) => {\n    if (devtoolsState.connected) {\n      fn();\n      resolve();\n    }\n    devtoolsContext.hooks.hook(\"devtoolsConnectedUpdated\", ({ state }) => {\n      if (state.connected) {\n        fn();\n        resolve();\n      }\n    });\n  });\n}\nvar resolveIcon = (icon) => {\n  if (!icon)\n    return;\n  if (icon.startsWith(\"baseline-\")) {\n    return `custom-ic-${icon}`;\n  }\n  if (icon.startsWith(\"i-\") || isUrlString(icon))\n    return icon;\n  return `custom-ic-baseline-${icon}`;\n};\nfunction addCustomTab(tab) {\n  const tabs = target.__VUE_DEVTOOLS_KIT_CUSTOM_TABS__;\n  if (tabs.some((t) => t.name === tab.name))\n    return;\n  tabs.push({\n    ...tab,\n    icon: resolveIcon(tab.icon)\n  });\n  updateAllStates();\n}\nfunction addCustomCommand(action) {\n  const commands = target.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__;\n  if (commands.some((t) => t.id === action.id))\n    return;\n  commands.push({\n    ...action,\n    icon: resolveIcon(action.icon),\n    children: action.children ? action.children.map((child) => ({\n      ...child,\n      icon: resolveIcon(child.icon)\n    })) : void 0\n  });\n  updateAllStates();\n}\nfunction removeCustomCommand(actionId) {\n  const commands = target.__VUE_DEVTOOLS_KIT_CUSTOM_COMMANDS__;\n  const index = commands.findIndex((t) => t.id === actionId);\n  if (index === -1)\n    return;\n  commands.splice(index, 1);\n  updateAllStates();\n}\nfunction openInEditor(options = {}) {\n  var _a25, _b25, _c;\n  const { file, host, baseUrl = window.location.origin, line = 0, column = 0 } = options;\n  if (file) {\n    if (host === \"chrome-extension\") {\n      const fileName = file.replace(/\\\\/g, \"\\\\\\\\\");\n      const _baseUrl = (_b25 = (_a25 = window.VUE_DEVTOOLS_CONFIG) == null ? void 0 : _a25.openInEditorHost) != null ? _b25 : \"/\";\n      fetch(`${_baseUrl}__open-in-editor?file=${encodeURI(file)}`).then((response) => {\n        if (!response.ok) {\n          const msg = `Opening component ${fileName} failed`;\n          console.log(`%c${msg}`, \"color:red\");\n        }\n      });\n    } else if (devtoolsState.vitePluginDetected) {\n      const _baseUrl = (_c = target.__VUE_DEVTOOLS_OPEN_IN_EDITOR_BASE_URL__) != null ? _c : baseUrl;\n      target.__VUE_INSPECTOR__.openInEditor(_baseUrl, file, line, column);\n    }\n  }\n}\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar _a10;\nvar _b10;\n(_b10 = (_a10 = target).__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__) != null ? _b10 : _a10.__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__ = [];\nvar devtoolsPluginBuffer = new Proxy(target.__VUE_DEVTOOLS_KIT_PLUGIN_BUFFER__, {\n  get(target22, prop, receiver) {\n    return Reflect.get(target22, prop, receiver);\n  }\n});\nfunction _getSettings(settings) {\n  const _settings = {};\n  Object.keys(settings).forEach((key) => {\n    _settings[key] = settings[key].defaultValue;\n  });\n  return _settings;\n}\nfunction getPluginLocalKey(pluginId) {\n  return `__VUE_DEVTOOLS_NEXT_PLUGIN_SETTINGS__${pluginId}__`;\n}\nfunction getPluginSettingsOptions(pluginId) {\n  var _a25, _b25, _c;\n  const item = (_b25 = (_a25 = devtoolsPluginBuffer.find((item2) => {\n    var _a26;\n    return item2[0].id === pluginId && !!((_a26 = item2[0]) == null ? void 0 : _a26.settings);\n  })) == null ? void 0 : _a25[0]) != null ? _b25 : null;\n  return (_c = item == null ? void 0 : item.settings) != null ? _c : null;\n}\nfunction getPluginSettings(pluginId, fallbackValue) {\n  var _a25, _b25, _c;\n  const localKey = getPluginLocalKey(pluginId);\n  if (localKey) {\n    const localSettings = localStorage.getItem(localKey);\n    if (localSettings) {\n      return JSON.parse(localSettings);\n    }\n  }\n  if (pluginId) {\n    const item = (_b25 = (_a25 = devtoolsPluginBuffer.find((item2) => item2[0].id === pluginId)) == null ? void 0 : _a25[0]) != null ? _b25 : null;\n    return _getSettings((_c = item == null ? void 0 : item.settings) != null ? _c : {});\n  }\n  return _getSettings(fallbackValue);\n}\nfunction initPluginSettings(pluginId, settings) {\n  const localKey = getPluginLocalKey(pluginId);\n  const localSettings = localStorage.getItem(localKey);\n  if (!localSettings) {\n    localStorage.setItem(localKey, JSON.stringify(_getSettings(settings)));\n  }\n}\nfunction setPluginSettings(pluginId, key, value) {\n  const localKey = getPluginLocalKey(pluginId);\n  const localSettings = localStorage.getItem(localKey);\n  const parsedLocalSettings = JSON.parse(localSettings || \"{}\");\n  const updated = {\n    ...parsedLocalSettings,\n    [key]: value\n  };\n  localStorage.setItem(localKey, JSON.stringify(updated));\n  devtoolsContext.hooks.callHookWith(\n    (callbacks) => {\n      callbacks.forEach((cb) => cb({\n        pluginId,\n        key,\n        oldValue: parsedLocalSettings[key],\n        newValue: value,\n        settings: updated\n      }));\n    },\n    \"setPluginSettings\"\n    /* SET_PLUGIN_SETTINGS */\n  );\n}\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar _a11;\nvar _b11;\nvar devtoolsHooks = (_b11 = (_a11 = target).__VUE_DEVTOOLS_HOOK) != null ? _b11 : _a11.__VUE_DEVTOOLS_HOOK = createHooks();\nvar on = {\n  vueAppInit(fn) {\n    devtoolsHooks.hook(\"app:init\", fn);\n  },\n  vueAppUnmount(fn) {\n    devtoolsHooks.hook(\"app:unmount\", fn);\n  },\n  vueAppConnected(fn) {\n    devtoolsHooks.hook(\"app:connected\", fn);\n  },\n  componentAdded(fn) {\n    return devtoolsHooks.hook(\"component:added\", fn);\n  },\n  componentEmit(fn) {\n    return devtoolsHooks.hook(\"component:emit\", fn);\n  },\n  componentUpdated(fn) {\n    return devtoolsHooks.hook(\"component:updated\", fn);\n  },\n  componentRemoved(fn) {\n    return devtoolsHooks.hook(\"component:removed\", fn);\n  },\n  setupDevtoolsPlugin(fn) {\n    devtoolsHooks.hook(\"devtools-plugin:setup\", fn);\n  },\n  perfStart(fn) {\n    return devtoolsHooks.hook(\"perf:start\", fn);\n  },\n  perfEnd(fn) {\n    return devtoolsHooks.hook(\"perf:end\", fn);\n  }\n};\nvar hook = {\n  on,\n  setupDevToolsPlugin(pluginDescriptor, setupFn) {\n    return devtoolsHooks.callHook(\"devtools-plugin:setup\", pluginDescriptor, setupFn);\n  }\n};\nvar DevToolsV6PluginAPI = class {\n  constructor({ plugin, ctx }) {\n    this.hooks = ctx.hooks;\n    this.plugin = plugin;\n  }\n  get on() {\n    return {\n      // component inspector\n      visitComponentTree: (handler) => {\n        this.hooks.hook(\"visitComponentTree\", handler);\n      },\n      inspectComponent: (handler) => {\n        this.hooks.hook(\"inspectComponent\", handler);\n      },\n      editComponentState: (handler) => {\n        this.hooks.hook(\"editComponentState\", handler);\n      },\n      // custom inspector\n      getInspectorTree: (handler) => {\n        this.hooks.hook(\"getInspectorTree\", handler);\n      },\n      getInspectorState: (handler) => {\n        this.hooks.hook(\"getInspectorState\", handler);\n      },\n      editInspectorState: (handler) => {\n        this.hooks.hook(\"editInspectorState\", handler);\n      },\n      // timeline\n      inspectTimelineEvent: (handler) => {\n        this.hooks.hook(\"inspectTimelineEvent\", handler);\n      },\n      timelineCleared: (handler) => {\n        this.hooks.hook(\"timelineCleared\", handler);\n      },\n      // settings\n      setPluginSettings: (handler) => {\n        this.hooks.hook(\"setPluginSettings\", handler);\n      }\n    };\n  }\n  // component inspector\n  notifyComponentUpdate(instance) {\n    var _a25;\n    if (devtoolsState.highPerfModeEnabled) {\n      return;\n    }\n    const inspector = getActiveInspectors().find((i) => i.packageName === this.plugin.descriptor.packageName);\n    if (inspector == null ? void 0 : inspector.id) {\n      if (instance) {\n        const args = [\n          instance.appContext.app,\n          instance.uid,\n          (_a25 = instance.parent) == null ? void 0 : _a25.uid,\n          instance\n        ];\n        devtoolsHooks.callHook(\"component:updated\", ...args);\n      } else {\n        devtoolsHooks.callHook(\n          \"component:updated\"\n          /* COMPONENT_UPDATED */\n        );\n      }\n      this.hooks.callHook(\"sendInspectorState\", { inspectorId: inspector.id, plugin: this.plugin });\n    }\n  }\n  // custom inspector\n  addInspector(options) {\n    this.hooks.callHook(\"addInspector\", { inspector: options, plugin: this.plugin });\n    if (this.plugin.descriptor.settings) {\n      initPluginSettings(options.id, this.plugin.descriptor.settings);\n    }\n  }\n  sendInspectorTree(inspectorId) {\n    if (devtoolsState.highPerfModeEnabled) {\n      return;\n    }\n    this.hooks.callHook(\"sendInspectorTree\", { inspectorId, plugin: this.plugin });\n  }\n  sendInspectorState(inspectorId) {\n    if (devtoolsState.highPerfModeEnabled) {\n      return;\n    }\n    this.hooks.callHook(\"sendInspectorState\", { inspectorId, plugin: this.plugin });\n  }\n  selectInspectorNode(inspectorId, nodeId) {\n    this.hooks.callHook(\"customInspectorSelectNode\", { inspectorId, nodeId, plugin: this.plugin });\n  }\n  visitComponentTree(payload) {\n    return this.hooks.callHook(\"visitComponentTree\", payload);\n  }\n  // timeline\n  now() {\n    if (devtoolsState.highPerfModeEnabled) {\n      return 0;\n    }\n    return Date.now();\n  }\n  addTimelineLayer(options) {\n    this.hooks.callHook(\"timelineLayerAdded\", { options, plugin: this.plugin });\n  }\n  addTimelineEvent(options) {\n    if (devtoolsState.highPerfModeEnabled) {\n      return;\n    }\n    this.hooks.callHook(\"timelineEventAdded\", { options, plugin: this.plugin });\n  }\n  // settings\n  getSettings(pluginId) {\n    return getPluginSettings(pluginId != null ? pluginId : this.plugin.descriptor.id, this.plugin.descriptor.settings);\n  }\n  // utilities\n  getComponentInstances(app) {\n    return this.hooks.callHook(\"getComponentInstances\", { app });\n  }\n  getComponentBounds(instance) {\n    return this.hooks.callHook(\"getComponentBounds\", { instance });\n  }\n  getComponentName(instance) {\n    return this.hooks.callHook(\"getComponentName\", { instance });\n  }\n  highlightElement(instance) {\n    const uid = instance.__VUE_DEVTOOLS_NEXT_UID__;\n    return this.hooks.callHook(\"componentHighlight\", { uid });\n  }\n  unhighlightElement() {\n    return this.hooks.callHook(\n      \"componentUnhighlight\"\n      /* COMPONENT_UNHIGHLIGHT */\n    );\n  }\n};\nvar DevToolsPluginAPI = DevToolsV6PluginAPI;\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar UNDEFINED = \"__vue_devtool_undefined__\";\nvar INFINITY = \"__vue_devtool_infinity__\";\nvar NEGATIVE_INFINITY = \"__vue_devtool_negative_infinity__\";\nvar NAN = \"__vue_devtool_nan__\";\ninit_esm_shims2();\ninit_esm_shims2();\nvar tokenMap = {\n  [UNDEFINED]: \"undefined\",\n  [NAN]: \"NaN\",\n  [INFINITY]: \"Infinity\",\n  [NEGATIVE_INFINITY]: \"-Infinity\"\n};\nvar reversedTokenMap = Object.entries(tokenMap).reduce((acc, [key, value]) => {\n  acc[value] = key;\n  return acc;\n}, {});\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar _a12;\nvar _b12;\n(_b12 = (_a12 = target).__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__) != null ? _b12 : _a12.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__ = /* @__PURE__ */ new Set();\nfunction setupDevToolsPlugin(pluginDescriptor, setupFn) {\n  return hook.setupDevToolsPlugin(pluginDescriptor, setupFn);\n}\nfunction callDevToolsPluginSetupFn(plugin, app) {\n  const [pluginDescriptor, setupFn] = plugin;\n  if (pluginDescriptor.app !== app)\n    return;\n  const api = new DevToolsPluginAPI({\n    plugin: {\n      setupFn,\n      descriptor: pluginDescriptor\n    },\n    ctx: devtoolsContext\n  });\n  if (pluginDescriptor.packageName === \"vuex\") {\n    api.on.editInspectorState((payload) => {\n      api.sendInspectorState(payload.inspectorId);\n    });\n  }\n  setupFn(api);\n}\nfunction registerDevToolsPlugin(app, options) {\n  if (target.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__.has(app)) {\n    return;\n  }\n  if (devtoolsState.highPerfModeEnabled && !(options == null ? void 0 : options.inspectingComponent)) {\n    return;\n  }\n  target.__VUE_DEVTOOLS_KIT__REGISTERED_PLUGIN_APPS__.add(app);\n  devtoolsPluginBuffer.forEach((plugin) => {\n    callDevToolsPluginSetupFn(plugin, app);\n  });\n}\ninit_esm_shims2();\ninit_esm_shims2();\nvar ROUTER_KEY = \"__VUE_DEVTOOLS_ROUTER__\";\nvar ROUTER_INFO_KEY = \"__VUE_DEVTOOLS_ROUTER_INFO__\";\nvar _a13;\nvar _b13;\n(_b13 = (_a13 = target)[ROUTER_INFO_KEY]) != null ? _b13 : _a13[ROUTER_INFO_KEY] = {\n  currentRoute: null,\n  routes: []\n};\nvar _a14;\nvar _b14;\n(_b14 = (_a14 = target)[ROUTER_KEY]) != null ? _b14 : _a14[ROUTER_KEY] = {};\nvar devtoolsRouterInfo = new Proxy(target[ROUTER_INFO_KEY], {\n  get(target22, property) {\n    return target[ROUTER_INFO_KEY][property];\n  }\n});\nvar devtoolsRouter = new Proxy(target[ROUTER_KEY], {\n  get(target22, property) {\n    if (property === \"value\") {\n      return target[ROUTER_KEY];\n    }\n  }\n});\nfunction getRoutes(router) {\n  const routesMap = /* @__PURE__ */ new Map();\n  return ((router == null ? void 0 : router.getRoutes()) || []).filter((i) => !routesMap.has(i.path) && routesMap.set(i.path, 1));\n}\nfunction filterRoutes(routes) {\n  return routes.map((item) => {\n    let { path, name, children, meta } = item;\n    if (children == null ? void 0 : children.length)\n      children = filterRoutes(children);\n    return {\n      path,\n      name,\n      children,\n      meta\n    };\n  });\n}\nfunction filterCurrentRoute(route) {\n  if (route) {\n    const { fullPath, hash, href, path, name, matched, params, query } = route;\n    return {\n      fullPath,\n      hash,\n      href,\n      path,\n      name,\n      params,\n      query,\n      matched: filterRoutes(matched)\n    };\n  }\n  return route;\n}\nfunction normalizeRouterInfo(appRecord, activeAppRecord2) {\n  function init() {\n    var _a25;\n    const router = (_a25 = appRecord.app) == null ? void 0 : _a25.config.globalProperties.$router;\n    const currentRoute = filterCurrentRoute(router == null ? void 0 : router.currentRoute.value);\n    const routes = filterRoutes(getRoutes(router));\n    const c = console.warn;\n    console.warn = () => {\n    };\n    target[ROUTER_INFO_KEY] = {\n      currentRoute: currentRoute ? deepClone(currentRoute) : {},\n      routes: deepClone(routes)\n    };\n    target[ROUTER_KEY] = router;\n    console.warn = c;\n  }\n  init();\n  hook.on.componentUpdated(debounce(() => {\n    var _a25;\n    if (((_a25 = activeAppRecord2.value) == null ? void 0 : _a25.app) !== appRecord.app)\n      return;\n    init();\n    if (devtoolsState.highPerfModeEnabled)\n      return;\n    devtoolsContext.hooks.callHook(\"routerInfoUpdated\", { state: target[ROUTER_INFO_KEY] });\n  }, 200));\n}\nfunction createDevToolsApi(hooks2) {\n  return {\n    // get inspector tree\n    async getInspectorTree(payload) {\n      const _payload = {\n        ...payload,\n        app: activeAppRecord.value.app,\n        rootNodes: []\n      };\n      await new Promise((resolve) => {\n        hooks2.callHookWith(\n          async (callbacks) => {\n            await Promise.all(callbacks.map((cb) => cb(_payload)));\n            resolve();\n          },\n          \"getInspectorTree\"\n          /* GET_INSPECTOR_TREE */\n        );\n      });\n      return _payload.rootNodes;\n    },\n    // get inspector state\n    async getInspectorState(payload) {\n      const _payload = {\n        ...payload,\n        app: activeAppRecord.value.app,\n        state: null\n      };\n      const ctx = {\n        currentTab: `custom-inspector:${payload.inspectorId}`\n      };\n      await new Promise((resolve) => {\n        hooks2.callHookWith(\n          async (callbacks) => {\n            await Promise.all(callbacks.map((cb) => cb(_payload, ctx)));\n            resolve();\n          },\n          \"getInspectorState\"\n          /* GET_INSPECTOR_STATE */\n        );\n      });\n      return _payload.state;\n    },\n    // edit inspector state\n    editInspectorState(payload) {\n      const stateEditor2 = new StateEditor();\n      const _payload = {\n        ...payload,\n        app: activeAppRecord.value.app,\n        set: (obj, path = payload.path, value = payload.state.value, cb) => {\n          stateEditor2.set(obj, path, value, cb || stateEditor2.createDefaultSetCallback(payload.state));\n        }\n      };\n      hooks2.callHookWith(\n        (callbacks) => {\n          callbacks.forEach((cb) => cb(_payload));\n        },\n        \"editInspectorState\"\n        /* EDIT_INSPECTOR_STATE */\n      );\n    },\n    // send inspector state\n    sendInspectorState(inspectorId) {\n      const inspector = getInspector(inspectorId);\n      hooks2.callHook(\"sendInspectorState\", { inspectorId, plugin: {\n        descriptor: inspector.descriptor,\n        setupFn: () => ({})\n      } });\n    },\n    // inspect component inspector\n    inspectComponentInspector() {\n      return inspectComponentHighLighter();\n    },\n    // cancel inspect component inspector\n    cancelInspectComponentInspector() {\n      return cancelInspectComponentHighLighter();\n    },\n    // get component render code\n    getComponentRenderCode(id) {\n      const instance = getComponentInstance(activeAppRecord.value, id);\n      if (instance)\n        return !((instance == null ? void 0 : instance.type) instanceof Function) ? instance.render.toString() : instance.type.toString();\n    },\n    // scroll to component\n    scrollToComponent(id) {\n      return scrollToComponent({ id });\n    },\n    // open in editor\n    openInEditor,\n    // get vue inspector\n    getVueInspector: getComponentInspector,\n    // toggle app\n    toggleApp(id, options) {\n      const appRecord = devtoolsAppRecords.value.find((record) => record.id === id);\n      if (appRecord) {\n        setActiveAppRecordId(id);\n        setActiveAppRecord(appRecord);\n        normalizeRouterInfo(appRecord, activeAppRecord);\n        callInspectorUpdatedHook();\n        registerDevToolsPlugin(appRecord.app, options);\n      }\n    },\n    // inspect dom\n    inspectDOM(instanceId) {\n      const instance = getComponentInstance(activeAppRecord.value, instanceId);\n      if (instance) {\n        const [el] = getRootElementsFromComponentInstance(instance);\n        if (el) {\n          target.__VUE_DEVTOOLS_INSPECT_DOM_TARGET__ = el;\n        }\n      }\n    },\n    updatePluginSettings(pluginId, key, value) {\n      setPluginSettings(pluginId, key, value);\n    },\n    getPluginSettings(pluginId) {\n      return {\n        options: getPluginSettingsOptions(pluginId),\n        values: getPluginSettings(pluginId)\n      };\n    }\n  };\n}\ninit_esm_shims2();\nvar _a15;\nvar _b15;\n(_b15 = (_a15 = target).__VUE_DEVTOOLS_ENV__) != null ? _b15 : _a15.__VUE_DEVTOOLS_ENV__ = {\n  vitePluginDetected: false\n};\nvar hooks = createDevToolsCtxHooks();\nvar _a16;\nvar _b16;\n(_b16 = (_a16 = target).__VUE_DEVTOOLS_KIT_CONTEXT__) != null ? _b16 : _a16.__VUE_DEVTOOLS_KIT_CONTEXT__ = {\n  hooks,\n  get state() {\n    return {\n      ...devtoolsState,\n      activeAppRecordId: activeAppRecord.id,\n      activeAppRecord: activeAppRecord.value,\n      appRecords: devtoolsAppRecords.value\n    };\n  },\n  api: createDevToolsApi(hooks)\n};\nvar devtoolsContext = target.__VUE_DEVTOOLS_KIT_CONTEXT__;\ninit_esm_shims2();\nvar import_speakingurl = __toESM2(require_speakingurl2(), 1);\nvar _a17;\nvar _b17;\nvar appRecordInfo = (_b17 = (_a17 = target).__VUE_DEVTOOLS_NEXT_APP_RECORD_INFO__) != null ? _b17 : _a17.__VUE_DEVTOOLS_NEXT_APP_RECORD_INFO__ = {\n  id: 0,\n  appIds: /* @__PURE__ */ new Set()\n};\nfunction onDevToolsClientConnected(fn) {\n  return new Promise((resolve) => {\n    if (devtoolsState.connected && devtoolsState.clientConnected) {\n      fn();\n      resolve();\n      return;\n    }\n    devtoolsContext.hooks.hook(\"devtoolsConnectedUpdated\", ({ state }) => {\n      if (state.connected && state.clientConnected) {\n        fn();\n        resolve();\n      }\n    });\n  });\n}\ninit_esm_shims2();\nfunction toggleHighPerfMode(state) {\n  devtoolsState.highPerfModeEnabled = state != null ? state : !devtoolsState.highPerfModeEnabled;\n  if (!state && activeAppRecord.value) {\n    registerDevToolsPlugin(activeAppRecord.value.app);\n  }\n}\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nfunction updateDevToolsClientDetected(params) {\n  devtoolsState.devtoolsClientDetected = {\n    ...devtoolsState.devtoolsClientDetected,\n    ...params\n  };\n  const devtoolsClientVisible = Object.values(devtoolsState.devtoolsClientDetected).some(Boolean);\n  toggleHighPerfMode(!devtoolsClientVisible);\n}\nvar _a18;\nvar _b18;\n(_b18 = (_a18 = target).__VUE_DEVTOOLS_UPDATE_CLIENT_DETECTED__) != null ? _b18 : _a18.__VUE_DEVTOOLS_UPDATE_CLIENT_DETECTED__ = updateDevToolsClientDetected;\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar DoubleIndexedKV = class {\n  constructor() {\n    this.keyToValue = /* @__PURE__ */ new Map();\n    this.valueToKey = /* @__PURE__ */ new Map();\n  }\n  set(key, value) {\n    this.keyToValue.set(key, value);\n    this.valueToKey.set(value, key);\n  }\n  getByKey(key) {\n    return this.keyToValue.get(key);\n  }\n  getByValue(value) {\n    return this.valueToKey.get(value);\n  }\n  clear() {\n    this.keyToValue.clear();\n    this.valueToKey.clear();\n  }\n};\nvar Registry = class {\n  constructor(generateIdentifier) {\n    this.generateIdentifier = generateIdentifier;\n    this.kv = new DoubleIndexedKV();\n  }\n  register(value, identifier) {\n    if (this.kv.getByValue(value)) {\n      return;\n    }\n    if (!identifier) {\n      identifier = this.generateIdentifier(value);\n    }\n    this.kv.set(identifier, value);\n  }\n  clear() {\n    this.kv.clear();\n  }\n  getIdentifier(value) {\n    return this.kv.getByValue(value);\n  }\n  getValue(identifier) {\n    return this.kv.getByKey(identifier);\n  }\n};\nvar ClassRegistry = class extends Registry {\n  constructor() {\n    super((c) => c.name);\n    this.classToAllowedProps = /* @__PURE__ */ new Map();\n  }\n  register(value, options) {\n    if (typeof options === \"object\") {\n      if (options.allowProps) {\n        this.classToAllowedProps.set(value, options.allowProps);\n      }\n      super.register(value, options.identifier);\n    } else {\n      super.register(value, options);\n    }\n  }\n  getAllowedProps(value) {\n    return this.classToAllowedProps.get(value);\n  }\n};\ninit_esm_shims2();\ninit_esm_shims2();\nfunction valuesOfObj(record) {\n  if (\"values\" in Object) {\n    return Object.values(record);\n  }\n  const values = [];\n  for (const key in record) {\n    if (record.hasOwnProperty(key)) {\n      values.push(record[key]);\n    }\n  }\n  return values;\n}\nfunction find(record, predicate) {\n  const values = valuesOfObj(record);\n  if (\"find\" in values) {\n    return values.find(predicate);\n  }\n  const valuesNotNever = values;\n  for (let i = 0; i < valuesNotNever.length; i++) {\n    const value = valuesNotNever[i];\n    if (predicate(value)) {\n      return value;\n    }\n  }\n  return void 0;\n}\nfunction forEach(record, run) {\n  Object.entries(record).forEach(([key, value]) => run(value, key));\n}\nfunction includes(arr, value) {\n  return arr.indexOf(value) !== -1;\n}\nfunction findArr(record, predicate) {\n  for (let i = 0; i < record.length; i++) {\n    const value = record[i];\n    if (predicate(value)) {\n      return value;\n    }\n  }\n  return void 0;\n}\nvar CustomTransformerRegistry = class {\n  constructor() {\n    this.transfomers = {};\n  }\n  register(transformer) {\n    this.transfomers[transformer.name] = transformer;\n  }\n  findApplicable(v) {\n    return find(this.transfomers, (transformer) => transformer.isApplicable(v));\n  }\n  findByName(name) {\n    return this.transfomers[name];\n  }\n};\ninit_esm_shims2();\ninit_esm_shims2();\nvar getType = (payload) => Object.prototype.toString.call(payload).slice(8, -1);\nvar isUndefined = (payload) => typeof payload === \"undefined\";\nvar isNull = (payload) => payload === null;\nvar isPlainObject2 = (payload) => {\n  if (typeof payload !== \"object\" || payload === null)\n    return false;\n  if (payload === Object.prototype)\n    return false;\n  if (Object.getPrototypeOf(payload) === null)\n    return true;\n  return Object.getPrototypeOf(payload) === Object.prototype;\n};\nvar isEmptyObject = (payload) => isPlainObject2(payload) && Object.keys(payload).length === 0;\nvar isArray = (payload) => Array.isArray(payload);\nvar isString = (payload) => typeof payload === \"string\";\nvar isNumber = (payload) => typeof payload === \"number\" && !isNaN(payload);\nvar isBoolean = (payload) => typeof payload === \"boolean\";\nvar isRegExp = (payload) => payload instanceof RegExp;\nvar isMap = (payload) => payload instanceof Map;\nvar isSet = (payload) => payload instanceof Set;\nvar isSymbol = (payload) => getType(payload) === \"Symbol\";\nvar isDate = (payload) => payload instanceof Date && !isNaN(payload.valueOf());\nvar isError = (payload) => payload instanceof Error;\nvar isNaNValue = (payload) => typeof payload === \"number\" && isNaN(payload);\nvar isPrimitive2 = (payload) => isBoolean(payload) || isNull(payload) || isUndefined(payload) || isNumber(payload) || isString(payload) || isSymbol(payload);\nvar isBigint = (payload) => typeof payload === \"bigint\";\nvar isInfinite = (payload) => payload === Infinity || payload === -Infinity;\nvar isTypedArray = (payload) => ArrayBuffer.isView(payload) && !(payload instanceof DataView);\nvar isURL = (payload) => payload instanceof URL;\ninit_esm_shims2();\nvar escapeKey = (key) => key.replace(/\\./g, \"\\\\.\");\nvar stringifyPath = (path) => path.map(String).map(escapeKey).join(\".\");\nvar parsePath = (string) => {\n  const result = [];\n  let segment = \"\";\n  for (let i = 0; i < string.length; i++) {\n    let char = string.charAt(i);\n    const isEscapedDot = char === \"\\\\\" && string.charAt(i + 1) === \".\";\n    if (isEscapedDot) {\n      segment += \".\";\n      i++;\n      continue;\n    }\n    const isEndOfSegment = char === \".\";\n    if (isEndOfSegment) {\n      result.push(segment);\n      segment = \"\";\n      continue;\n    }\n    segment += char;\n  }\n  const lastSegment = segment;\n  result.push(lastSegment);\n  return result;\n};\ninit_esm_shims2();\nfunction simpleTransformation(isApplicable, annotation, transform, untransform) {\n  return {\n    isApplicable,\n    annotation,\n    transform,\n    untransform\n  };\n}\nvar simpleRules = [\n  simpleTransformation(isUndefined, \"undefined\", () => null, () => void 0),\n  simpleTransformation(isBigint, \"bigint\", (v) => v.toString(), (v) => {\n    if (typeof BigInt !== \"undefined\") {\n      return BigInt(v);\n    }\n    console.error(\"Please add a BigInt polyfill.\");\n    return v;\n  }),\n  simpleTransformation(isDate, \"Date\", (v) => v.toISOString(), (v) => new Date(v)),\n  simpleTransformation(isError, \"Error\", (v, superJson) => {\n    const baseError = {\n      name: v.name,\n      message: v.message\n    };\n    superJson.allowedErrorProps.forEach((prop) => {\n      baseError[prop] = v[prop];\n    });\n    return baseError;\n  }, (v, superJson) => {\n    const e = new Error(v.message);\n    e.name = v.name;\n    e.stack = v.stack;\n    superJson.allowedErrorProps.forEach((prop) => {\n      e[prop] = v[prop];\n    });\n    return e;\n  }),\n  simpleTransformation(isRegExp, \"regexp\", (v) => \"\" + v, (regex) => {\n    const body = regex.slice(1, regex.lastIndexOf(\"/\"));\n    const flags = regex.slice(regex.lastIndexOf(\"/\") + 1);\n    return new RegExp(body, flags);\n  }),\n  simpleTransformation(\n    isSet,\n    \"set\",\n    // (sets only exist in es6+)\n    // eslint-disable-next-line es5/no-es6-methods\n    (v) => [...v.values()],\n    (v) => new Set(v)\n  ),\n  simpleTransformation(isMap, \"map\", (v) => [...v.entries()], (v) => new Map(v)),\n  simpleTransformation((v) => isNaNValue(v) || isInfinite(v), \"number\", (v) => {\n    if (isNaNValue(v)) {\n      return \"NaN\";\n    }\n    if (v > 0) {\n      return \"Infinity\";\n    } else {\n      return \"-Infinity\";\n    }\n  }, Number),\n  simpleTransformation((v) => v === 0 && 1 / v === -Infinity, \"number\", () => {\n    return \"-0\";\n  }, Number),\n  simpleTransformation(isURL, \"URL\", (v) => v.toString(), (v) => new URL(v))\n];\nfunction compositeTransformation(isApplicable, annotation, transform, untransform) {\n  return {\n    isApplicable,\n    annotation,\n    transform,\n    untransform\n  };\n}\nvar symbolRule = compositeTransformation((s, superJson) => {\n  if (isSymbol(s)) {\n    const isRegistered = !!superJson.symbolRegistry.getIdentifier(s);\n    return isRegistered;\n  }\n  return false;\n}, (s, superJson) => {\n  const identifier = superJson.symbolRegistry.getIdentifier(s);\n  return [\"symbol\", identifier];\n}, (v) => v.description, (_, a, superJson) => {\n  const value = superJson.symbolRegistry.getValue(a[1]);\n  if (!value) {\n    throw new Error(\"Trying to deserialize unknown symbol\");\n  }\n  return value;\n});\nvar constructorToName = [\n  Int8Array,\n  Uint8Array,\n  Int16Array,\n  Uint16Array,\n  Int32Array,\n  Uint32Array,\n  Float32Array,\n  Float64Array,\n  Uint8ClampedArray\n].reduce((obj, ctor) => {\n  obj[ctor.name] = ctor;\n  return obj;\n}, {});\nvar typedArrayRule = compositeTransformation(isTypedArray, (v) => [\"typed-array\", v.constructor.name], (v) => [...v], (v, a) => {\n  const ctor = constructorToName[a[1]];\n  if (!ctor) {\n    throw new Error(\"Trying to deserialize unknown typed array\");\n  }\n  return new ctor(v);\n});\nfunction isInstanceOfRegisteredClass(potentialClass, superJson) {\n  if (potentialClass == null ? void 0 : potentialClass.constructor) {\n    const isRegistered = !!superJson.classRegistry.getIdentifier(potentialClass.constructor);\n    return isRegistered;\n  }\n  return false;\n}\nvar classRule = compositeTransformation(isInstanceOfRegisteredClass, (clazz, superJson) => {\n  const identifier = superJson.classRegistry.getIdentifier(clazz.constructor);\n  return [\"class\", identifier];\n}, (clazz, superJson) => {\n  const allowedProps = superJson.classRegistry.getAllowedProps(clazz.constructor);\n  if (!allowedProps) {\n    return { ...clazz };\n  }\n  const result = {};\n  allowedProps.forEach((prop) => {\n    result[prop] = clazz[prop];\n  });\n  return result;\n}, (v, a, superJson) => {\n  const clazz = superJson.classRegistry.getValue(a[1]);\n  if (!clazz) {\n    throw new Error(`Trying to deserialize unknown class '${a[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);\n  }\n  return Object.assign(Object.create(clazz.prototype), v);\n});\nvar customRule = compositeTransformation((value, superJson) => {\n  return !!superJson.customTransformerRegistry.findApplicable(value);\n}, (value, superJson) => {\n  const transformer = superJson.customTransformerRegistry.findApplicable(value);\n  return [\"custom\", transformer.name];\n}, (value, superJson) => {\n  const transformer = superJson.customTransformerRegistry.findApplicable(value);\n  return transformer.serialize(value);\n}, (v, a, superJson) => {\n  const transformer = superJson.customTransformerRegistry.findByName(a[1]);\n  if (!transformer) {\n    throw new Error(\"Trying to deserialize unknown custom value\");\n  }\n  return transformer.deserialize(v);\n});\nvar compositeRules = [classRule, symbolRule, customRule, typedArrayRule];\nvar transformValue = (value, superJson) => {\n  const applicableCompositeRule = findArr(compositeRules, (rule) => rule.isApplicable(value, superJson));\n  if (applicableCompositeRule) {\n    return {\n      value: applicableCompositeRule.transform(value, superJson),\n      type: applicableCompositeRule.annotation(value, superJson)\n    };\n  }\n  const applicableSimpleRule = findArr(simpleRules, (rule) => rule.isApplicable(value, superJson));\n  if (applicableSimpleRule) {\n    return {\n      value: applicableSimpleRule.transform(value, superJson),\n      type: applicableSimpleRule.annotation\n    };\n  }\n  return void 0;\n};\nvar simpleRulesByAnnotation = {};\nsimpleRules.forEach((rule) => {\n  simpleRulesByAnnotation[rule.annotation] = rule;\n});\nvar untransformValue = (json, type, superJson) => {\n  if (isArray(type)) {\n    switch (type[0]) {\n      case \"symbol\":\n        return symbolRule.untransform(json, type, superJson);\n      case \"class\":\n        return classRule.untransform(json, type, superJson);\n      case \"custom\":\n        return customRule.untransform(json, type, superJson);\n      case \"typed-array\":\n        return typedArrayRule.untransform(json, type, superJson);\n      default:\n        throw new Error(\"Unknown transformation: \" + type);\n    }\n  } else {\n    const transformation = simpleRulesByAnnotation[type];\n    if (!transformation) {\n      throw new Error(\"Unknown transformation: \" + type);\n    }\n    return transformation.untransform(json, superJson);\n  }\n};\ninit_esm_shims2();\nvar getNthKey = (value, n) => {\n  if (n > value.size)\n    throw new Error(\"index out of bounds\");\n  const keys = value.keys();\n  while (n > 0) {\n    keys.next();\n    n--;\n  }\n  return keys.next().value;\n};\nfunction validatePath(path) {\n  if (includes(path, \"__proto__\")) {\n    throw new Error(\"__proto__ is not allowed as a property\");\n  }\n  if (includes(path, \"prototype\")) {\n    throw new Error(\"prototype is not allowed as a property\");\n  }\n  if (includes(path, \"constructor\")) {\n    throw new Error(\"constructor is not allowed as a property\");\n  }\n}\nvar getDeep = (object, path) => {\n  validatePath(path);\n  for (let i = 0; i < path.length; i++) {\n    const key = path[i];\n    if (isSet(object)) {\n      object = getNthKey(object, +key);\n    } else if (isMap(object)) {\n      const row = +key;\n      const type = +path[++i] === 0 ? \"key\" : \"value\";\n      const keyOfRow = getNthKey(object, row);\n      switch (type) {\n        case \"key\":\n          object = keyOfRow;\n          break;\n        case \"value\":\n          object = object.get(keyOfRow);\n          break;\n      }\n    } else {\n      object = object[key];\n    }\n  }\n  return object;\n};\nvar setDeep = (object, path, mapper) => {\n  validatePath(path);\n  if (path.length === 0) {\n    return mapper(object);\n  }\n  let parent = object;\n  for (let i = 0; i < path.length - 1; i++) {\n    const key = path[i];\n    if (isArray(parent)) {\n      const index = +key;\n      parent = parent[index];\n    } else if (isPlainObject2(parent)) {\n      parent = parent[key];\n    } else if (isSet(parent)) {\n      const row = +key;\n      parent = getNthKey(parent, row);\n    } else if (isMap(parent)) {\n      const isEnd = i === path.length - 2;\n      if (isEnd) {\n        break;\n      }\n      const row = +key;\n      const type = +path[++i] === 0 ? \"key\" : \"value\";\n      const keyOfRow = getNthKey(parent, row);\n      switch (type) {\n        case \"key\":\n          parent = keyOfRow;\n          break;\n        case \"value\":\n          parent = parent.get(keyOfRow);\n          break;\n      }\n    }\n  }\n  const lastKey = path[path.length - 1];\n  if (isArray(parent)) {\n    parent[+lastKey] = mapper(parent[+lastKey]);\n  } else if (isPlainObject2(parent)) {\n    parent[lastKey] = mapper(parent[lastKey]);\n  }\n  if (isSet(parent)) {\n    const oldValue = getNthKey(parent, +lastKey);\n    const newValue = mapper(oldValue);\n    if (oldValue !== newValue) {\n      parent.delete(oldValue);\n      parent.add(newValue);\n    }\n  }\n  if (isMap(parent)) {\n    const row = +path[path.length - 2];\n    const keyToRow = getNthKey(parent, row);\n    const type = +lastKey === 0 ? \"key\" : \"value\";\n    switch (type) {\n      case \"key\": {\n        const newKey = mapper(keyToRow);\n        parent.set(newKey, parent.get(keyToRow));\n        if (newKey !== keyToRow) {\n          parent.delete(keyToRow);\n        }\n        break;\n      }\n      case \"value\": {\n        parent.set(keyToRow, mapper(parent.get(keyToRow)));\n        break;\n      }\n    }\n  }\n  return object;\n};\nfunction traverse(tree, walker2, origin = []) {\n  if (!tree) {\n    return;\n  }\n  if (!isArray(tree)) {\n    forEach(tree, (subtree, key) => traverse(subtree, walker2, [...origin, ...parsePath(key)]));\n    return;\n  }\n  const [nodeValue, children] = tree;\n  if (children) {\n    forEach(children, (child, key) => {\n      traverse(child, walker2, [...origin, ...parsePath(key)]);\n    });\n  }\n  walker2(nodeValue, origin);\n}\nfunction applyValueAnnotations(plain, annotations, superJson) {\n  traverse(annotations, (type, path) => {\n    plain = setDeep(plain, path, (v) => untransformValue(v, type, superJson));\n  });\n  return plain;\n}\nfunction applyReferentialEqualityAnnotations(plain, annotations) {\n  function apply(identicalPaths, path) {\n    const object = getDeep(plain, parsePath(path));\n    identicalPaths.map(parsePath).forEach((identicalObjectPath) => {\n      plain = setDeep(plain, identicalObjectPath, () => object);\n    });\n  }\n  if (isArray(annotations)) {\n    const [root, other] = annotations;\n    root.forEach((identicalPath) => {\n      plain = setDeep(plain, parsePath(identicalPath), () => plain);\n    });\n    if (other) {\n      forEach(other, apply);\n    }\n  } else {\n    forEach(annotations, apply);\n  }\n  return plain;\n}\nvar isDeep = (object, superJson) => isPlainObject2(object) || isArray(object) || isMap(object) || isSet(object) || isInstanceOfRegisteredClass(object, superJson);\nfunction addIdentity(object, path, identities) {\n  const existingSet = identities.get(object);\n  if (existingSet) {\n    existingSet.push(path);\n  } else {\n    identities.set(object, [path]);\n  }\n}\nfunction generateReferentialEqualityAnnotations(identitites, dedupe) {\n  const result = {};\n  let rootEqualityPaths = void 0;\n  identitites.forEach((paths) => {\n    if (paths.length <= 1) {\n      return;\n    }\n    if (!dedupe) {\n      paths = paths.map((path) => path.map(String)).sort((a, b) => a.length - b.length);\n    }\n    const [representativePath, ...identicalPaths] = paths;\n    if (representativePath.length === 0) {\n      rootEqualityPaths = identicalPaths.map(stringifyPath);\n    } else {\n      result[stringifyPath(representativePath)] = identicalPaths.map(stringifyPath);\n    }\n  });\n  if (rootEqualityPaths) {\n    if (isEmptyObject(result)) {\n      return [rootEqualityPaths];\n    } else {\n      return [rootEqualityPaths, result];\n    }\n  } else {\n    return isEmptyObject(result) ? void 0 : result;\n  }\n}\nvar walker = (object, identities, superJson, dedupe, path = [], objectsInThisPath = [], seenObjects = /* @__PURE__ */ new Map()) => {\n  var _a25;\n  const primitive = isPrimitive2(object);\n  if (!primitive) {\n    addIdentity(object, path, identities);\n    const seen = seenObjects.get(object);\n    if (seen) {\n      return dedupe ? {\n        transformedValue: null\n      } : seen;\n    }\n  }\n  if (!isDeep(object, superJson)) {\n    const transformed2 = transformValue(object, superJson);\n    const result2 = transformed2 ? {\n      transformedValue: transformed2.value,\n      annotations: [transformed2.type]\n    } : {\n      transformedValue: object\n    };\n    if (!primitive) {\n      seenObjects.set(object, result2);\n    }\n    return result2;\n  }\n  if (includes(objectsInThisPath, object)) {\n    return {\n      transformedValue: null\n    };\n  }\n  const transformationResult = transformValue(object, superJson);\n  const transformed = (_a25 = transformationResult == null ? void 0 : transformationResult.value) != null ? _a25 : object;\n  const transformedValue = isArray(transformed) ? [] : {};\n  const innerAnnotations = {};\n  forEach(transformed, (value, index) => {\n    if (index === \"__proto__\" || index === \"constructor\" || index === \"prototype\") {\n      throw new Error(`Detected property ${index}. This is a prototype pollution risk, please remove it from your object.`);\n    }\n    const recursiveResult = walker(value, identities, superJson, dedupe, [...path, index], [...objectsInThisPath, object], seenObjects);\n    transformedValue[index] = recursiveResult.transformedValue;\n    if (isArray(recursiveResult.annotations)) {\n      innerAnnotations[index] = recursiveResult.annotations;\n    } else if (isPlainObject2(recursiveResult.annotations)) {\n      forEach(recursiveResult.annotations, (tree, key) => {\n        innerAnnotations[escapeKey(index) + \".\" + key] = tree;\n      });\n    }\n  });\n  const result = isEmptyObject(innerAnnotations) ? {\n    transformedValue,\n    annotations: !!transformationResult ? [transformationResult.type] : void 0\n  } : {\n    transformedValue,\n    annotations: !!transformationResult ? [transformationResult.type, innerAnnotations] : innerAnnotations\n  };\n  if (!primitive) {\n    seenObjects.set(object, result);\n  }\n  return result;\n};\ninit_esm_shims2();\ninit_esm_shims2();\nfunction getType2(payload) {\n  return Object.prototype.toString.call(payload).slice(8, -1);\n}\nfunction isArray2(payload) {\n  return getType2(payload) === \"Array\";\n}\nfunction isPlainObject3(payload) {\n  if (getType2(payload) !== \"Object\")\n    return false;\n  const prototype = Object.getPrototypeOf(payload);\n  return !!prototype && prototype.constructor === Object && prototype === Object.prototype;\n}\nfunction isNull2(payload) {\n  return getType2(payload) === \"Null\";\n}\nfunction isOneOf(a, b, c, d, e) {\n  return (value) => a(value) || b(value) || !!c && c(value) || !!d && d(value) || !!e && e(value);\n}\nfunction isUndefined2(payload) {\n  return getType2(payload) === \"Undefined\";\n}\nvar isNullOrUndefined = isOneOf(isNull2, isUndefined2);\nfunction assignProp(carry, key, newVal, originalObject, includeNonenumerable) {\n  const propType = {}.propertyIsEnumerable.call(originalObject, key) ? \"enumerable\" : \"nonenumerable\";\n  if (propType === \"enumerable\")\n    carry[key] = newVal;\n  if (includeNonenumerable && propType === \"nonenumerable\") {\n    Object.defineProperty(carry, key, {\n      value: newVal,\n      enumerable: false,\n      writable: true,\n      configurable: true\n    });\n  }\n}\nfunction copy(target22, options = {}) {\n  if (isArray2(target22)) {\n    return target22.map((item) => copy(item, options));\n  }\n  if (!isPlainObject3(target22)) {\n    return target22;\n  }\n  const props = Object.getOwnPropertyNames(target22);\n  const symbols = Object.getOwnPropertySymbols(target22);\n  return [...props, ...symbols].reduce((carry, key) => {\n    if (isArray2(options.props) && !options.props.includes(key)) {\n      return carry;\n    }\n    const val = target22[key];\n    const newVal = copy(val, options);\n    assignProp(carry, key, newVal, target22, options.nonenumerable);\n    return carry;\n  }, {});\n}\nvar SuperJSON = class {\n  /**\n   * @param dedupeReferentialEqualities  If true, SuperJSON will make sure only one instance of referentially equal objects are serialized and the rest are replaced with `null`.\n   */\n  constructor({ dedupe = false } = {}) {\n    this.classRegistry = new ClassRegistry();\n    this.symbolRegistry = new Registry((s) => {\n      var _a25;\n      return (_a25 = s.description) != null ? _a25 : \"\";\n    });\n    this.customTransformerRegistry = new CustomTransformerRegistry();\n    this.allowedErrorProps = [];\n    this.dedupe = dedupe;\n  }\n  serialize(object) {\n    const identities = /* @__PURE__ */ new Map();\n    const output = walker(object, identities, this, this.dedupe);\n    const res = {\n      json: output.transformedValue\n    };\n    if (output.annotations) {\n      res.meta = {\n        ...res.meta,\n        values: output.annotations\n      };\n    }\n    const equalityAnnotations = generateReferentialEqualityAnnotations(identities, this.dedupe);\n    if (equalityAnnotations) {\n      res.meta = {\n        ...res.meta,\n        referentialEqualities: equalityAnnotations\n      };\n    }\n    return res;\n  }\n  deserialize(payload) {\n    const { json, meta } = payload;\n    let result = copy(json);\n    if (meta == null ? void 0 : meta.values) {\n      result = applyValueAnnotations(result, meta.values, this);\n    }\n    if (meta == null ? void 0 : meta.referentialEqualities) {\n      result = applyReferentialEqualityAnnotations(result, meta.referentialEqualities);\n    }\n    return result;\n  }\n  stringify(object) {\n    return JSON.stringify(this.serialize(object));\n  }\n  parse(string) {\n    return this.deserialize(JSON.parse(string));\n  }\n  registerClass(v, options) {\n    this.classRegistry.register(v, options);\n  }\n  registerSymbol(v, identifier) {\n    this.symbolRegistry.register(v, identifier);\n  }\n  registerCustom(transformer, name) {\n    this.customTransformerRegistry.register({\n      name,\n      ...transformer\n    });\n  }\n  allowErrorProps(...props) {\n    this.allowedErrorProps.push(...props);\n  }\n};\nSuperJSON.defaultInstance = new SuperJSON();\nSuperJSON.serialize = SuperJSON.defaultInstance.serialize.bind(SuperJSON.defaultInstance);\nSuperJSON.deserialize = SuperJSON.defaultInstance.deserialize.bind(SuperJSON.defaultInstance);\nSuperJSON.stringify = SuperJSON.defaultInstance.stringify.bind(SuperJSON.defaultInstance);\nSuperJSON.parse = SuperJSON.defaultInstance.parse.bind(SuperJSON.defaultInstance);\nSuperJSON.registerClass = SuperJSON.defaultInstance.registerClass.bind(SuperJSON.defaultInstance);\nSuperJSON.registerSymbol = SuperJSON.defaultInstance.registerSymbol.bind(SuperJSON.defaultInstance);\nSuperJSON.registerCustom = SuperJSON.defaultInstance.registerCustom.bind(SuperJSON.defaultInstance);\nSuperJSON.allowErrorProps = SuperJSON.defaultInstance.allowErrorProps.bind(SuperJSON.defaultInstance);\nvar serialize = SuperJSON.serialize;\nvar deserialize = SuperJSON.deserialize;\nvar stringify = SuperJSON.stringify;\nvar parse = SuperJSON.parse;\nvar registerClass = SuperJSON.registerClass;\nvar registerCustom = SuperJSON.registerCustom;\nvar registerSymbol = SuperJSON.registerSymbol;\nvar allowErrorProps = SuperJSON.allowErrorProps;\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar _a19;\nvar _b19;\n(_b19 = (_a19 = target).__VUE_DEVTOOLS_KIT_MESSAGE_CHANNELS__) != null ? _b19 : _a19.__VUE_DEVTOOLS_KIT_MESSAGE_CHANNELS__ = [];\nvar _a20;\nvar _b20;\n(_b20 = (_a20 = target).__VUE_DEVTOOLS_KIT_RPC_CLIENT__) != null ? _b20 : _a20.__VUE_DEVTOOLS_KIT_RPC_CLIENT__ = null;\nvar _a21;\nvar _b21;\n(_b21 = (_a21 = target).__VUE_DEVTOOLS_KIT_RPC_SERVER__) != null ? _b21 : _a21.__VUE_DEVTOOLS_KIT_RPC_SERVER__ = null;\nvar _a222;\nvar _b22;\n(_b22 = (_a222 = target).__VUE_DEVTOOLS_KIT_VITE_RPC_CLIENT__) != null ? _b22 : _a222.__VUE_DEVTOOLS_KIT_VITE_RPC_CLIENT__ = null;\nvar _a23;\nvar _b23;\n(_b23 = (_a23 = target).__VUE_DEVTOOLS_KIT_VITE_RPC_SERVER__) != null ? _b23 : _a23.__VUE_DEVTOOLS_KIT_VITE_RPC_SERVER__ = null;\nvar _a24;\nvar _b24;\n(_b24 = (_a24 = target).__VUE_DEVTOOLS_KIT_BROADCAST_RPC_SERVER__) != null ? _b24 : _a24.__VUE_DEVTOOLS_KIT_BROADCAST_RPC_SERVER__ = null;\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\ninit_esm_shims2();\nvar MAX_SERIALIZED_SIZE = 2 * 1024 * 1024;\nexport {\n  addCustomCommand,\n  addCustomTab,\n  onDevToolsClientConnected,\n  onDevToolsConnected,\n  removeCustomCommand,\n  setupDevToolsPlugin,\n  setupDevToolsPlugin as setupDevtoolsPlugin\n};\n//# sourceMappingURL=vitepress___@vue_devtools-api.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vitepress___@vueuse_core.js",
    "content": "import {\n  DefaultMagicKeysAliasMap,\n  StorageSerializers,\n  TransitionPresets,\n  assert,\n  breakpointsAntDesign,\n  breakpointsBootstrapV5,\n  breakpointsElement,\n  breakpointsMasterCss,\n  breakpointsPrimeFlex,\n  breakpointsQuasar,\n  breakpointsSematic,\n  breakpointsTailwind,\n  breakpointsVuetify,\n  breakpointsVuetifyV2,\n  breakpointsVuetifyV3,\n  bypassFilter,\n  camelize,\n  clamp,\n  cloneFnJSON,\n  computedAsync,\n  computedEager,\n  computedInject,\n  computedWithControl,\n  containsProp,\n  controlledRef,\n  createEventHook,\n  createFetch,\n  createFilterWrapper,\n  createGlobalState,\n  createInjectionState,\n  createReusableTemplate,\n  createSharedComposable,\n  createSingletonPromise,\n  createTemplatePromise,\n  createUnrefFn,\n  customStorageEventName,\n  debounceFilter,\n  defaultDocument,\n  defaultLocation,\n  defaultNavigator,\n  defaultWindow,\n  executeTransition,\n  extendRef,\n  formatDate,\n  formatTimeAgo,\n  get,\n  getLifeCycleTarget,\n  getSSRHandler,\n  hasOwn,\n  hyphenate,\n  identity,\n  increaseWithUnit,\n  injectLocal,\n  invoke,\n  isClient,\n  isDef,\n  isDefined,\n  isIOS,\n  isObject,\n  isWorker,\n  makeDestructurable,\n  mapGamepadToXbox360Controller,\n  noop,\n  normalizeDate,\n  notNullish,\n  now,\n  objectEntries,\n  objectOmit,\n  objectPick,\n  onClickOutside,\n  onElementRemoval,\n  onKeyDown,\n  onKeyPressed,\n  onKeyStroke,\n  onKeyUp,\n  onLongPress,\n  onStartTyping,\n  pausableFilter,\n  promiseTimeout,\n  provideLocal,\n  provideSSRWidth,\n  pxValue,\n  rand,\n  reactify,\n  reactifyObject,\n  reactiveComputed,\n  reactiveOmit,\n  reactivePick,\n  refAutoReset,\n  refDebounced,\n  refDefault,\n  refThrottled,\n  refWithControl,\n  resolveRef,\n  resolveUnref,\n  set,\n  setSSRHandler,\n  syncRef,\n  syncRefs,\n  templateRef,\n  throttleFilter,\n  timestamp,\n  toArray,\n  toReactive,\n  toRef,\n  toRefs,\n  toValue,\n  tryOnBeforeMount,\n  tryOnBeforeUnmount,\n  tryOnMounted,\n  tryOnScopeDispose,\n  tryOnUnmounted,\n  unrefElement,\n  until,\n  useActiveElement,\n  useAnimate,\n  useArrayDifference,\n  useArrayEvery,\n  useArrayFilter,\n  useArrayFind,\n  useArrayFindIndex,\n  useArrayFindLast,\n  useArrayIncludes,\n  useArrayJoin,\n  useArrayMap,\n  useArrayReduce,\n  useArraySome,\n  useArrayUnique,\n  useAsyncQueue,\n  useAsyncState,\n  useBase64,\n  useBattery,\n  useBluetooth,\n  useBreakpoints,\n  useBroadcastChannel,\n  useBrowserLocation,\n  useCached,\n  useClipboard,\n  useClipboardItems,\n  useCloned,\n  useColorMode,\n  useConfirmDialog,\n  useCountdown,\n  useCounter,\n  useCssVar,\n  useCurrentElement,\n  useCycleList,\n  useDark,\n  useDateFormat,\n  useDebounceFn,\n  useDebouncedRefHistory,\n  useDeviceMotion,\n  useDeviceOrientation,\n  useDevicePixelRatio,\n  useDevicesList,\n  useDisplayMedia,\n  useDocumentVisibility,\n  useDraggable,\n  useDropZone,\n  useElementBounding,\n  useElementByPoint,\n  useElementHover,\n  useElementSize,\n  useElementVisibility,\n  useEventBus,\n  useEventListener,\n  useEventSource,\n  useEyeDropper,\n  useFavicon,\n  useFetch,\n  useFileDialog,\n  useFileSystemAccess,\n  useFocus,\n  useFocusWithin,\n  useFps,\n  useFullscreen,\n  useGamepad,\n  useGeolocation,\n  useIdle,\n  useImage,\n  useInfiniteScroll,\n  useIntersectionObserver,\n  useInterval,\n  useIntervalFn,\n  useKeyModifier,\n  useLastChanged,\n  useLocalStorage,\n  useMagicKeys,\n  useManualRefHistory,\n  useMediaControls,\n  useMediaQuery,\n  useMemoize,\n  useMemory,\n  useMounted,\n  useMouse,\n  useMouseInElement,\n  useMousePressed,\n  useMutationObserver,\n  useNavigatorLanguage,\n  useNetwork,\n  useNow,\n  useObjectUrl,\n  useOffsetPagination,\n  useOnline,\n  usePageLeave,\n  useParallax,\n  useParentElement,\n  usePerformanceObserver,\n  usePermission,\n  usePointer,\n  usePointerLock,\n  usePointerSwipe,\n  usePreferredColorScheme,\n  usePreferredContrast,\n  usePreferredDark,\n  usePreferredLanguages,\n  usePreferredReducedMotion,\n  usePreferredReducedTransparency,\n  usePrevious,\n  useRafFn,\n  useRefHistory,\n  useResizeObserver,\n  useSSRWidth,\n  useScreenOrientation,\n  useScreenSafeArea,\n  useScriptTag,\n  useScroll,\n  useScrollLock,\n  useSessionStorage,\n  useShare,\n  useSorted,\n  useSpeechRecognition,\n  useSpeechSynthesis,\n  useStepper,\n  useStorage,\n  useStorageAsync,\n  useStyleTag,\n  useSupported,\n  useSwipe,\n  useTemplateRefsList,\n  useTextDirection,\n  useTextSelection,\n  useTextareaAutosize,\n  useThrottleFn,\n  useThrottledRefHistory,\n  useTimeAgo,\n  useTimeout,\n  useTimeoutFn,\n  useTimeoutPoll,\n  useTimestamp,\n  useTitle,\n  useToNumber,\n  useToString,\n  useToggle,\n  useTransition,\n  useUrlSearchParams,\n  useUserMedia,\n  useVModel,\n  useVModels,\n  useVibrate,\n  useVirtualList,\n  useWakeLock,\n  useWebNotification,\n  useWebSocket,\n  useWebWorker,\n  useWebWorkerFn,\n  useWindowFocus,\n  useWindowScroll,\n  useWindowSize,\n  watchArray,\n  watchAtMost,\n  watchDebounced,\n  watchDeep,\n  watchIgnorable,\n  watchImmediate,\n  watchOnce,\n  watchPausable,\n  watchThrottled,\n  watchTriggerable,\n  watchWithFilter,\n  whenever\n} from \"./chunk-KT7LHMJ2.js\";\nimport \"./chunk-CQOUZRMK.js\";\nexport {\n  DefaultMagicKeysAliasMap,\n  StorageSerializers,\n  TransitionPresets,\n  assert,\n  computedAsync as asyncComputed,\n  refAutoReset as autoResetRef,\n  breakpointsAntDesign,\n  breakpointsBootstrapV5,\n  breakpointsElement,\n  breakpointsMasterCss,\n  breakpointsPrimeFlex,\n  breakpointsQuasar,\n  breakpointsSematic,\n  breakpointsTailwind,\n  breakpointsVuetify,\n  breakpointsVuetifyV2,\n  breakpointsVuetifyV3,\n  bypassFilter,\n  camelize,\n  clamp,\n  cloneFnJSON,\n  computedAsync,\n  computedEager,\n  computedInject,\n  computedWithControl,\n  containsProp,\n  computedWithControl as controlledComputed,\n  controlledRef,\n  createEventHook,\n  createFetch,\n  createFilterWrapper,\n  createGlobalState,\n  createInjectionState,\n  reactify as createReactiveFn,\n  createReusableTemplate,\n  createSharedComposable,\n  createSingletonPromise,\n  createTemplatePromise,\n  createUnrefFn,\n  customStorageEventName,\n  debounceFilter,\n  refDebounced as debouncedRef,\n  watchDebounced as debouncedWatch,\n  defaultDocument,\n  defaultLocation,\n  defaultNavigator,\n  defaultWindow,\n  computedEager as eagerComputed,\n  executeTransition,\n  extendRef,\n  formatDate,\n  formatTimeAgo,\n  get,\n  getLifeCycleTarget,\n  getSSRHandler,\n  hasOwn,\n  hyphenate,\n  identity,\n  watchIgnorable as ignorableWatch,\n  increaseWithUnit,\n  injectLocal,\n  invoke,\n  isClient,\n  isDef,\n  isDefined,\n  isIOS,\n  isObject,\n  isWorker,\n  makeDestructurable,\n  mapGamepadToXbox360Controller,\n  noop,\n  normalizeDate,\n  notNullish,\n  now,\n  objectEntries,\n  objectOmit,\n  objectPick,\n  onClickOutside,\n  onElementRemoval,\n  onKeyDown,\n  onKeyPressed,\n  onKeyStroke,\n  onKeyUp,\n  onLongPress,\n  onStartTyping,\n  pausableFilter,\n  watchPausable as pausableWatch,\n  promiseTimeout,\n  provideLocal,\n  provideSSRWidth,\n  pxValue,\n  rand,\n  reactify,\n  reactifyObject,\n  reactiveComputed,\n  reactiveOmit,\n  reactivePick,\n  refAutoReset,\n  refDebounced,\n  refDefault,\n  refThrottled,\n  refWithControl,\n  resolveRef,\n  resolveUnref,\n  set,\n  setSSRHandler,\n  syncRef,\n  syncRefs,\n  templateRef,\n  throttleFilter,\n  refThrottled as throttledRef,\n  watchThrottled as throttledWatch,\n  timestamp,\n  toArray,\n  toReactive,\n  toRef,\n  toRefs,\n  toValue,\n  tryOnBeforeMount,\n  tryOnBeforeUnmount,\n  tryOnMounted,\n  tryOnScopeDispose,\n  tryOnUnmounted,\n  unrefElement,\n  until,\n  useActiveElement,\n  useAnimate,\n  useArrayDifference,\n  useArrayEvery,\n  useArrayFilter,\n  useArrayFind,\n  useArrayFindIndex,\n  useArrayFindLast,\n  useArrayIncludes,\n  useArrayJoin,\n  useArrayMap,\n  useArrayReduce,\n  useArraySome,\n  useArrayUnique,\n  useAsyncQueue,\n  useAsyncState,\n  useBase64,\n  useBattery,\n  useBluetooth,\n  useBreakpoints,\n  useBroadcastChannel,\n  useBrowserLocation,\n  useCached,\n  useClipboard,\n  useClipboardItems,\n  useCloned,\n  useColorMode,\n  useConfirmDialog,\n  useCountdown,\n  useCounter,\n  useCssVar,\n  useCurrentElement,\n  useCycleList,\n  useDark,\n  useDateFormat,\n  refDebounced as useDebounce,\n  useDebounceFn,\n  useDebouncedRefHistory,\n  useDeviceMotion,\n  useDeviceOrientation,\n  useDevicePixelRatio,\n  useDevicesList,\n  useDisplayMedia,\n  useDocumentVisibility,\n  useDraggable,\n  useDropZone,\n  useElementBounding,\n  useElementByPoint,\n  useElementHover,\n  useElementSize,\n  useElementVisibility,\n  useEventBus,\n  useEventListener,\n  useEventSource,\n  useEyeDropper,\n  useFavicon,\n  useFetch,\n  useFileDialog,\n  useFileSystemAccess,\n  useFocus,\n  useFocusWithin,\n  useFps,\n  useFullscreen,\n  useGamepad,\n  useGeolocation,\n  useIdle,\n  useImage,\n  useInfiniteScroll,\n  useIntersectionObserver,\n  useInterval,\n  useIntervalFn,\n  useKeyModifier,\n  useLastChanged,\n  useLocalStorage,\n  useMagicKeys,\n  useManualRefHistory,\n  useMediaControls,\n  useMediaQuery,\n  useMemoize,\n  useMemory,\n  useMounted,\n  useMouse,\n  useMouseInElement,\n  useMousePressed,\n  useMutationObserver,\n  useNavigatorLanguage,\n  useNetwork,\n  useNow,\n  useObjectUrl,\n  useOffsetPagination,\n  useOnline,\n  usePageLeave,\n  useParallax,\n  useParentElement,\n  usePerformanceObserver,\n  usePermission,\n  usePointer,\n  usePointerLock,\n  usePointerSwipe,\n  usePreferredColorScheme,\n  usePreferredContrast,\n  usePreferredDark,\n  usePreferredLanguages,\n  usePreferredReducedMotion,\n  usePreferredReducedTransparency,\n  usePrevious,\n  useRafFn,\n  useRefHistory,\n  useResizeObserver,\n  useSSRWidth,\n  useScreenOrientation,\n  useScreenSafeArea,\n  useScriptTag,\n  useScroll,\n  useScrollLock,\n  useSessionStorage,\n  useShare,\n  useSorted,\n  useSpeechRecognition,\n  useSpeechSynthesis,\n  useStepper,\n  useStorage,\n  useStorageAsync,\n  useStyleTag,\n  useSupported,\n  useSwipe,\n  useTemplateRefsList,\n  useTextDirection,\n  useTextSelection,\n  useTextareaAutosize,\n  refThrottled as useThrottle,\n  useThrottleFn,\n  useThrottledRefHistory,\n  useTimeAgo,\n  useTimeout,\n  useTimeoutFn,\n  useTimeoutPoll,\n  useTimestamp,\n  useTitle,\n  useToNumber,\n  useToString,\n  useToggle,\n  useTransition,\n  useUrlSearchParams,\n  useUserMedia,\n  useVModel,\n  useVModels,\n  useVibrate,\n  useVirtualList,\n  useWakeLock,\n  useWebNotification,\n  useWebSocket,\n  useWebWorker,\n  useWebWorkerFn,\n  useWindowFocus,\n  useWindowScroll,\n  useWindowSize,\n  watchArray,\n  watchAtMost,\n  watchDebounced,\n  watchDeep,\n  watchIgnorable,\n  watchImmediate,\n  watchOnce,\n  watchPausable,\n  watchThrottled,\n  watchTriggerable,\n  watchWithFilter,\n  whenever\n};\n//# sourceMappingURL=vitepress___@vueuse_core.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js",
    "content": "import {\n  notNullish,\n  toArray,\n  tryOnScopeDispose,\n  unrefElement\n} from \"./chunk-KT7LHMJ2.js\";\nimport {\n  computed,\n  ref,\n  toValue,\n  watch\n} from \"./chunk-CQOUZRMK.js\";\n\n// node_modules/.pnpm/tabbable@6.2.0/node_modules/tabbable/dist/index.esm.js\nvar candidateSelectors = [\"input:not([inert])\", \"select:not([inert])\", \"textarea:not([inert])\", \"a[href]:not([inert])\", \"button:not([inert])\", \"[tabindex]:not(slot):not([inert])\", \"audio[controls]:not([inert])\", \"video[controls]:not([inert])\", '[contenteditable]:not([contenteditable=\"false\"]):not([inert])', \"details>summary:first-of-type:not([inert])\", \"details:not([inert])\"];\nvar candidateSelector = candidateSelectors.join(\",\");\nvar NoElement = typeof Element === \"undefined\";\nvar matches = NoElement ? function() {\n} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;\nvar getRootNode = !NoElement && Element.prototype.getRootNode ? function(element) {\n  var _element$getRootNode;\n  return element === null || element === void 0 ? void 0 : (_element$getRootNode = element.getRootNode) === null || _element$getRootNode === void 0 ? void 0 : _element$getRootNode.call(element);\n} : function(element) {\n  return element === null || element === void 0 ? void 0 : element.ownerDocument;\n};\nvar isInert = function isInert2(node, lookUp) {\n  var _node$getAttribute;\n  if (lookUp === void 0) {\n    lookUp = true;\n  }\n  var inertAtt = node === null || node === void 0 ? void 0 : (_node$getAttribute = node.getAttribute) === null || _node$getAttribute === void 0 ? void 0 : _node$getAttribute.call(node, \"inert\");\n  var inert = inertAtt === \"\" || inertAtt === \"true\";\n  var result = inert || lookUp && node && isInert2(node.parentNode);\n  return result;\n};\nvar isContentEditable = function isContentEditable2(node) {\n  var _node$getAttribute2;\n  var attValue = node === null || node === void 0 ? void 0 : (_node$getAttribute2 = node.getAttribute) === null || _node$getAttribute2 === void 0 ? void 0 : _node$getAttribute2.call(node, \"contenteditable\");\n  return attValue === \"\" || attValue === \"true\";\n};\nvar getCandidates = function getCandidates2(el, includeContainer, filter) {\n  if (isInert(el)) {\n    return [];\n  }\n  var candidates = Array.prototype.slice.apply(el.querySelectorAll(candidateSelector));\n  if (includeContainer && matches.call(el, candidateSelector)) {\n    candidates.unshift(el);\n  }\n  candidates = candidates.filter(filter);\n  return candidates;\n};\nvar getCandidatesIteratively = function getCandidatesIteratively2(elements, includeContainer, options) {\n  var candidates = [];\n  var elementsToCheck = Array.from(elements);\n  while (elementsToCheck.length) {\n    var element = elementsToCheck.shift();\n    if (isInert(element, false)) {\n      continue;\n    }\n    if (element.tagName === \"SLOT\") {\n      var assigned = element.assignedElements();\n      var content = assigned.length ? assigned : element.children;\n      var nestedCandidates = getCandidatesIteratively2(content, true, options);\n      if (options.flatten) {\n        candidates.push.apply(candidates, nestedCandidates);\n      } else {\n        candidates.push({\n          scopeParent: element,\n          candidates: nestedCandidates\n        });\n      }\n    } else {\n      var validCandidate = matches.call(element, candidateSelector);\n      if (validCandidate && options.filter(element) && (includeContainer || !elements.includes(element))) {\n        candidates.push(element);\n      }\n      var shadowRoot = element.shadowRoot || // check for an undisclosed shadow\n      typeof options.getShadowRoot === \"function\" && options.getShadowRoot(element);\n      var validShadowRoot = !isInert(shadowRoot, false) && (!options.shadowRootFilter || options.shadowRootFilter(element));\n      if (shadowRoot && validShadowRoot) {\n        var _nestedCandidates = getCandidatesIteratively2(shadowRoot === true ? element.children : shadowRoot.children, true, options);\n        if (options.flatten) {\n          candidates.push.apply(candidates, _nestedCandidates);\n        } else {\n          candidates.push({\n            scopeParent: element,\n            candidates: _nestedCandidates\n          });\n        }\n      } else {\n        elementsToCheck.unshift.apply(elementsToCheck, element.children);\n      }\n    }\n  }\n  return candidates;\n};\nvar hasTabIndex = function hasTabIndex2(node) {\n  return !isNaN(parseInt(node.getAttribute(\"tabindex\"), 10));\n};\nvar getTabIndex = function getTabIndex2(node) {\n  if (!node) {\n    throw new Error(\"No node provided\");\n  }\n  if (node.tabIndex < 0) {\n    if ((/^(AUDIO|VIDEO|DETAILS)$/.test(node.tagName) || isContentEditable(node)) && !hasTabIndex(node)) {\n      return 0;\n    }\n  }\n  return node.tabIndex;\n};\nvar getSortOrderTabIndex = function getSortOrderTabIndex2(node, isScope) {\n  var tabIndex = getTabIndex(node);\n  if (tabIndex < 0 && isScope && !hasTabIndex(node)) {\n    return 0;\n  }\n  return tabIndex;\n};\nvar sortOrderedTabbables = function sortOrderedTabbables2(a, b) {\n  return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex;\n};\nvar isInput = function isInput2(node) {\n  return node.tagName === \"INPUT\";\n};\nvar isHiddenInput = function isHiddenInput2(node) {\n  return isInput(node) && node.type === \"hidden\";\n};\nvar isDetailsWithSummary = function isDetailsWithSummary2(node) {\n  var r = node.tagName === \"DETAILS\" && Array.prototype.slice.apply(node.children).some(function(child) {\n    return child.tagName === \"SUMMARY\";\n  });\n  return r;\n};\nvar getCheckedRadio = function getCheckedRadio2(nodes, form) {\n  for (var i = 0; i < nodes.length; i++) {\n    if (nodes[i].checked && nodes[i].form === form) {\n      return nodes[i];\n    }\n  }\n};\nvar isTabbableRadio = function isTabbableRadio2(node) {\n  if (!node.name) {\n    return true;\n  }\n  var radioScope = node.form || getRootNode(node);\n  var queryRadios = function queryRadios2(name) {\n    return radioScope.querySelectorAll('input[type=\"radio\"][name=\"' + name + '\"]');\n  };\n  var radioSet;\n  if (typeof window !== \"undefined\" && typeof window.CSS !== \"undefined\" && typeof window.CSS.escape === \"function\") {\n    radioSet = queryRadios(window.CSS.escape(node.name));\n  } else {\n    try {\n      radioSet = queryRadios(node.name);\n    } catch (err) {\n      console.error(\"Looks like you have a radio button with a name attribute containing invalid CSS selector characters and need the CSS.escape polyfill: %s\", err.message);\n      return false;\n    }\n  }\n  var checked = getCheckedRadio(radioSet, node.form);\n  return !checked || checked === node;\n};\nvar isRadio = function isRadio2(node) {\n  return isInput(node) && node.type === \"radio\";\n};\nvar isNonTabbableRadio = function isNonTabbableRadio2(node) {\n  return isRadio(node) && !isTabbableRadio(node);\n};\nvar isNodeAttached = function isNodeAttached2(node) {\n  var _nodeRoot;\n  var nodeRoot = node && getRootNode(node);\n  var nodeRootHost = (_nodeRoot = nodeRoot) === null || _nodeRoot === void 0 ? void 0 : _nodeRoot.host;\n  var attached = false;\n  if (nodeRoot && nodeRoot !== node) {\n    var _nodeRootHost, _nodeRootHost$ownerDo, _node$ownerDocument;\n    attached = !!((_nodeRootHost = nodeRootHost) !== null && _nodeRootHost !== void 0 && (_nodeRootHost$ownerDo = _nodeRootHost.ownerDocument) !== null && _nodeRootHost$ownerDo !== void 0 && _nodeRootHost$ownerDo.contains(nodeRootHost) || node !== null && node !== void 0 && (_node$ownerDocument = node.ownerDocument) !== null && _node$ownerDocument !== void 0 && _node$ownerDocument.contains(node));\n    while (!attached && nodeRootHost) {\n      var _nodeRoot2, _nodeRootHost2, _nodeRootHost2$ownerD;\n      nodeRoot = getRootNode(nodeRootHost);\n      nodeRootHost = (_nodeRoot2 = nodeRoot) === null || _nodeRoot2 === void 0 ? void 0 : _nodeRoot2.host;\n      attached = !!((_nodeRootHost2 = nodeRootHost) !== null && _nodeRootHost2 !== void 0 && (_nodeRootHost2$ownerD = _nodeRootHost2.ownerDocument) !== null && _nodeRootHost2$ownerD !== void 0 && _nodeRootHost2$ownerD.contains(nodeRootHost));\n    }\n  }\n  return attached;\n};\nvar isZeroArea = function isZeroArea2(node) {\n  var _node$getBoundingClie = node.getBoundingClientRect(), width = _node$getBoundingClie.width, height = _node$getBoundingClie.height;\n  return width === 0 && height === 0;\n};\nvar isHidden = function isHidden2(node, _ref) {\n  var displayCheck = _ref.displayCheck, getShadowRoot = _ref.getShadowRoot;\n  if (getComputedStyle(node).visibility === \"hidden\") {\n    return true;\n  }\n  var isDirectSummary = matches.call(node, \"details>summary:first-of-type\");\n  var nodeUnderDetails = isDirectSummary ? node.parentElement : node;\n  if (matches.call(nodeUnderDetails, \"details:not([open]) *\")) {\n    return true;\n  }\n  if (!displayCheck || displayCheck === \"full\" || displayCheck === \"legacy-full\") {\n    if (typeof getShadowRoot === \"function\") {\n      var originalNode = node;\n      while (node) {\n        var parentElement = node.parentElement;\n        var rootNode = getRootNode(node);\n        if (parentElement && !parentElement.shadowRoot && getShadowRoot(parentElement) === true) {\n          return isZeroArea(node);\n        } else if (node.assignedSlot) {\n          node = node.assignedSlot;\n        } else if (!parentElement && rootNode !== node.ownerDocument) {\n          node = rootNode.host;\n        } else {\n          node = parentElement;\n        }\n      }\n      node = originalNode;\n    }\n    if (isNodeAttached(node)) {\n      return !node.getClientRects().length;\n    }\n    if (displayCheck !== \"legacy-full\") {\n      return true;\n    }\n  } else if (displayCheck === \"non-zero-area\") {\n    return isZeroArea(node);\n  }\n  return false;\n};\nvar isDisabledFromFieldset = function isDisabledFromFieldset2(node) {\n  if (/^(INPUT|BUTTON|SELECT|TEXTAREA)$/.test(node.tagName)) {\n    var parentNode = node.parentElement;\n    while (parentNode) {\n      if (parentNode.tagName === \"FIELDSET\" && parentNode.disabled) {\n        for (var i = 0; i < parentNode.children.length; i++) {\n          var child = parentNode.children.item(i);\n          if (child.tagName === \"LEGEND\") {\n            return matches.call(parentNode, \"fieldset[disabled] *\") ? true : !child.contains(node);\n          }\n        }\n        return true;\n      }\n      parentNode = parentNode.parentElement;\n    }\n  }\n  return false;\n};\nvar isNodeMatchingSelectorFocusable = function isNodeMatchingSelectorFocusable2(options, node) {\n  if (node.disabled || // we must do an inert look up to filter out any elements inside an inert ancestor\n  //  because we're limited in the type of selectors we can use in JSDom (see related\n  //  note related to `candidateSelectors`)\n  isInert(node) || isHiddenInput(node) || isHidden(node, options) || // For a details element with a summary, the summary element gets the focus\n  isDetailsWithSummary(node) || isDisabledFromFieldset(node)) {\n    return false;\n  }\n  return true;\n};\nvar isNodeMatchingSelectorTabbable = function isNodeMatchingSelectorTabbable2(options, node) {\n  if (isNonTabbableRadio(node) || getTabIndex(node) < 0 || !isNodeMatchingSelectorFocusable(options, node)) {\n    return false;\n  }\n  return true;\n};\nvar isValidShadowRootTabbable = function isValidShadowRootTabbable2(shadowHostNode) {\n  var tabIndex = parseInt(shadowHostNode.getAttribute(\"tabindex\"), 10);\n  if (isNaN(tabIndex) || tabIndex >= 0) {\n    return true;\n  }\n  return false;\n};\nvar sortByOrder = function sortByOrder2(candidates) {\n  var regularTabbables = [];\n  var orderedTabbables = [];\n  candidates.forEach(function(item, i) {\n    var isScope = !!item.scopeParent;\n    var element = isScope ? item.scopeParent : item;\n    var candidateTabindex = getSortOrderTabIndex(element, isScope);\n    var elements = isScope ? sortByOrder2(item.candidates) : element;\n    if (candidateTabindex === 0) {\n      isScope ? regularTabbables.push.apply(regularTabbables, elements) : regularTabbables.push(element);\n    } else {\n      orderedTabbables.push({\n        documentOrder: i,\n        tabIndex: candidateTabindex,\n        item,\n        isScope,\n        content: elements\n      });\n    }\n  });\n  return orderedTabbables.sort(sortOrderedTabbables).reduce(function(acc, sortable) {\n    sortable.isScope ? acc.push.apply(acc, sortable.content) : acc.push(sortable.content);\n    return acc;\n  }, []).concat(regularTabbables);\n};\nvar tabbable = function tabbable2(container, options) {\n  options = options || {};\n  var candidates;\n  if (options.getShadowRoot) {\n    candidates = getCandidatesIteratively([container], options.includeContainer, {\n      filter: isNodeMatchingSelectorTabbable.bind(null, options),\n      flatten: false,\n      getShadowRoot: options.getShadowRoot,\n      shadowRootFilter: isValidShadowRootTabbable\n    });\n  } else {\n    candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorTabbable.bind(null, options));\n  }\n  return sortByOrder(candidates);\n};\nvar focusable = function focusable2(container, options) {\n  options = options || {};\n  var candidates;\n  if (options.getShadowRoot) {\n    candidates = getCandidatesIteratively([container], options.includeContainer, {\n      filter: isNodeMatchingSelectorFocusable.bind(null, options),\n      flatten: true,\n      getShadowRoot: options.getShadowRoot\n    });\n  } else {\n    candidates = getCandidates(container, options.includeContainer, isNodeMatchingSelectorFocusable.bind(null, options));\n  }\n  return candidates;\n};\nvar isTabbable = function isTabbable2(node, options) {\n  options = options || {};\n  if (!node) {\n    throw new Error(\"No node provided\");\n  }\n  if (matches.call(node, candidateSelector) === false) {\n    return false;\n  }\n  return isNodeMatchingSelectorTabbable(options, node);\n};\nvar focusableCandidateSelector = candidateSelectors.concat(\"iframe\").join(\",\");\nvar isFocusable = function isFocusable2(node, options) {\n  options = options || {};\n  if (!node) {\n    throw new Error(\"No node provided\");\n  }\n  if (matches.call(node, focusableCandidateSelector) === false) {\n    return false;\n  }\n  return isNodeMatchingSelectorFocusable(options, node);\n};\n\n// node_modules/.pnpm/focus-trap@7.6.4/node_modules/focus-trap/dist/focus-trap.esm.js\nfunction _arrayLikeToArray(r, a) {\n  (null == a || a > r.length) && (a = r.length);\n  for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];\n  return n;\n}\nfunction _arrayWithoutHoles(r) {\n  if (Array.isArray(r)) return _arrayLikeToArray(r);\n}\nfunction _defineProperty(e, r, t) {\n  return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {\n    value: t,\n    enumerable: true,\n    configurable: true,\n    writable: true\n  }) : e[r] = t, e;\n}\nfunction _iterableToArray(r) {\n  if (\"undefined\" != typeof Symbol && null != r[Symbol.iterator] || null != r[\"@@iterator\"]) return Array.from(r);\n}\nfunction _nonIterableSpread() {\n  throw new TypeError(\"Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\");\n}\nfunction ownKeys(e, r) {\n  var t = Object.keys(e);\n  if (Object.getOwnPropertySymbols) {\n    var o = Object.getOwnPropertySymbols(e);\n    r && (o = o.filter(function(r2) {\n      return Object.getOwnPropertyDescriptor(e, r2).enumerable;\n    })), t.push.apply(t, o);\n  }\n  return t;\n}\nfunction _objectSpread2(e) {\n  for (var r = 1; r < arguments.length; r++) {\n    var t = null != arguments[r] ? arguments[r] : {};\n    r % 2 ? ownKeys(Object(t), true).forEach(function(r2) {\n      _defineProperty(e, r2, t[r2]);\n    }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) {\n      Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2));\n    });\n  }\n  return e;\n}\nfunction _toConsumableArray(r) {\n  return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread();\n}\nfunction _toPrimitive(t, r) {\n  if (\"object\" != typeof t || !t) return t;\n  var e = t[Symbol.toPrimitive];\n  if (void 0 !== e) {\n    var i = e.call(t, r || \"default\");\n    if (\"object\" != typeof i) return i;\n    throw new TypeError(\"@@toPrimitive must return a primitive value.\");\n  }\n  return (\"string\" === r ? String : Number)(t);\n}\nfunction _toPropertyKey(t) {\n  var i = _toPrimitive(t, \"string\");\n  return \"symbol\" == typeof i ? i : i + \"\";\n}\nfunction _unsupportedIterableToArray(r, a) {\n  if (r) {\n    if (\"string\" == typeof r) return _arrayLikeToArray(r, a);\n    var t = {}.toString.call(r).slice(8, -1);\n    return \"Object\" === t && r.constructor && (t = r.constructor.name), \"Map\" === t || \"Set\" === t ? Array.from(r) : \"Arguments\" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;\n  }\n}\nvar activeFocusTraps = {\n  activateTrap: function activateTrap(trapStack, trap) {\n    if (trapStack.length > 0) {\n      var activeTrap = trapStack[trapStack.length - 1];\n      if (activeTrap !== trap) {\n        activeTrap._setPausedState(true);\n      }\n    }\n    var trapIndex = trapStack.indexOf(trap);\n    if (trapIndex === -1) {\n      trapStack.push(trap);\n    } else {\n      trapStack.splice(trapIndex, 1);\n      trapStack.push(trap);\n    }\n  },\n  deactivateTrap: function deactivateTrap(trapStack, trap) {\n    var trapIndex = trapStack.indexOf(trap);\n    if (trapIndex !== -1) {\n      trapStack.splice(trapIndex, 1);\n    }\n    if (trapStack.length > 0 && !trapStack[trapStack.length - 1]._isManuallyPaused()) {\n      trapStack[trapStack.length - 1]._setPausedState(false);\n    }\n  }\n};\nvar isSelectableInput = function isSelectableInput2(node) {\n  return node.tagName && node.tagName.toLowerCase() === \"input\" && typeof node.select === \"function\";\n};\nvar isEscapeEvent = function isEscapeEvent2(e) {\n  return (e === null || e === void 0 ? void 0 : e.key) === \"Escape\" || (e === null || e === void 0 ? void 0 : e.key) === \"Esc\" || (e === null || e === void 0 ? void 0 : e.keyCode) === 27;\n};\nvar isTabEvent = function isTabEvent2(e) {\n  return (e === null || e === void 0 ? void 0 : e.key) === \"Tab\" || (e === null || e === void 0 ? void 0 : e.keyCode) === 9;\n};\nvar isKeyForward = function isKeyForward2(e) {\n  return isTabEvent(e) && !e.shiftKey;\n};\nvar isKeyBackward = function isKeyBackward2(e) {\n  return isTabEvent(e) && e.shiftKey;\n};\nvar delay = function delay2(fn) {\n  return setTimeout(fn, 0);\n};\nvar valueOrHandler = function valueOrHandler2(value) {\n  for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {\n    params[_key - 1] = arguments[_key];\n  }\n  return typeof value === \"function\" ? value.apply(void 0, params) : value;\n};\nvar getActualTarget = function getActualTarget2(event) {\n  return event.target.shadowRoot && typeof event.composedPath === \"function\" ? event.composedPath()[0] : event.target;\n};\nvar internalTrapStack = [];\nvar createFocusTrap = function createFocusTrap2(elements, userOptions) {\n  var doc = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.document) || document;\n  var trapStack = (userOptions === null || userOptions === void 0 ? void 0 : userOptions.trapStack) || internalTrapStack;\n  var config = _objectSpread2({\n    returnFocusOnDeactivate: true,\n    escapeDeactivates: true,\n    delayInitialFocus: true,\n    isKeyForward,\n    isKeyBackward\n  }, userOptions);\n  var state = {\n    // containers given to createFocusTrap()\n    // @type {Array<HTMLElement>}\n    containers: [],\n    // list of objects identifying tabbable nodes in `containers` in the trap\n    // NOTE: it's possible that a group has no tabbable nodes if nodes get removed while the trap\n    //  is active, but the trap should never get to a state where there isn't at least one group\n    //  with at least one tabbable node in it (that would lead to an error condition that would\n    //  result in an error being thrown)\n    // @type {Array<{\n    //   container: HTMLElement,\n    //   tabbableNodes: Array<HTMLElement>, // empty if none\n    //   focusableNodes: Array<HTMLElement>, // empty if none\n    //   posTabIndexesFound: boolean,\n    //   firstTabbableNode: HTMLElement|undefined,\n    //   lastTabbableNode: HTMLElement|undefined,\n    //   firstDomTabbableNode: HTMLElement|undefined,\n    //   lastDomTabbableNode: HTMLElement|undefined,\n    //   nextTabbableNode: (node: HTMLElement, forward: boolean) => HTMLElement|undefined\n    // }>}\n    containerGroups: [],\n    // same order/length as `containers` list\n    // references to objects in `containerGroups`, but only those that actually have\n    //  tabbable nodes in them\n    // NOTE: same order as `containers` and `containerGroups`, but __not necessarily__\n    //  the same length\n    tabbableGroups: [],\n    nodeFocusedBeforeActivation: null,\n    mostRecentlyFocusedNode: null,\n    active: false,\n    paused: false,\n    manuallyPaused: false,\n    // timer ID for when delayInitialFocus is true and initial focus in this trap\n    //  has been delayed during activation\n    delayInitialFocusTimer: void 0,\n    // the most recent KeyboardEvent for the configured nav key (typically [SHIFT+]TAB), if any\n    recentNavEvent: void 0\n  };\n  var trap;\n  var getOption = function getOption2(configOverrideOptions, optionName, configOptionName) {\n    return configOverrideOptions && configOverrideOptions[optionName] !== void 0 ? configOverrideOptions[optionName] : config[configOptionName || optionName];\n  };\n  var findContainerIndex = function findContainerIndex2(element, event) {\n    var composedPath = typeof (event === null || event === void 0 ? void 0 : event.composedPath) === \"function\" ? event.composedPath() : void 0;\n    return state.containerGroups.findIndex(function(_ref) {\n      var container = _ref.container, tabbableNodes = _ref.tabbableNodes;\n      return container.contains(element) || // fall back to explicit tabbable search which will take into consideration any\n      //  web components if the `tabbableOptions.getShadowRoot` option was used for\n      //  the trap, enabling shadow DOM support in tabbable (`Node.contains()` doesn't\n      //  look inside web components even if open)\n      (composedPath === null || composedPath === void 0 ? void 0 : composedPath.includes(container)) || tabbableNodes.find(function(node) {\n        return node === element;\n      });\n    });\n  };\n  var getNodeForOption = function getNodeForOption2(optionName) {\n    var _ref2 = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, _ref2$hasFallback = _ref2.hasFallback, hasFallback = _ref2$hasFallback === void 0 ? false : _ref2$hasFallback, _ref2$params = _ref2.params, params = _ref2$params === void 0 ? [] : _ref2$params;\n    var optionValue = config[optionName];\n    if (typeof optionValue === \"function\") {\n      optionValue = optionValue.apply(void 0, _toConsumableArray(params));\n    }\n    if (optionValue === true) {\n      optionValue = void 0;\n    }\n    if (!optionValue) {\n      if (optionValue === void 0 || optionValue === false) {\n        return optionValue;\n      }\n      throw new Error(\"`\".concat(optionName, \"` was specified but was not a node, or did not return a node\"));\n    }\n    var node = optionValue;\n    if (typeof optionValue === \"string\") {\n      try {\n        node = doc.querySelector(optionValue);\n      } catch (err) {\n        throw new Error(\"`\".concat(optionName, '` appears to be an invalid selector; error=\"').concat(err.message, '\"'));\n      }\n      if (!node) {\n        if (!hasFallback) {\n          throw new Error(\"`\".concat(optionName, \"` as selector refers to no known node\"));\n        }\n      }\n    }\n    return node;\n  };\n  var getInitialFocusNode = function getInitialFocusNode2() {\n    var node = getNodeForOption(\"initialFocus\", {\n      hasFallback: true\n    });\n    if (node === false) {\n      return false;\n    }\n    if (node === void 0 || node && !isFocusable(node, config.tabbableOptions)) {\n      if (findContainerIndex(doc.activeElement) >= 0) {\n        node = doc.activeElement;\n      } else {\n        var firstTabbableGroup = state.tabbableGroups[0];\n        var firstTabbableNode = firstTabbableGroup && firstTabbableGroup.firstTabbableNode;\n        node = firstTabbableNode || getNodeForOption(\"fallbackFocus\");\n      }\n    } else if (node === null) {\n      node = getNodeForOption(\"fallbackFocus\");\n    }\n    if (!node) {\n      throw new Error(\"Your focus-trap needs to have at least one focusable element\");\n    }\n    return node;\n  };\n  var updateTabbableNodes = function updateTabbableNodes2() {\n    state.containerGroups = state.containers.map(function(container) {\n      var tabbableNodes = tabbable(container, config.tabbableOptions);\n      var focusableNodes = focusable(container, config.tabbableOptions);\n      var firstTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[0] : void 0;\n      var lastTabbableNode = tabbableNodes.length > 0 ? tabbableNodes[tabbableNodes.length - 1] : void 0;\n      var firstDomTabbableNode = focusableNodes.find(function(node) {\n        return isTabbable(node);\n      });\n      var lastDomTabbableNode = focusableNodes.slice().reverse().find(function(node) {\n        return isTabbable(node);\n      });\n      var posTabIndexesFound = !!tabbableNodes.find(function(node) {\n        return getTabIndex(node) > 0;\n      });\n      return {\n        container,\n        tabbableNodes,\n        focusableNodes,\n        /** True if at least one node with positive `tabindex` was found in this container. */\n        posTabIndexesFound,\n        /** First tabbable node in container, __tabindex__ order; `undefined` if none. */\n        firstTabbableNode,\n        /** Last tabbable node in container, __tabindex__ order; `undefined` if none. */\n        lastTabbableNode,\n        // NOTE: DOM order is NOT NECESSARILY \"document position\" order, but figuring that out\n        //  would require more than just https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition\n        //  because that API doesn't work with Shadow DOM as well as it should (@see\n        //  https://github.com/whatwg/dom/issues/320) and since this first/last is only needed, so far,\n        //  to address an edge case related to positive tabindex support, this seems like a much easier,\n        //  \"close enough most of the time\" alternative for positive tabindexes which should generally\n        //  be avoided anyway...\n        /** First tabbable node in container, __DOM__ order; `undefined` if none. */\n        firstDomTabbableNode,\n        /** Last tabbable node in container, __DOM__ order; `undefined` if none. */\n        lastDomTabbableNode,\n        /**\n         * Finds the __tabbable__ node that follows the given node in the specified direction,\n         *  in this container, if any.\n         * @param {HTMLElement} node\n         * @param {boolean} [forward] True if going in forward tab order; false if going\n         *  in reverse.\n         * @returns {HTMLElement|undefined} The next tabbable node, if any.\n         */\n        nextTabbableNode: function nextTabbableNode(node) {\n          var forward = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true;\n          var nodeIdx = tabbableNodes.indexOf(node);\n          if (nodeIdx < 0) {\n            if (forward) {\n              return focusableNodes.slice(focusableNodes.indexOf(node) + 1).find(function(el) {\n                return isTabbable(el);\n              });\n            }\n            return focusableNodes.slice(0, focusableNodes.indexOf(node)).reverse().find(function(el) {\n              return isTabbable(el);\n            });\n          }\n          return tabbableNodes[nodeIdx + (forward ? 1 : -1)];\n        }\n      };\n    });\n    state.tabbableGroups = state.containerGroups.filter(function(group) {\n      return group.tabbableNodes.length > 0;\n    });\n    if (state.tabbableGroups.length <= 0 && !getNodeForOption(\"fallbackFocus\")) {\n      throw new Error(\"Your focus-trap must have at least one container with at least one tabbable node in it at all times\");\n    }\n    if (state.containerGroups.find(function(g) {\n      return g.posTabIndexesFound;\n    }) && state.containerGroups.length > 1) {\n      throw new Error(\"At least one node with a positive tabindex was found in one of your focus-trap's multiple containers. Positive tabindexes are only supported in single-container focus-traps.\");\n    }\n  };\n  var _getActiveElement = function getActiveElement(el) {\n    var activeElement = el.activeElement;\n    if (!activeElement) {\n      return;\n    }\n    if (activeElement.shadowRoot && activeElement.shadowRoot.activeElement !== null) {\n      return _getActiveElement(activeElement.shadowRoot);\n    }\n    return activeElement;\n  };\n  var _tryFocus = function tryFocus(node) {\n    if (node === false) {\n      return;\n    }\n    if (node === _getActiveElement(document)) {\n      return;\n    }\n    if (!node || !node.focus) {\n      _tryFocus(getInitialFocusNode());\n      return;\n    }\n    node.focus({\n      preventScroll: !!config.preventScroll\n    });\n    state.mostRecentlyFocusedNode = node;\n    if (isSelectableInput(node)) {\n      node.select();\n    }\n  };\n  var getReturnFocusNode = function getReturnFocusNode2(previousActiveElement) {\n    var node = getNodeForOption(\"setReturnFocus\", {\n      params: [previousActiveElement]\n    });\n    return node ? node : node === false ? false : previousActiveElement;\n  };\n  var findNextNavNode = function findNextNavNode2(_ref3) {\n    var target = _ref3.target, event = _ref3.event, _ref3$isBackward = _ref3.isBackward, isBackward = _ref3$isBackward === void 0 ? false : _ref3$isBackward;\n    target = target || getActualTarget(event);\n    updateTabbableNodes();\n    var destinationNode = null;\n    if (state.tabbableGroups.length > 0) {\n      var containerIndex = findContainerIndex(target, event);\n      var containerGroup = containerIndex >= 0 ? state.containerGroups[containerIndex] : void 0;\n      if (containerIndex < 0) {\n        if (isBackward) {\n          destinationNode = state.tabbableGroups[state.tabbableGroups.length - 1].lastTabbableNode;\n        } else {\n          destinationNode = state.tabbableGroups[0].firstTabbableNode;\n        }\n      } else if (isBackward) {\n        var startOfGroupIndex = state.tabbableGroups.findIndex(function(_ref4) {\n          var firstTabbableNode = _ref4.firstTabbableNode;\n          return target === firstTabbableNode;\n        });\n        if (startOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target, false))) {\n          startOfGroupIndex = containerIndex;\n        }\n        if (startOfGroupIndex >= 0) {\n          var destinationGroupIndex = startOfGroupIndex === 0 ? state.tabbableGroups.length - 1 : startOfGroupIndex - 1;\n          var destinationGroup = state.tabbableGroups[destinationGroupIndex];\n          destinationNode = getTabIndex(target) >= 0 ? destinationGroup.lastTabbableNode : destinationGroup.lastDomTabbableNode;\n        } else if (!isTabEvent(event)) {\n          destinationNode = containerGroup.nextTabbableNode(target, false);\n        }\n      } else {\n        var lastOfGroupIndex = state.tabbableGroups.findIndex(function(_ref5) {\n          var lastTabbableNode = _ref5.lastTabbableNode;\n          return target === lastTabbableNode;\n        });\n        if (lastOfGroupIndex < 0 && (containerGroup.container === target || isFocusable(target, config.tabbableOptions) && !isTabbable(target, config.tabbableOptions) && !containerGroup.nextTabbableNode(target))) {\n          lastOfGroupIndex = containerIndex;\n        }\n        if (lastOfGroupIndex >= 0) {\n          var _destinationGroupIndex = lastOfGroupIndex === state.tabbableGroups.length - 1 ? 0 : lastOfGroupIndex + 1;\n          var _destinationGroup = state.tabbableGroups[_destinationGroupIndex];\n          destinationNode = getTabIndex(target) >= 0 ? _destinationGroup.firstTabbableNode : _destinationGroup.firstDomTabbableNode;\n        } else if (!isTabEvent(event)) {\n          destinationNode = containerGroup.nextTabbableNode(target);\n        }\n      }\n    } else {\n      destinationNode = getNodeForOption(\"fallbackFocus\");\n    }\n    return destinationNode;\n  };\n  var checkPointerDown = function checkPointerDown2(e) {\n    var target = getActualTarget(e);\n    if (findContainerIndex(target, e) >= 0) {\n      return;\n    }\n    if (valueOrHandler(config.clickOutsideDeactivates, e)) {\n      trap.deactivate({\n        // NOTE: by setting `returnFocus: false`, deactivate() will do nothing,\n        //  which will result in the outside click setting focus to the node\n        //  that was clicked (and if not focusable, to \"nothing\"); by setting\n        //  `returnFocus: true`, we'll attempt to re-focus the node originally-focused\n        //  on activation (or the configured `setReturnFocus` node), whether the\n        //  outside click was on a focusable node or not\n        returnFocus: config.returnFocusOnDeactivate\n      });\n      return;\n    }\n    if (valueOrHandler(config.allowOutsideClick, e)) {\n      return;\n    }\n    e.preventDefault();\n  };\n  var checkFocusIn = function checkFocusIn2(event) {\n    var target = getActualTarget(event);\n    var targetContained = findContainerIndex(target, event) >= 0;\n    if (targetContained || target instanceof Document) {\n      if (targetContained) {\n        state.mostRecentlyFocusedNode = target;\n      }\n    } else {\n      event.stopImmediatePropagation();\n      var nextNode;\n      var navAcrossContainers = true;\n      if (state.mostRecentlyFocusedNode) {\n        if (getTabIndex(state.mostRecentlyFocusedNode) > 0) {\n          var mruContainerIdx = findContainerIndex(state.mostRecentlyFocusedNode);\n          var tabbableNodes = state.containerGroups[mruContainerIdx].tabbableNodes;\n          if (tabbableNodes.length > 0) {\n            var mruTabIdx = tabbableNodes.findIndex(function(node) {\n              return node === state.mostRecentlyFocusedNode;\n            });\n            if (mruTabIdx >= 0) {\n              if (config.isKeyForward(state.recentNavEvent)) {\n                if (mruTabIdx + 1 < tabbableNodes.length) {\n                  nextNode = tabbableNodes[mruTabIdx + 1];\n                  navAcrossContainers = false;\n                }\n              } else {\n                if (mruTabIdx - 1 >= 0) {\n                  nextNode = tabbableNodes[mruTabIdx - 1];\n                  navAcrossContainers = false;\n                }\n              }\n            }\n          }\n        } else {\n          if (!state.containerGroups.some(function(g) {\n            return g.tabbableNodes.some(function(n) {\n              return getTabIndex(n) > 0;\n            });\n          })) {\n            navAcrossContainers = false;\n          }\n        }\n      } else {\n        navAcrossContainers = false;\n      }\n      if (navAcrossContainers) {\n        nextNode = findNextNavNode({\n          // move FROM the MRU node, not event-related node (which will be the node that is\n          //  outside the trap causing the focus escape we're trying to fix)\n          target: state.mostRecentlyFocusedNode,\n          isBackward: config.isKeyBackward(state.recentNavEvent)\n        });\n      }\n      if (nextNode) {\n        _tryFocus(nextNode);\n      } else {\n        _tryFocus(state.mostRecentlyFocusedNode || getInitialFocusNode());\n      }\n    }\n    state.recentNavEvent = void 0;\n  };\n  var checkKeyNav = function checkKeyNav2(event) {\n    var isBackward = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : false;\n    state.recentNavEvent = event;\n    var destinationNode = findNextNavNode({\n      event,\n      isBackward\n    });\n    if (destinationNode) {\n      if (isTabEvent(event)) {\n        event.preventDefault();\n      }\n      _tryFocus(destinationNode);\n    }\n  };\n  var checkTabKey = function checkTabKey2(event) {\n    if (config.isKeyForward(event) || config.isKeyBackward(event)) {\n      checkKeyNav(event, config.isKeyBackward(event));\n    }\n  };\n  var checkEscapeKey = function checkEscapeKey2(event) {\n    if (isEscapeEvent(event) && valueOrHandler(config.escapeDeactivates, event) !== false) {\n      event.preventDefault();\n      trap.deactivate();\n    }\n  };\n  var checkClick = function checkClick2(e) {\n    var target = getActualTarget(e);\n    if (findContainerIndex(target, e) >= 0) {\n      return;\n    }\n    if (valueOrHandler(config.clickOutsideDeactivates, e)) {\n      return;\n    }\n    if (valueOrHandler(config.allowOutsideClick, e)) {\n      return;\n    }\n    e.preventDefault();\n    e.stopImmediatePropagation();\n  };\n  var addListeners = function addListeners2() {\n    if (!state.active) {\n      return;\n    }\n    activeFocusTraps.activateTrap(trapStack, trap);\n    state.delayInitialFocusTimer = config.delayInitialFocus ? delay(function() {\n      _tryFocus(getInitialFocusNode());\n    }) : _tryFocus(getInitialFocusNode());\n    doc.addEventListener(\"focusin\", checkFocusIn, true);\n    doc.addEventListener(\"mousedown\", checkPointerDown, {\n      capture: true,\n      passive: false\n    });\n    doc.addEventListener(\"touchstart\", checkPointerDown, {\n      capture: true,\n      passive: false\n    });\n    doc.addEventListener(\"click\", checkClick, {\n      capture: true,\n      passive: false\n    });\n    doc.addEventListener(\"keydown\", checkTabKey, {\n      capture: true,\n      passive: false\n    });\n    doc.addEventListener(\"keydown\", checkEscapeKey);\n    return trap;\n  };\n  var removeListeners = function removeListeners2() {\n    if (!state.active) {\n      return;\n    }\n    doc.removeEventListener(\"focusin\", checkFocusIn, true);\n    doc.removeEventListener(\"mousedown\", checkPointerDown, true);\n    doc.removeEventListener(\"touchstart\", checkPointerDown, true);\n    doc.removeEventListener(\"click\", checkClick, true);\n    doc.removeEventListener(\"keydown\", checkTabKey, true);\n    doc.removeEventListener(\"keydown\", checkEscapeKey);\n    return trap;\n  };\n  var checkDomRemoval = function checkDomRemoval2(mutations) {\n    var isFocusedNodeRemoved = mutations.some(function(mutation) {\n      var removedNodes = Array.from(mutation.removedNodes);\n      return removedNodes.some(function(node) {\n        return node === state.mostRecentlyFocusedNode;\n      });\n    });\n    if (isFocusedNodeRemoved) {\n      _tryFocus(getInitialFocusNode());\n    }\n  };\n  var mutationObserver = typeof window !== \"undefined\" && \"MutationObserver\" in window ? new MutationObserver(checkDomRemoval) : void 0;\n  var updateObservedNodes = function updateObservedNodes2() {\n    if (!mutationObserver) {\n      return;\n    }\n    mutationObserver.disconnect();\n    if (state.active && !state.paused) {\n      state.containers.map(function(container) {\n        mutationObserver.observe(container, {\n          subtree: true,\n          childList: true\n        });\n      });\n    }\n  };\n  trap = {\n    get active() {\n      return state.active;\n    },\n    get paused() {\n      return state.paused;\n    },\n    activate: function activate(activateOptions) {\n      if (state.active) {\n        return this;\n      }\n      var onActivate = getOption(activateOptions, \"onActivate\");\n      var onPostActivate = getOption(activateOptions, \"onPostActivate\");\n      var checkCanFocusTrap = getOption(activateOptions, \"checkCanFocusTrap\");\n      if (!checkCanFocusTrap) {\n        updateTabbableNodes();\n      }\n      state.active = true;\n      state.paused = false;\n      state.nodeFocusedBeforeActivation = doc.activeElement;\n      onActivate === null || onActivate === void 0 || onActivate();\n      var finishActivation = function finishActivation2() {\n        if (checkCanFocusTrap) {\n          updateTabbableNodes();\n        }\n        addListeners();\n        updateObservedNodes();\n        onPostActivate === null || onPostActivate === void 0 || onPostActivate();\n      };\n      if (checkCanFocusTrap) {\n        checkCanFocusTrap(state.containers.concat()).then(finishActivation, finishActivation);\n        return this;\n      }\n      finishActivation();\n      return this;\n    },\n    deactivate: function deactivate(deactivateOptions) {\n      if (!state.active) {\n        return this;\n      }\n      var options = _objectSpread2({\n        onDeactivate: config.onDeactivate,\n        onPostDeactivate: config.onPostDeactivate,\n        checkCanReturnFocus: config.checkCanReturnFocus\n      }, deactivateOptions);\n      clearTimeout(state.delayInitialFocusTimer);\n      state.delayInitialFocusTimer = void 0;\n      removeListeners();\n      state.active = false;\n      state.paused = false;\n      updateObservedNodes();\n      activeFocusTraps.deactivateTrap(trapStack, trap);\n      var onDeactivate = getOption(options, \"onDeactivate\");\n      var onPostDeactivate = getOption(options, \"onPostDeactivate\");\n      var checkCanReturnFocus = getOption(options, \"checkCanReturnFocus\");\n      var returnFocus = getOption(options, \"returnFocus\", \"returnFocusOnDeactivate\");\n      onDeactivate === null || onDeactivate === void 0 || onDeactivate();\n      var finishDeactivation = function finishDeactivation2() {\n        delay(function() {\n          if (returnFocus) {\n            _tryFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation));\n          }\n          onPostDeactivate === null || onPostDeactivate === void 0 || onPostDeactivate();\n        });\n      };\n      if (returnFocus && checkCanReturnFocus) {\n        checkCanReturnFocus(getReturnFocusNode(state.nodeFocusedBeforeActivation)).then(finishDeactivation, finishDeactivation);\n        return this;\n      }\n      finishDeactivation();\n      return this;\n    },\n    pause: function pause(pauseOptions) {\n      if (!state.active) {\n        return this;\n      }\n      state.manuallyPaused = true;\n      return this._setPausedState(true, pauseOptions);\n    },\n    unpause: function unpause(unpauseOptions) {\n      if (!state.active) {\n        return this;\n      }\n      state.manuallyPaused = false;\n      if (trapStack[trapStack.length - 1] !== this) {\n        return this;\n      }\n      return this._setPausedState(false, unpauseOptions);\n    },\n    updateContainerElements: function updateContainerElements(containerElements) {\n      var elementsAsArray = [].concat(containerElements).filter(Boolean);\n      state.containers = elementsAsArray.map(function(element) {\n        return typeof element === \"string\" ? doc.querySelector(element) : element;\n      });\n      if (state.active) {\n        updateTabbableNodes();\n      }\n      updateObservedNodes();\n      return this;\n    }\n  };\n  Object.defineProperties(trap, {\n    _isManuallyPaused: {\n      value: function value() {\n        return state.manuallyPaused;\n      }\n    },\n    _setPausedState: {\n      value: function value(paused, options) {\n        if (state.paused === paused) {\n          return this;\n        }\n        state.paused = paused;\n        if (paused) {\n          var onPause = getOption(options, \"onPause\");\n          var onPostPause = getOption(options, \"onPostPause\");\n          onPause === null || onPause === void 0 || onPause();\n          removeListeners();\n          updateObservedNodes();\n          onPostPause === null || onPostPause === void 0 || onPostPause();\n        } else {\n          var onUnpause = getOption(options, \"onUnpause\");\n          var onPostUnpause = getOption(options, \"onPostUnpause\");\n          onUnpause === null || onUnpause === void 0 || onUnpause();\n          updateTabbableNodes();\n          addListeners();\n          updateObservedNodes();\n          onPostUnpause === null || onPostUnpause === void 0 || onPostUnpause();\n        }\n        return this;\n      }\n    }\n  });\n  trap.updateContainerElements(elements);\n  return trap;\n};\n\n// node_modules/.pnpm/@vueuse+integrations@12.5.0_focus-trap@7.6.4/node_modules/@vueuse/integrations/useFocusTrap.mjs\nfunction useFocusTrap(target, options = {}) {\n  let trap;\n  const { immediate, ...focusTrapOptions } = options;\n  const hasFocus = ref(false);\n  const isPaused = ref(false);\n  const activate = (opts) => trap && trap.activate(opts);\n  const deactivate = (opts) => trap && trap.deactivate(opts);\n  const pause = () => {\n    if (trap) {\n      trap.pause();\n      isPaused.value = true;\n    }\n  };\n  const unpause = () => {\n    if (trap) {\n      trap.unpause();\n      isPaused.value = false;\n    }\n  };\n  const targets = computed(() => {\n    const _targets = toValue(target);\n    return toArray(_targets).map((el) => {\n      const _el = toValue(el);\n      return typeof _el === \"string\" ? _el : unrefElement(_el);\n    }).filter(notNullish);\n  });\n  watch(\n    targets,\n    (els) => {\n      if (!els.length)\n        return;\n      trap = createFocusTrap(els, {\n        ...focusTrapOptions,\n        onActivate() {\n          hasFocus.value = true;\n          if (options.onActivate)\n            options.onActivate();\n        },\n        onDeactivate() {\n          hasFocus.value = false;\n          if (options.onDeactivate)\n            options.onDeactivate();\n        }\n      });\n      if (immediate)\n        activate();\n    },\n    { flush: \"post\" }\n  );\n  tryOnScopeDispose(() => deactivate());\n  return {\n    hasFocus,\n    isPaused,\n    activate,\n    deactivate,\n    pause,\n    unpause\n  };\n}\nexport {\n  useFocusTrap\n};\n/*! Bundled license information:\n\ntabbable/dist/index.esm.js:\n  (*!\n  * tabbable 6.2.0\n  * @license MIT, https://github.com/focus-trap/tabbable/blob/master/LICENSE\n  *)\n\nfocus-trap/dist/focus-trap.esm.js:\n  (*!\n  * focus-trap 7.6.4\n  * @license MIT, https://github.com/focus-trap/focus-trap/blob/master/LICENSE\n  *)\n*/\n//# sourceMappingURL=vitepress___@vueuse_integrations_useFocusTrap.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js",
    "content": "// node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/lib/domiterator.js\nvar DOMIterator = class _DOMIterator {\n  /**\n   * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM\n   * element, an array of DOM elements, a NodeList or a selector\n   * @param {boolean} [iframes=true] - A boolean indicating if iframes should\n   * be handled\n   * @param {string[]} [exclude=[]] - An array containing exclusion selectors\n   * for iframes\n   * @param {number} [iframesTimeout=5000] - A number indicating the ms to\n   * wait before an iframe should be skipped, in case the load event isn't\n   * fired. This also applies if the user is offline and the resource of the\n   * iframe is online (either by the browsers \"offline\" mode or because\n   * there's no internet connection)\n   */\n  constructor(ctx, iframes = true, exclude = [], iframesTimeout = 5e3) {\n    this.ctx = ctx;\n    this.iframes = iframes;\n    this.exclude = exclude;\n    this.iframesTimeout = iframesTimeout;\n  }\n  /**\n   * Checks if the specified DOM element matches the selector\n   * @param  {HTMLElement} element - The DOM element\n   * @param  {string|string[]} selector - The selector or an array with\n   * selectors\n   * @return {boolean}\n   * @access public\n   */\n  static matches(element, selector) {\n    const selectors = typeof selector === \"string\" ? [selector] : selector, fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector;\n    if (fn) {\n      let match = false;\n      selectors.every((sel) => {\n        if (fn.call(element, sel)) {\n          match = true;\n          return false;\n        }\n        return true;\n      });\n      return match;\n    } else {\n      return false;\n    }\n  }\n  /**\n   * Returns all contexts filtered by duplicates (even nested)\n   * @return {HTMLElement[]} - An array containing DOM contexts\n   * @access protected\n   */\n  getContexts() {\n    let ctx, filteredCtx = [];\n    if (typeof this.ctx === \"undefined\" || !this.ctx) {\n      ctx = [];\n    } else if (NodeList.prototype.isPrototypeOf(this.ctx)) {\n      ctx = Array.prototype.slice.call(this.ctx);\n    } else if (Array.isArray(this.ctx)) {\n      ctx = this.ctx;\n    } else if (typeof this.ctx === \"string\") {\n      ctx = Array.prototype.slice.call(\n        document.querySelectorAll(this.ctx)\n      );\n    } else {\n      ctx = [this.ctx];\n    }\n    ctx.forEach((ctx2) => {\n      const isDescendant = filteredCtx.filter((contexts) => {\n        return contexts.contains(ctx2);\n      }).length > 0;\n      if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) {\n        filteredCtx.push(ctx2);\n      }\n    });\n    return filteredCtx;\n  }\n  /**\n   * @callback DOMIterator~getIframeContentsSuccessCallback\n   * @param {HTMLDocument} contents - The contentDocument of the iframe\n   */\n  /**\n   * Calls the success callback function with the iframe document. If it can't\n   * be accessed it calls the error callback function\n   * @param {HTMLElement} ifr - The iframe DOM element\n   * @param {DOMIterator~getIframeContentsSuccessCallback} successFn\n   * @param {function} [errorFn]\n   * @access protected\n   */\n  getIframeContents(ifr, successFn, errorFn = () => {\n  }) {\n    let doc;\n    try {\n      const ifrWin = ifr.contentWindow;\n      doc = ifrWin.document;\n      if (!ifrWin || !doc) {\n        throw new Error(\"iframe inaccessible\");\n      }\n    } catch (e) {\n      errorFn();\n    }\n    if (doc) {\n      successFn(doc);\n    }\n  }\n  /**\n   * Checks if an iframe is empty (if about:blank is the shown page)\n   * @param {HTMLElement} ifr - The iframe DOM element\n   * @return {boolean}\n   * @access protected\n   */\n  isIframeBlank(ifr) {\n    const bl = \"about:blank\", src = ifr.getAttribute(\"src\").trim(), href = ifr.contentWindow.location.href;\n    return href === bl && src !== bl && src;\n  }\n  /**\n   * Observes the onload event of an iframe and calls the success callback or\n   * the error callback if the iframe is inaccessible. If the event isn't\n   * fired within the specified {@link DOMIterator#iframesTimeout}, then it'll\n   * call the error callback too\n   * @param {HTMLElement} ifr - The iframe DOM element\n   * @param {DOMIterator~getIframeContentsSuccessCallback} successFn\n   * @param {function} errorFn\n   * @access protected\n   */\n  observeIframeLoad(ifr, successFn, errorFn) {\n    let called = false, tout = null;\n    const listener = () => {\n      if (called) {\n        return;\n      }\n      called = true;\n      clearTimeout(tout);\n      try {\n        if (!this.isIframeBlank(ifr)) {\n          ifr.removeEventListener(\"load\", listener);\n          this.getIframeContents(ifr, successFn, errorFn);\n        }\n      } catch (e) {\n        errorFn();\n      }\n    };\n    ifr.addEventListener(\"load\", listener);\n    tout = setTimeout(listener, this.iframesTimeout);\n  }\n  /**\n   * Callback when the iframe is ready\n   * @callback DOMIterator~onIframeReadySuccessCallback\n   * @param {HTMLDocument} contents - The contentDocument of the iframe\n   */\n  /**\n   * Callback if the iframe can't be accessed\n   * @callback DOMIterator~onIframeReadyErrorCallback\n   */\n  /**\n   * Calls the callback if the specified iframe is ready for DOM access\n   * @param  {HTMLElement} ifr - The iframe DOM element\n   * @param  {DOMIterator~onIframeReadySuccessCallback} successFn - Success\n   * callback\n   * @param {DOMIterator~onIframeReadyErrorCallback} errorFn - Error callback\n   * @see {@link http://stackoverflow.com/a/36155560/3894981} for\n   * background information\n   * @access protected\n   */\n  onIframeReady(ifr, successFn, errorFn) {\n    try {\n      if (ifr.contentWindow.document.readyState === \"complete\") {\n        if (this.isIframeBlank(ifr)) {\n          this.observeIframeLoad(ifr, successFn, errorFn);\n        } else {\n          this.getIframeContents(ifr, successFn, errorFn);\n        }\n      } else {\n        this.observeIframeLoad(ifr, successFn, errorFn);\n      }\n    } catch (e) {\n      errorFn();\n    }\n  }\n  /**\n   * Callback when all iframes are ready for DOM access\n   * @callback DOMIterator~waitForIframesDoneCallback\n   */\n  /**\n   * Iterates over all iframes and calls the done callback when all of them\n   * are ready for DOM access (including nested ones)\n   * @param {HTMLElement} ctx - The context DOM element\n   * @param {DOMIterator~waitForIframesDoneCallback} done - Done callback\n   */\n  waitForIframes(ctx, done) {\n    let eachCalled = 0;\n    this.forEachIframe(ctx, () => true, (ifr) => {\n      eachCalled++;\n      this.waitForIframes(ifr.querySelector(\"html\"), () => {\n        if (!--eachCalled) {\n          done();\n        }\n      });\n    }, (handled) => {\n      if (!handled) {\n        done();\n      }\n    });\n  }\n  /**\n   * Callback allowing to filter an iframe. Must return true when the element\n   * should remain, otherwise false\n   * @callback DOMIterator~forEachIframeFilterCallback\n   * @param {HTMLElement} iframe - The iframe DOM element\n   */\n  /**\n   * Callback for each iframe content\n   * @callback DOMIterator~forEachIframeEachCallback\n   * @param {HTMLElement} content - The iframe document\n   */\n  /**\n   * Callback if all iframes inside the context were handled\n   * @callback DOMIterator~forEachIframeEndCallback\n   * @param {number} handled - The number of handled iframes (those who\n   * wheren't filtered)\n   */\n  /**\n   * Iterates over all iframes inside the specified context and calls the\n   * callbacks when they're ready. Filters iframes based on the instance\n   * exclusion selectors\n   * @param {HTMLElement} ctx - The context DOM element\n   * @param {DOMIterator~forEachIframeFilterCallback} filter - Filter callback\n   * @param {DOMIterator~forEachIframeEachCallback} each - Each callback\n   * @param {DOMIterator~forEachIframeEndCallback} [end] - End callback\n   * @access protected\n   */\n  forEachIframe(ctx, filter, each, end = () => {\n  }) {\n    let ifr = ctx.querySelectorAll(\"iframe\"), open = ifr.length, handled = 0;\n    ifr = Array.prototype.slice.call(ifr);\n    const checkEnd = () => {\n      if (--open <= 0) {\n        end(handled);\n      }\n    };\n    if (!open) {\n      checkEnd();\n    }\n    ifr.forEach((ifr2) => {\n      if (_DOMIterator.matches(ifr2, this.exclude)) {\n        checkEnd();\n      } else {\n        this.onIframeReady(ifr2, (con) => {\n          if (filter(ifr2)) {\n            handled++;\n            each(con);\n          }\n          checkEnd();\n        }, checkEnd);\n      }\n    });\n  }\n  /**\n   * Creates a NodeIterator on the specified context\n   * @see {@link https://developer.mozilla.org/en/docs/Web/API/NodeIterator}\n   * @param {HTMLElement} ctx - The context DOM element\n   * @param {DOMIterator~whatToShow} whatToShow\n   * @param {DOMIterator~filterCb} filter\n   * @return {NodeIterator}\n   * @access protected\n   */\n  createIterator(ctx, whatToShow, filter) {\n    return document.createNodeIterator(ctx, whatToShow, filter, false);\n  }\n  /**\n   * Creates an instance of DOMIterator in an iframe\n   * @param {HTMLDocument} contents - Iframe document\n   * @return {DOMIterator}\n   * @access protected\n   */\n  createInstanceOnIframe(contents) {\n    return new _DOMIterator(contents.querySelector(\"html\"), this.iframes);\n  }\n  /**\n   * Checks if an iframe occurs between two nodes, more specifically if an\n   * iframe occurs before the specified node and after the specified prevNode\n   * @param {HTMLElement} node - The node that should occur after the iframe\n   * @param {HTMLElement} prevNode - The node that should occur before the\n   * iframe\n   * @param {HTMLElement} ifr - The iframe to check against\n   * @return {boolean}\n   * @access protected\n   */\n  compareNodeIframe(node, prevNode, ifr) {\n    const compCurr = node.compareDocumentPosition(ifr), prev = Node.DOCUMENT_POSITION_PRECEDING;\n    if (compCurr & prev) {\n      if (prevNode !== null) {\n        const compPrev = prevNode.compareDocumentPosition(ifr), after = Node.DOCUMENT_POSITION_FOLLOWING;\n        if (compPrev & after) {\n          return true;\n        }\n      } else {\n        return true;\n      }\n    }\n    return false;\n  }\n  /**\n   * @typedef {DOMIterator~getIteratorNodeReturn}\n   * @type {object.<string>}\n   * @property {HTMLElement} prevNode - The previous node or null if there is\n   * no\n   * @property {HTMLElement} node - The current node\n   */\n  /**\n   * Returns the previous and current node of the specified iterator\n   * @param {NodeIterator} itr - The iterator\n   * @return {DOMIterator~getIteratorNodeReturn}\n   * @access protected\n   */\n  getIteratorNode(itr) {\n    const prevNode = itr.previousNode();\n    let node;\n    if (prevNode === null) {\n      node = itr.nextNode();\n    } else {\n      node = itr.nextNode() && itr.nextNode();\n    }\n    return {\n      prevNode,\n      node\n    };\n  }\n  /**\n   * An array containing objects. The object key \"val\" contains an iframe\n   * DOM element. The object key \"handled\" contains a boolean indicating if\n   * the iframe was handled already.\n   * It wouldn't be enough to save all open or all already handled iframes.\n   * The information of open iframes is necessary because they may occur after\n   * all other text nodes (and compareNodeIframe would never be true). The\n   * information of already handled iframes is necessary as otherwise they may\n   * be handled multiple times\n   * @typedef DOMIterator~checkIframeFilterIfr\n   * @type {object[]}\n   */\n  /**\n   * Checks if an iframe wasn't handled already and if so, calls\n   * {@link DOMIterator#compareNodeIframe} to check if it should be handled.\n   * Information wheter an iframe was or wasn't handled is given within the\n   * <code>ifr</code> dictionary\n   * @param {HTMLElement} node - The node that should occur after the iframe\n   * @param {HTMLElement} prevNode - The node that should occur before the\n   * iframe\n   * @param {HTMLElement} currIfr - The iframe to check\n   * @param {DOMIterator~checkIframeFilterIfr} ifr - The iframe dictionary.\n   * Will be manipulated (by reference)\n   * @return {boolean} Returns true when it should be handled, otherwise false\n   * @access protected\n   */\n  checkIframeFilter(node, prevNode, currIfr, ifr) {\n    let key = false, handled = false;\n    ifr.forEach((ifrDict, i) => {\n      if (ifrDict.val === currIfr) {\n        key = i;\n        handled = ifrDict.handled;\n      }\n    });\n    if (this.compareNodeIframe(node, prevNode, currIfr)) {\n      if (key === false && !handled) {\n        ifr.push({\n          val: currIfr,\n          handled: true\n        });\n      } else if (key !== false && !handled) {\n        ifr[key].handled = true;\n      }\n      return true;\n    }\n    if (key === false) {\n      ifr.push({\n        val: currIfr,\n        handled: false\n      });\n    }\n    return false;\n  }\n  /**\n   * Creates an iterator on all open iframes in the specified array and calls\n   * the end callback when finished\n   * @param {DOMIterator~checkIframeFilterIfr} ifr\n   * @param {DOMIterator~whatToShow} whatToShow\n   * @param  {DOMIterator~forEachNodeCallback} eCb - Each callback\n   * @param {DOMIterator~filterCb} fCb\n   * @access protected\n   */\n  handleOpenIframes(ifr, whatToShow, eCb, fCb) {\n    ifr.forEach((ifrDict) => {\n      if (!ifrDict.handled) {\n        this.getIframeContents(ifrDict.val, (con) => {\n          this.createInstanceOnIframe(con).forEachNode(\n            whatToShow,\n            eCb,\n            fCb\n          );\n        });\n      }\n    });\n  }\n  /**\n   * Iterates through all nodes in the specified context and handles iframe\n   * nodes at the correct position\n   * @param {DOMIterator~whatToShow} whatToShow\n   * @param {HTMLElement} ctx - The context\n   * @param  {DOMIterator~forEachNodeCallback} eachCb - Each callback\n   * @param {DOMIterator~filterCb} filterCb - Filter callback\n   * @param {DOMIterator~forEachNodeEndCallback} doneCb - End callback\n   * @access protected\n   */\n  iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) {\n    const itr = this.createIterator(ctx, whatToShow, filterCb);\n    let ifr = [], elements = [], node, prevNode, retrieveNodes = () => {\n      ({\n        prevNode,\n        node\n      } = this.getIteratorNode(itr));\n      return node;\n    };\n    while (retrieveNodes()) {\n      if (this.iframes) {\n        this.forEachIframe(ctx, (currIfr) => {\n          return this.checkIframeFilter(node, prevNode, currIfr, ifr);\n        }, (con) => {\n          this.createInstanceOnIframe(con).forEachNode(\n            whatToShow,\n            (ifrNode) => elements.push(ifrNode),\n            filterCb\n          );\n        });\n      }\n      elements.push(node);\n    }\n    elements.forEach((node2) => {\n      eachCb(node2);\n    });\n    if (this.iframes) {\n      this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb);\n    }\n    doneCb();\n  }\n  /**\n   * Callback for each node\n   * @callback DOMIterator~forEachNodeCallback\n   * @param {HTMLElement} node - The DOM text node element\n   */\n  /**\n   * Callback if all contexts were handled\n   * @callback DOMIterator~forEachNodeEndCallback\n   */\n  /**\n   * Iterates over all contexts and initializes\n   * {@link DOMIterator#iterateThroughNodes iterateThroughNodes} on them\n   * @param {DOMIterator~whatToShow} whatToShow\n   * @param  {DOMIterator~forEachNodeCallback} each - Each callback\n   * @param {DOMIterator~filterCb} filter - Filter callback\n   * @param {DOMIterator~forEachNodeEndCallback} done - End callback\n   * @access public\n   */\n  forEachNode(whatToShow, each, filter, done = () => {\n  }) {\n    const contexts = this.getContexts();\n    let open = contexts.length;\n    if (!open) {\n      done();\n    }\n    contexts.forEach((ctx) => {\n      const ready = () => {\n        this.iterateThroughNodes(whatToShow, ctx, each, filter, () => {\n          if (--open <= 0) {\n            done();\n          }\n        });\n      };\n      if (this.iframes) {\n        this.waitForIframes(ctx, ready);\n      } else {\n        ready();\n      }\n    });\n  }\n  /**\n   * Callback to filter nodes. Can return e.g. NodeFilter.FILTER_ACCEPT or\n   * NodeFilter.FILTER_REJECT\n   * @see {@link http://tinyurl.com/zdczmm2}\n   * @callback DOMIterator~filterCb\n   * @param {HTMLElement} node - The node to filter\n   */\n  /**\n   * @typedef DOMIterator~whatToShow\n   * @see {@link http://tinyurl.com/zfqqkx2}\n   * @type {number}\n   */\n};\n\n// node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/lib/mark.js\nvar Mark = class {\n  // eslint-disable-line no-unused-vars\n  /**\n   * @param {HTMLElement|HTMLElement[]|NodeList|string} ctx - The context DOM\n   * element, an array of DOM elements, a NodeList or a selector\n   */\n  constructor(ctx) {\n    this.ctx = ctx;\n    this.ie = false;\n    const ua = window.navigator.userAgent;\n    if (ua.indexOf(\"MSIE\") > -1 || ua.indexOf(\"Trident\") > -1) {\n      this.ie = true;\n    }\n  }\n  /**\n   * Options defined by the user. They will be initialized from one of the\n   * public methods. See {@link Mark#mark}, {@link Mark#markRegExp},\n   * {@link Mark#markRanges} and {@link Mark#unmark} for option properties.\n   * @type {object}\n   * @param {object} [val] - An object that will be merged with defaults\n   * @access protected\n   */\n  set opt(val) {\n    this._opt = Object.assign({}, {\n      \"element\": \"\",\n      \"className\": \"\",\n      \"exclude\": [],\n      \"iframes\": false,\n      \"iframesTimeout\": 5e3,\n      \"separateWordSearch\": true,\n      \"diacritics\": true,\n      \"synonyms\": {},\n      \"accuracy\": \"partially\",\n      \"acrossElements\": false,\n      \"caseSensitive\": false,\n      \"ignoreJoiners\": false,\n      \"ignoreGroups\": 0,\n      \"ignorePunctuation\": [],\n      \"wildcards\": \"disabled\",\n      \"each\": () => {\n      },\n      \"noMatch\": () => {\n      },\n      \"filter\": () => true,\n      \"done\": () => {\n      },\n      \"debug\": false,\n      \"log\": window.console\n    }, val);\n  }\n  get opt() {\n    return this._opt;\n  }\n  /**\n   * An instance of DOMIterator\n   * @type {DOMIterator}\n   * @access protected\n   */\n  get iterator() {\n    return new DOMIterator(\n      this.ctx,\n      this.opt.iframes,\n      this.opt.exclude,\n      this.opt.iframesTimeout\n    );\n  }\n  /**\n   * Logs a message if log is enabled\n   * @param {string} msg - The message to log\n   * @param {string} [level=\"debug\"] - The log level, e.g. <code>warn</code>\n   * <code>error</code>, <code>debug</code>\n   * @access protected\n   */\n  log(msg, level = \"debug\") {\n    const log = this.opt.log;\n    if (!this.opt.debug) {\n      return;\n    }\n    if (typeof log === \"object\" && typeof log[level] === \"function\") {\n      log[level](`mark.js: ${msg}`);\n    }\n  }\n  /**\n   * Escapes a string for usage within a regular expression\n   * @param {string} str - The string to escape\n   * @return {string}\n   * @access protected\n   */\n  escapeStr(str) {\n    return str.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, \"\\\\$&\");\n  }\n  /**\n   * Creates a regular expression string to match the specified search\n   * term including synonyms, diacritics and accuracy if defined\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createRegExp(str) {\n    if (this.opt.wildcards !== \"disabled\") {\n      str = this.setupWildcardsRegExp(str);\n    }\n    str = this.escapeStr(str);\n    if (Object.keys(this.opt.synonyms).length) {\n      str = this.createSynonymsRegExp(str);\n    }\n    if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {\n      str = this.setupIgnoreJoinersRegExp(str);\n    }\n    if (this.opt.diacritics) {\n      str = this.createDiacriticsRegExp(str);\n    }\n    str = this.createMergedBlanksRegExp(str);\n    if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {\n      str = this.createJoinersRegExp(str);\n    }\n    if (this.opt.wildcards !== \"disabled\") {\n      str = this.createWildcardsRegExp(str);\n    }\n    str = this.createAccuracyRegExp(str);\n    return str;\n  }\n  /**\n   * Creates a regular expression string to match the defined synonyms\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createSynonymsRegExp(str) {\n    const syn = this.opt.synonyms, sens = this.opt.caseSensitive ? \"\" : \"i\", joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? \"\\0\" : \"\";\n    for (let index in syn) {\n      if (syn.hasOwnProperty(index)) {\n        const value = syn[index], k1 = this.opt.wildcards !== \"disabled\" ? this.setupWildcardsRegExp(index) : this.escapeStr(index), k2 = this.opt.wildcards !== \"disabled\" ? this.setupWildcardsRegExp(value) : this.escapeStr(value);\n        if (k1 !== \"\" && k2 !== \"\") {\n          str = str.replace(\n            new RegExp(\n              `(${this.escapeStr(k1)}|${this.escapeStr(k2)})`,\n              `gm${sens}`\n            ),\n            joinerPlaceholder + `(${this.processSynomyms(k1)}|${this.processSynomyms(k2)})` + joinerPlaceholder\n          );\n        }\n      }\n    }\n    return str;\n  }\n  /**\n   * Setup synonyms to work with ignoreJoiners and or ignorePunctuation\n   * @param {string} str - synonym key or value to process\n   * @return {string} - processed synonym string\n   */\n  processSynomyms(str) {\n    if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) {\n      str = this.setupIgnoreJoinersRegExp(str);\n    }\n    return str;\n  }\n  /**\n   * Sets up the regular expression string to allow later insertion of\n   * wildcard regular expression matches\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  setupWildcardsRegExp(str) {\n    str = str.replace(/(?:\\\\)*\\?/g, (val) => {\n      return val.charAt(0) === \"\\\\\" ? \"?\" : \"\u0001\";\n    });\n    return str.replace(/(?:\\\\)*\\*/g, (val) => {\n      return val.charAt(0) === \"\\\\\" ? \"*\" : \"\u0002\";\n    });\n  }\n  /**\n   * Sets up the regular expression string to allow later insertion of\n   * wildcard regular expression matches\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createWildcardsRegExp(str) {\n    let spaces = this.opt.wildcards === \"withSpaces\";\n    return str.replace(/\\u0001/g, spaces ? \"[\\\\S\\\\s]?\" : \"\\\\S?\").replace(/\\u0002/g, spaces ? \"[\\\\S\\\\s]*?\" : \"\\\\S*\");\n  }\n  /**\n   * Sets up the regular expression string to allow later insertion of\n   * designated characters (soft hyphens & zero width characters)\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  setupIgnoreJoinersRegExp(str) {\n    return str.replace(/[^(|)\\\\]/g, (val, indx, original) => {\n      let nextChar = original.charAt(indx + 1);\n      if (/[(|)\\\\]/.test(nextChar) || nextChar === \"\") {\n        return val;\n      } else {\n        return val + \"\\0\";\n      }\n    });\n  }\n  /**\n   * Creates a regular expression string to allow ignoring of designated\n   * characters (soft hyphens, zero width characters & punctuation) based on\n   * the specified option values of <code>ignorePunctuation</code> and\n   * <code>ignoreJoiners</code>\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createJoinersRegExp(str) {\n    let joiner = [];\n    const ignorePunctuation = this.opt.ignorePunctuation;\n    if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) {\n      joiner.push(this.escapeStr(ignorePunctuation.join(\"\")));\n    }\n    if (this.opt.ignoreJoiners) {\n      joiner.push(\"\\\\u00ad\\\\u200b\\\\u200c\\\\u200d\");\n    }\n    return joiner.length ? str.split(/\\u0000+/).join(`[${joiner.join(\"\")}]*`) : str;\n  }\n  /**\n   * Creates a regular expression string to match diacritics\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createDiacriticsRegExp(str) {\n    const sens = this.opt.caseSensitive ? \"\" : \"i\", dct = this.opt.caseSensitive ? [\n      \"aàáảãạăằắẳẵặâầấẩẫậäåāą\",\n      \"AÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ\",\n      \"cçćč\",\n      \"CÇĆČ\",\n      \"dđď\",\n      \"DĐĎ\",\n      \"eèéẻẽẹêềếểễệëěēę\",\n      \"EÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ\",\n      \"iìíỉĩịîïī\",\n      \"IÌÍỈĨỊÎÏĪ\",\n      \"lł\",\n      \"LŁ\",\n      \"nñňń\",\n      \"NÑŇŃ\",\n      \"oòóỏõọôồốổỗộơởỡớờợöøō\",\n      \"OÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ\",\n      \"rř\",\n      \"RŘ\",\n      \"sšśșş\",\n      \"SŠŚȘŞ\",\n      \"tťțţ\",\n      \"TŤȚŢ\",\n      \"uùúủũụưừứửữựûüůū\",\n      \"UÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ\",\n      \"yýỳỷỹỵÿ\",\n      \"YÝỲỶỸỴŸ\",\n      \"zžżź\",\n      \"ZŽŻŹ\"\n    ] : [\n      \"aàáảãạăằắẳẵặâầấẩẫậäåāąAÀÁẢÃẠĂẰẮẲẴẶÂẦẤẨẪẬÄÅĀĄ\",\n      \"cçćčCÇĆČ\",\n      \"dđďDĐĎ\",\n      \"eèéẻẽẹêềếểễệëěēęEÈÉẺẼẸÊỀẾỂỄỆËĚĒĘ\",\n      \"iìíỉĩịîïīIÌÍỈĨỊÎÏĪ\",\n      \"lłLŁ\",\n      \"nñňńNÑŇŃ\",\n      \"oòóỏõọôồốổỗộơởỡớờợöøōOÒÓỎÕỌÔỒỐỔỖỘƠỞỠỚỜỢÖØŌ\",\n      \"rřRŘ\",\n      \"sšśșşSŠŚȘŞ\",\n      \"tťțţTŤȚŢ\",\n      \"uùúủũụưừứửữựûüůūUÙÚỦŨỤƯỪỨỬỮỰÛÜŮŪ\",\n      \"yýỳỷỹỵÿYÝỲỶỸỴŸ\",\n      \"zžżźZŽŻŹ\"\n    ];\n    let handled = [];\n    str.split(\"\").forEach((ch) => {\n      dct.every((dct2) => {\n        if (dct2.indexOf(ch) !== -1) {\n          if (handled.indexOf(dct2) > -1) {\n            return false;\n          }\n          str = str.replace(\n            new RegExp(`[${dct2}]`, `gm${sens}`),\n            `[${dct2}]`\n          );\n          handled.push(dct2);\n        }\n        return true;\n      });\n    });\n    return str;\n  }\n  /**\n   * Creates a regular expression string that merges whitespace characters\n   * including subsequent ones into a single pattern, one or multiple\n   * whitespaces\n   * @param  {string} str - The search term to be used\n   * @return {string}\n   * @access protected\n   */\n  createMergedBlanksRegExp(str) {\n    return str.replace(/[\\s]+/gmi, \"[\\\\s]+\");\n  }\n  /**\n   * Creates a regular expression string to match the specified string with\n   * the defined accuracy. As in the regular expression of \"exactly\" can be\n   * a group containing a blank at the beginning, all regular expressions will\n   * be created with two groups. The first group can be ignored (may contain\n   * the said blank), the second contains the actual match\n   * @param  {string} str - The searm term to be used\n   * @return {str}\n   * @access protected\n   */\n  createAccuracyRegExp(str) {\n    const chars = \"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~¡¿\";\n    let acc = this.opt.accuracy, val = typeof acc === \"string\" ? acc : acc.value, ls = typeof acc === \"string\" ? [] : acc.limiters, lsJoin = \"\";\n    ls.forEach((limiter) => {\n      lsJoin += `|${this.escapeStr(limiter)}`;\n    });\n    switch (val) {\n      case \"partially\":\n      default:\n        return `()(${str})`;\n      case \"complementary\":\n        lsJoin = \"\\\\s\" + (lsJoin ? lsJoin : this.escapeStr(chars));\n        return `()([^${lsJoin}]*${str}[^${lsJoin}]*)`;\n      case \"exactly\":\n        return `(^|\\\\s${lsJoin})(${str})(?=$|\\\\s${lsJoin})`;\n    }\n  }\n  /**\n   * @typedef Mark~separatedKeywords\n   * @type {object.<string>}\n   * @property {array.<string>} keywords - The list of keywords\n   * @property {number} length - The length\n   */\n  /**\n   * Returns a list of keywords dependent on whether separate word search\n   * was defined. Also it filters empty keywords\n   * @param {array} sv - The array of keywords\n   * @return {Mark~separatedKeywords}\n   * @access protected\n   */\n  getSeparatedKeywords(sv) {\n    let stack = [];\n    sv.forEach((kw) => {\n      if (!this.opt.separateWordSearch) {\n        if (kw.trim() && stack.indexOf(kw) === -1) {\n          stack.push(kw);\n        }\n      } else {\n        kw.split(\" \").forEach((kwSplitted) => {\n          if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) {\n            stack.push(kwSplitted);\n          }\n        });\n      }\n    });\n    return {\n      // sort because of https://git.io/v6USg\n      \"keywords\": stack.sort((a, b) => {\n        return b.length - a.length;\n      }),\n      \"length\": stack.length\n    };\n  }\n  /**\n   * Check if a value is a number\n   * @param {number|string} value - the value to check;\n   * numeric strings allowed\n   * @return {boolean}\n   * @access protected\n   */\n  isNumeric(value) {\n    return Number(parseFloat(value)) == value;\n  }\n  /**\n   * @typedef Mark~rangeObject\n   * @type {object}\n   * @property {number} start - The start position within the composite value\n   * @property {number} length - The length of the string to mark within the\n   * composite value.\n   */\n  /**\n   * @typedef Mark~setOfRanges\n   * @type {object[]}\n   * @property {Mark~rangeObject}\n   */\n  /**\n   * Returns a processed list of integer offset indexes that do not overlap\n   * each other, and remove any string values or additional elements\n   * @param {Mark~setOfRanges} array - unprocessed raw array\n   * @return {Mark~setOfRanges} - processed array with any invalid entries\n   * removed\n   * @throws Will throw an error if an array of objects is not passed\n   * @access protected\n   */\n  checkRanges(array) {\n    if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== \"[object Object]\") {\n      this.log(\"markRanges() will only accept an array of objects\");\n      this.opt.noMatch(array);\n      return [];\n    }\n    const stack = [];\n    let last = 0;\n    array.sort((a, b) => {\n      return a.start - b.start;\n    }).forEach((item) => {\n      let { start, end, valid } = this.callNoMatchOnInvalidRanges(item, last);\n      if (valid) {\n        item.start = start;\n        item.length = end - start;\n        stack.push(item);\n        last = end;\n      }\n    });\n    return stack;\n  }\n  /**\n   * @typedef Mark~validObject\n   * @type {object}\n   * @property {number} start - The start position within the composite value\n   * @property {number} end - The calculated end position within the composite\n   * value.\n   * @property {boolean} valid - boolean value indicating that the start and\n   * calculated end range is valid\n   */\n  /**\n    * Initial validation of ranges for markRanges. Preliminary checks are done\n    * to ensure the start and length values exist and are not zero or non-\n    * numeric\n    * @param {Mark~rangeObject} range - the current range object\n    * @param {number} last - last index of range\n    * @return {Mark~validObject}\n    * @access protected\n    */\n  callNoMatchOnInvalidRanges(range, last) {\n    let start, end, valid = false;\n    if (range && typeof range.start !== \"undefined\") {\n      start = parseInt(range.start, 10);\n      end = start + parseInt(range.length, 10);\n      if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) {\n        valid = true;\n      } else {\n        this.log(\n          `Ignoring invalid or overlapping range: ${JSON.stringify(range)}`\n        );\n        this.opt.noMatch(range);\n      }\n    } else {\n      this.log(`Ignoring invalid range: ${JSON.stringify(range)}`);\n      this.opt.noMatch(range);\n    }\n    return {\n      start,\n      end,\n      valid\n    };\n  }\n  /**\n   * Check valid range for markRanges. Check ranges with access to the context\n   * string. Range values are double checked, lengths that extend the mark\n   * beyond the string length are limitied and ranges containing only\n   * whitespace are ignored\n   * @param {Mark~rangeObject} range - the current range object\n   * @param {number} originalLength - original length of the context string\n   * @param {string} string - current content string\n   * @return {Mark~validObject}\n   * @access protected\n   */\n  checkWhitespaceRanges(range, originalLength, string) {\n    let end, valid = true, max = string.length, offset = originalLength - max, start = parseInt(range.start, 10) - offset;\n    start = start > max ? max : start;\n    end = start + parseInt(range.length, 10);\n    if (end > max) {\n      end = max;\n      this.log(`End range automatically set to the max value of ${max}`);\n    }\n    if (start < 0 || end - start < 0 || start > max || end > max) {\n      valid = false;\n      this.log(`Invalid range: ${JSON.stringify(range)}`);\n      this.opt.noMatch(range);\n    } else if (string.substring(start, end).replace(/\\s+/g, \"\") === \"\") {\n      valid = false;\n      this.log(\"Skipping whitespace only range: \" + JSON.stringify(range));\n      this.opt.noMatch(range);\n    }\n    return {\n      start,\n      end,\n      valid\n    };\n  }\n  /**\n   * @typedef Mark~getTextNodesDict\n   * @type {object.<string>}\n   * @property {string} value - The composite value of all text nodes\n   * @property {object[]} nodes - An array of objects\n   * @property {number} nodes.start - The start position within the composite\n   * value\n   * @property {number} nodes.end - The end position within the composite\n   * value\n   * @property {HTMLElement} nodes.node - The DOM text node element\n   */\n  /**\n   * Callback\n   * @callback Mark~getTextNodesCallback\n   * @param {Mark~getTextNodesDict}\n   */\n  /**\n   * Calls the callback with an object containing all text nodes (including\n   * iframe text nodes) with start and end positions and the composite value\n   * of them (string)\n   * @param {Mark~getTextNodesCallback} cb - Callback\n   * @access protected\n   */\n  getTextNodes(cb) {\n    let val = \"\", nodes = [];\n    this.iterator.forEachNode(NodeFilter.SHOW_TEXT, (node) => {\n      nodes.push({\n        start: val.length,\n        end: (val += node.textContent).length,\n        node\n      });\n    }, (node) => {\n      if (this.matchesExclude(node.parentNode)) {\n        return NodeFilter.FILTER_REJECT;\n      } else {\n        return NodeFilter.FILTER_ACCEPT;\n      }\n    }, () => {\n      cb({\n        value: val,\n        nodes\n      });\n    });\n  }\n  /**\n   * Checks if an element matches any of the specified exclude selectors. Also\n   * it checks for elements in which no marks should be performed (e.g.\n   * script and style tags) and optionally already marked elements\n   * @param  {HTMLElement} el - The element to check\n   * @return {boolean}\n   * @access protected\n   */\n  matchesExclude(el) {\n    return DOMIterator.matches(el, this.opt.exclude.concat([\n      // ignores the elements itself, not their childrens (selector *)\n      \"script\",\n      \"style\",\n      \"title\",\n      \"head\",\n      \"html\"\n    ]));\n  }\n  /**\n   * Wraps the instance element and class around matches that fit the start\n   * and end positions within the node\n   * @param  {HTMLElement} node - The DOM text node\n   * @param  {number} start - The position where to start wrapping\n   * @param  {number} end - The position where to end wrapping\n   * @return {HTMLElement} Returns the splitted text node that will appear\n   * after the wrapped text node\n   * @access protected\n   */\n  wrapRangeInTextNode(node, start, end) {\n    const hEl = !this.opt.element ? \"mark\" : this.opt.element, startNode = node.splitText(start), ret = startNode.splitText(end - start);\n    let repl = document.createElement(hEl);\n    repl.setAttribute(\"data-markjs\", \"true\");\n    if (this.opt.className) {\n      repl.setAttribute(\"class\", this.opt.className);\n    }\n    repl.textContent = startNode.textContent;\n    startNode.parentNode.replaceChild(repl, startNode);\n    return ret;\n  }\n  /**\n   * @typedef Mark~wrapRangeInMappedTextNodeDict\n   * @type {object.<string>}\n   * @property {string} value - The composite value of all text nodes\n   * @property {object[]} nodes - An array of objects\n   * @property {number} nodes.start - The start position within the composite\n   * value\n   * @property {number} nodes.end - The end position within the composite\n   * value\n   * @property {HTMLElement} nodes.node - The DOM text node element\n   */\n  /**\n   * Each callback\n   * @callback Mark~wrapMatchesEachCallback\n   * @param {HTMLElement} node - The wrapped DOM element\n   * @param {number} lastIndex - The last matching position within the\n   * composite value of text nodes\n   */\n  /**\n   * Filter callback\n   * @callback Mark~wrapMatchesFilterCallback\n   * @param {HTMLElement} node - The matching text node DOM element\n   */\n  /**\n   * Determines matches by start and end positions using the text node\n   * dictionary even across text nodes and calls\n   * {@link Mark#wrapRangeInTextNode} to wrap them\n   * @param  {Mark~wrapRangeInMappedTextNodeDict} dict - The dictionary\n   * @param  {number} start - The start position of the match\n   * @param  {number} end - The end position of the match\n   * @param  {Mark~wrapMatchesFilterCallback} filterCb - Filter callback\n   * @param  {Mark~wrapMatchesEachCallback} eachCb - Each callback\n   * @access protected\n   */\n  wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) {\n    dict.nodes.every((n, i) => {\n      const sibl = dict.nodes[i + 1];\n      if (typeof sibl === \"undefined\" || sibl.start > start) {\n        if (!filterCb(n.node)) {\n          return false;\n        }\n        const s = start - n.start, e = (end > n.end ? n.end : end) - n.start, startStr = dict.value.substr(0, n.start), endStr = dict.value.substr(e + n.start);\n        n.node = this.wrapRangeInTextNode(n.node, s, e);\n        dict.value = startStr + endStr;\n        dict.nodes.forEach((k, j) => {\n          if (j >= i) {\n            if (dict.nodes[j].start > 0 && j !== i) {\n              dict.nodes[j].start -= e;\n            }\n            dict.nodes[j].end -= e;\n          }\n        });\n        end -= e;\n        eachCb(n.node.previousSibling, n.start);\n        if (end > n.end) {\n          start = n.end;\n        } else {\n          return false;\n        }\n      }\n      return true;\n    });\n  }\n  /**\n   * Filter callback before each wrapping\n   * @callback Mark~wrapMatchesFilterCallback\n   * @param {string} match - The matching string\n   * @param {HTMLElement} node - The text node where the match occurs\n   */\n  /**\n   * Callback for each wrapped element\n   * @callback Mark~wrapMatchesEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   */\n  /**\n   * Callback on end\n   * @callback Mark~wrapMatchesEndCallback\n   */\n  /**\n   * Wraps the instance element and class around matches within single HTML\n   * elements in all contexts\n   * @param {RegExp} regex - The regular expression to be searched for\n   * @param {number} ignoreGroups - A number indicating the amount of RegExp\n   * matching groups to ignore\n   * @param {Mark~wrapMatchesFilterCallback} filterCb\n   * @param {Mark~wrapMatchesEachCallback} eachCb\n   * @param {Mark~wrapMatchesEndCallback} endCb\n   * @access protected\n   */\n  wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) {\n    const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;\n    this.getTextNodes((dict) => {\n      dict.nodes.forEach((node) => {\n        node = node.node;\n        let match;\n        while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== \"\") {\n          if (!filterCb(match[matchIdx], node)) {\n            continue;\n          }\n          let pos = match.index;\n          if (matchIdx !== 0) {\n            for (let i = 1; i < matchIdx; i++) {\n              pos += match[i].length;\n            }\n          }\n          node = this.wrapRangeInTextNode(\n            node,\n            pos,\n            pos + match[matchIdx].length\n          );\n          eachCb(node.previousSibling);\n          regex.lastIndex = 0;\n        }\n      });\n      endCb();\n    });\n  }\n  /**\n   * Callback for each wrapped element\n   * @callback Mark~wrapMatchesAcrossElementsEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   */\n  /**\n   * Filter callback before each wrapping\n   * @callback Mark~wrapMatchesAcrossElementsFilterCallback\n   * @param {string} match - The matching string\n   * @param {HTMLElement} node - The text node where the match occurs\n   */\n  /**\n   * Callback on end\n   * @callback Mark~wrapMatchesAcrossElementsEndCallback\n   */\n  /**\n   * Wraps the instance element and class around matches across all HTML\n   * elements in all contexts\n   * @param {RegExp} regex - The regular expression to be searched for\n   * @param {number} ignoreGroups - A number indicating the amount of RegExp\n   * matching groups to ignore\n   * @param {Mark~wrapMatchesAcrossElementsFilterCallback} filterCb\n   * @param {Mark~wrapMatchesAcrossElementsEachCallback} eachCb\n   * @param {Mark~wrapMatchesAcrossElementsEndCallback} endCb\n   * @access protected\n   */\n  wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) {\n    const matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1;\n    this.getTextNodes((dict) => {\n      let match;\n      while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== \"\") {\n        let start = match.index;\n        if (matchIdx !== 0) {\n          for (let i = 1; i < matchIdx; i++) {\n            start += match[i].length;\n          }\n        }\n        const end = start + match[matchIdx].length;\n        this.wrapRangeInMappedTextNode(dict, start, end, (node) => {\n          return filterCb(match[matchIdx], node);\n        }, (node, lastIndex) => {\n          regex.lastIndex = lastIndex;\n          eachCb(node);\n        });\n      }\n      endCb();\n    });\n  }\n  /**\n   * Callback for each wrapped element\n   * @callback Mark~wrapRangeFromIndexEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   * @param {Mark~rangeObject} range - the current range object; provided\n   * start and length values will be numeric integers modified from the\n   * provided original ranges.\n   */\n  /**\n   * Filter callback before each wrapping\n   * @callback Mark~wrapRangeFromIndexFilterCallback\n   * @param {HTMLElement} node - The text node which includes the range\n   * @param {Mark~rangeObject} range - the current range object\n   * @param {string} match - string extracted from the matching range\n   * @param {number} counter - A counter indicating the number of all marks\n   */\n  /**\n   * Callback on end\n   * @callback Mark~wrapRangeFromIndexEndCallback\n   */\n  /**\n   * Wraps the indicated ranges across all HTML elements in all contexts\n   * @param {Mark~setOfRanges} ranges\n   * @param {Mark~wrapRangeFromIndexFilterCallback} filterCb\n   * @param {Mark~wrapRangeFromIndexEachCallback} eachCb\n   * @param {Mark~wrapRangeFromIndexEndCallback} endCb\n   * @access protected\n   */\n  wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) {\n    this.getTextNodes((dict) => {\n      const originalLength = dict.value.length;\n      ranges.forEach((range, counter) => {\n        let { start, end, valid } = this.checkWhitespaceRanges(\n          range,\n          originalLength,\n          dict.value\n        );\n        if (valid) {\n          this.wrapRangeInMappedTextNode(dict, start, end, (node) => {\n            return filterCb(\n              node,\n              range,\n              dict.value.substring(start, end),\n              counter\n            );\n          }, (node) => {\n            eachCb(node, range);\n          });\n        }\n      });\n      endCb();\n    });\n  }\n  /**\n   * Unwraps the specified DOM node with its content (text nodes or HTML)\n   * without destroying possibly present events (using innerHTML) and\n   * normalizes the parent at the end (merge splitted text nodes)\n   * @param  {HTMLElement} node - The DOM node to unwrap\n   * @access protected\n   */\n  unwrapMatches(node) {\n    const parent = node.parentNode;\n    let docFrag = document.createDocumentFragment();\n    while (node.firstChild) {\n      docFrag.appendChild(node.removeChild(node.firstChild));\n    }\n    parent.replaceChild(docFrag, node);\n    if (!this.ie) {\n      parent.normalize();\n    } else {\n      this.normalizeTextNode(parent);\n    }\n  }\n  /**\n   * Normalizes text nodes. It's a workaround for the native normalize method\n   * that has a bug in IE (see attached link). Should only be used in IE\n   * browsers as it's slower than the native method.\n   * @see {@link http://tinyurl.com/z5asa8c}\n   * @param {HTMLElement} node - The DOM node to normalize\n   * @access protected\n   */\n  normalizeTextNode(node) {\n    if (!node) {\n      return;\n    }\n    if (node.nodeType === 3) {\n      while (node.nextSibling && node.nextSibling.nodeType === 3) {\n        node.nodeValue += node.nextSibling.nodeValue;\n        node.parentNode.removeChild(node.nextSibling);\n      }\n    } else {\n      this.normalizeTextNode(node.firstChild);\n    }\n    this.normalizeTextNode(node.nextSibling);\n  }\n  /**\n   * Callback when finished\n   * @callback Mark~commonDoneCallback\n   * @param {number} totalMatches - The number of marked elements\n   */\n  /**\n   * @typedef Mark~commonOptions\n   * @type {object.<string>}\n   * @property {string} [element=\"mark\"] - HTML element tag name\n   * @property {string} [className] - An optional class name\n   * @property {string[]} [exclude] - An array with exclusion selectors.\n   * Elements matching those selectors will be ignored\n   * @property {boolean} [iframes=false] - Whether to search inside iframes\n   * @property {Mark~commonDoneCallback} [done]\n   * @property {boolean} [debug=false] - Wheter to log messages\n   * @property {object} [log=window.console] - Where to log messages (only if\n   * debug is true)\n   */\n  /**\n   * Callback for each marked element\n   * @callback Mark~markRegExpEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   */\n  /**\n   * Callback if there were no matches\n   * @callback Mark~markRegExpNoMatchCallback\n   * @param {RegExp} regexp - The regular expression\n   */\n  /**\n   * Callback to filter matches\n   * @callback Mark~markRegExpFilterCallback\n   * @param {HTMLElement} textNode - The text node which includes the match\n   * @param {string} match - The matching string for the RegExp\n   * @param {number} counter - A counter indicating the number of all marks\n   */\n  /**\n   * These options also include the common options from\n   * {@link Mark~commonOptions}\n   * @typedef Mark~markRegExpOptions\n   * @type {object.<string>}\n   * @property {Mark~markRegExpEachCallback} [each]\n   * @property {Mark~markRegExpNoMatchCallback} [noMatch]\n   * @property {Mark~markRegExpFilterCallback} [filter]\n   */\n  /**\n   * Marks a custom regular expression\n   * @param  {RegExp} regexp - The regular expression\n   * @param  {Mark~markRegExpOptions} [opt] - Optional options object\n   * @access public\n   */\n  markRegExp(regexp, opt) {\n    this.opt = opt;\n    this.log(`Searching with expression \"${regexp}\"`);\n    let totalMatches = 0, fn = \"wrapMatches\";\n    const eachCb = (element) => {\n      totalMatches++;\n      this.opt.each(element);\n    };\n    if (this.opt.acrossElements) {\n      fn = \"wrapMatchesAcrossElements\";\n    }\n    this[fn](regexp, this.opt.ignoreGroups, (match, node) => {\n      return this.opt.filter(node, match, totalMatches);\n    }, eachCb, () => {\n      if (totalMatches === 0) {\n        this.opt.noMatch(regexp);\n      }\n      this.opt.done(totalMatches);\n    });\n  }\n  /**\n   * Callback for each marked element\n   * @callback Mark~markEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   */\n  /**\n   * Callback if there were no matches\n   * @callback Mark~markNoMatchCallback\n   * @param {RegExp} term - The search term that was not found\n   */\n  /**\n   * Callback to filter matches\n   * @callback Mark~markFilterCallback\n   * @param {HTMLElement} textNode - The text node which includes the match\n   * @param {string} match - The matching term\n   * @param {number} totalCounter - A counter indicating the number of all\n   * marks\n   * @param {number} termCounter - A counter indicating the number of marks\n   * for the specific match\n   */\n  /**\n   * @typedef Mark~markAccuracyObject\n   * @type {object.<string>}\n   * @property {string} value - A accuracy string value\n   * @property {string[]} limiters - A custom array of limiters. For example\n   * <code>[\"-\", \",\"]</code>\n   */\n  /**\n   * @typedef Mark~markAccuracySetting\n   * @type {string}\n   * @property {\"partially\"|\"complementary\"|\"exactly\"|Mark~markAccuracyObject}\n   * [accuracy=\"partially\"] - Either one of the following string values:\n   * <ul>\n   *   <li><i>partially</i>: When searching for \"lor\" only \"lor\" inside\n   *   \"lorem\" will be marked</li>\n   *   <li><i>complementary</i>: When searching for \"lor\" the whole word\n   *   \"lorem\" will be marked</li>\n   *   <li><i>exactly</i>: When searching for \"lor\" only those exact words\n   *   will be marked. In this example nothing inside \"lorem\". This value\n   *   is equivalent to the previous option <i>wordBoundary</i></li>\n   * </ul>\n   * Or an object containing two properties:\n   * <ul>\n   *   <li><i>value</i>: One of the above named string values</li>\n   *   <li><i>limiters</i>: A custom array of string limiters for accuracy\n   *   \"exactly\" or \"complementary\"</li>\n   * </ul>\n   */\n  /**\n   * @typedef Mark~markWildcardsSetting\n   * @type {string}\n   * @property {\"disabled\"|\"enabled\"|\"withSpaces\"}\n   * [wildcards=\"disabled\"] - Set to any of the following string values:\n   * <ul>\n   *   <li><i>disabled</i>: Disable wildcard usage</li>\n   *   <li><i>enabled</i>: When searching for \"lor?m\", the \"?\" will match zero\n   *   or one non-space character (e.g. \"lorm\", \"loram\", \"lor3m\", etc). When\n   *   searching for \"lor*m\", the \"*\" will match zero or more non-space\n   *   characters (e.g. \"lorm\", \"loram\", \"lor123m\", etc).</li>\n   *   <li><i>withSpaces</i>: When searching for \"lor?m\", the \"?\" will\n   *   match zero or one space or non-space character (e.g. \"lor m\", \"loram\",\n   *   etc). When searching for \"lor*m\", the \"*\" will match zero or more space\n   *   or non-space characters (e.g. \"lorm\", \"lore et dolor ipsum\", \"lor: m\",\n   *   etc).</li>\n   * </ul>\n   */\n  /**\n   * @typedef Mark~markIgnorePunctuationSetting\n   * @type {string[]}\n   * @property {string} The strings in this setting will contain punctuation\n   * marks that will be ignored:\n   * <ul>\n   *   <li>These punctuation marks can be between any characters, e.g. setting\n   *   this option to <code>[\"'\"]</code> would match \"Worlds\", \"World's\" and\n   *   \"Wo'rlds\"</li>\n   *   <li>One or more apostrophes between the letters would still produce a\n   *   match (e.g. \"W'o''r'l'd's\").</li>\n   *   <li>A typical setting for this option could be as follows:\n   *   <pre>ignorePunctuation: \":;.,-–—‒_(){}[]!'\\\"+=\".split(\"\"),</pre> This\n   *   setting includes common punctuation as well as a minus, en-dash,\n   *   em-dash and figure-dash\n   *   ({@link https://en.wikipedia.org/wiki/Dash#Figure_dash ref}), as well\n   *   as an underscore.</li>\n   * </ul>\n   */\n  /**\n   * These options also include the common options from\n   * {@link Mark~commonOptions}\n   * @typedef Mark~markOptions\n   * @type {object.<string>}\n   * @property {boolean} [separateWordSearch=true] - Whether to search for\n   * each word separated by a blank instead of the complete term\n   * @property {boolean} [diacritics=true] - If diacritic characters should be\n   * matched. ({@link https://en.wikipedia.org/wiki/Diacritic Diacritics})\n   * @property {object} [synonyms] - An object with synonyms. The key will be\n   * a synonym for the value and the value for the key\n   * @property {Mark~markAccuracySetting} [accuracy]\n   * @property {Mark~markWildcardsSetting} [wildcards]\n   * @property {boolean} [acrossElements=false] - Whether to find matches\n   * across HTML elements. By default, only matches within single HTML\n   * elements will be found\n   * @property {boolean} [ignoreJoiners=false] - Whether to ignore word\n   * joiners inside of key words. These include soft-hyphens, zero-width\n   * space, zero-width non-joiners and zero-width joiners.\n   * @property {Mark~markIgnorePunctuationSetting} [ignorePunctuation]\n   * @property {Mark~markEachCallback} [each]\n   * @property {Mark~markNoMatchCallback} [noMatch]\n   * @property {Mark~markFilterCallback} [filter]\n   */\n  /**\n   * Marks the specified search terms\n   * @param {string|string[]} [sv] - Search value, either a search string or\n   * an array containing multiple search strings\n   * @param  {Mark~markOptions} [opt] - Optional options object\n   * @access public\n   */\n  mark(sv, opt) {\n    this.opt = opt;\n    let totalMatches = 0, fn = \"wrapMatches\";\n    const {\n      keywords: kwArr,\n      length: kwArrLen\n    } = this.getSeparatedKeywords(typeof sv === \"string\" ? [sv] : sv), sens = this.opt.caseSensitive ? \"\" : \"i\", handler = (kw) => {\n      let regex = new RegExp(this.createRegExp(kw), `gm${sens}`), matches = 0;\n      this.log(`Searching with expression \"${regex}\"`);\n      this[fn](regex, 1, (term, node) => {\n        return this.opt.filter(node, kw, totalMatches, matches);\n      }, (element) => {\n        matches++;\n        totalMatches++;\n        this.opt.each(element);\n      }, () => {\n        if (matches === 0) {\n          this.opt.noMatch(kw);\n        }\n        if (kwArr[kwArrLen - 1] === kw) {\n          this.opt.done(totalMatches);\n        } else {\n          handler(kwArr[kwArr.indexOf(kw) + 1]);\n        }\n      });\n    };\n    if (this.opt.acrossElements) {\n      fn = \"wrapMatchesAcrossElements\";\n    }\n    if (kwArrLen === 0) {\n      this.opt.done(totalMatches);\n    } else {\n      handler(kwArr[0]);\n    }\n  }\n  /**\n   * Callback for each marked element\n   * @callback Mark~markRangesEachCallback\n   * @param {HTMLElement} element - The marked DOM element\n   * @param {array} range - array of range start and end points\n   */\n  /**\n   * Callback if a processed range is invalid, out-of-bounds, overlaps another\n   * range, or only matches whitespace\n   * @callback Mark~markRangesNoMatchCallback\n   * @param {Mark~rangeObject} range - a range object\n   */\n  /**\n   * Callback to filter matches\n   * @callback Mark~markRangesFilterCallback\n   * @param {HTMLElement} node - The text node which includes the range\n   * @param {array} range - array of range start and end points\n   * @param {string} match - string extracted from the matching range\n   * @param {number} counter - A counter indicating the number of all marks\n   */\n  /**\n   * These options also include the common options from\n   * {@link Mark~commonOptions}\n   * @typedef Mark~markRangesOptions\n   * @type {object.<string>}\n   * @property {Mark~markRangesEachCallback} [each]\n   * @property {Mark~markRangesNoMatchCallback} [noMatch]\n   * @property {Mark~markRangesFilterCallback} [filter]\n   */\n  /**\n   * Marks an array of objects containing a start with an end or length of the\n   * string to mark\n   * @param  {Mark~setOfRanges} rawRanges - The original (preprocessed)\n   * array of objects\n   * @param  {Mark~markRangesOptions} [opt] - Optional options object\n   * @access public\n   */\n  markRanges(rawRanges, opt) {\n    this.opt = opt;\n    let totalMatches = 0, ranges = this.checkRanges(rawRanges);\n    if (ranges && ranges.length) {\n      this.log(\n        \"Starting to mark with the following ranges: \" + JSON.stringify(ranges)\n      );\n      this.wrapRangeFromIndex(\n        ranges,\n        (node, range, match, counter) => {\n          return this.opt.filter(node, range, match, counter);\n        },\n        (element, range) => {\n          totalMatches++;\n          this.opt.each(element, range);\n        },\n        () => {\n          this.opt.done(totalMatches);\n        }\n      );\n    } else {\n      this.opt.done(totalMatches);\n    }\n  }\n  /**\n   * Removes all marked elements inside the context with their HTML and\n   * normalizes the parent at the end\n   * @param  {Mark~commonOptions} [opt] - Optional options object\n   * @access public\n   */\n  unmark(opt) {\n    this.opt = opt;\n    let sel = this.opt.element ? this.opt.element : \"*\";\n    sel += \"[data-markjs]\";\n    if (this.opt.className) {\n      sel += `.${this.opt.className}`;\n    }\n    this.log(`Removal selector \"${sel}\"`);\n    this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, (node) => {\n      this.unwrapMatches(node);\n    }, (node) => {\n      const matchesSel = DOMIterator.matches(node, sel), matchesExclude = this.matchesExclude(node);\n      if (!matchesSel || matchesExclude) {\n        return NodeFilter.FILTER_REJECT;\n      } else {\n        return NodeFilter.FILTER_ACCEPT;\n      }\n    }, this.opt.done);\n  }\n};\n\n// node_modules/.pnpm/mark.js@8.11.1/node_modules/mark.js/src/vanilla.js\nfunction Mark2(ctx) {\n  const instance = new Mark(ctx);\n  this.mark = (sv, opt) => {\n    instance.mark(sv, opt);\n    return this;\n  };\n  this.markRegExp = (sv, opt) => {\n    instance.markRegExp(sv, opt);\n    return this;\n  };\n  this.markRanges = (sv, opt) => {\n    instance.markRanges(sv, opt);\n    return this;\n  };\n  this.unmark = (opt) => {\n    instance.unmark(opt);\n    return this;\n  };\n  return this;\n}\nexport {\n  Mark2 as default\n};\n//# sourceMappingURL=vitepress___mark__js_src_vanilla__js.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vitepress___minisearch.js",
    "content": "// node_modules/.pnpm/minisearch@7.1.1/node_modules/minisearch/dist/es/index.js\nfunction __awaiter(thisArg, _arguments, P, generator) {\n  function adopt(value) {\n    return value instanceof P ? value : new P(function(resolve) {\n      resolve(value);\n    });\n  }\n  return new (P || (P = Promise))(function(resolve, reject) {\n    function fulfilled(value) {\n      try {\n        step(generator.next(value));\n      } catch (e) {\n        reject(e);\n      }\n    }\n    function rejected(value) {\n      try {\n        step(generator[\"throw\"](value));\n      } catch (e) {\n        reject(e);\n      }\n    }\n    function step(result) {\n      result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);\n    }\n    step((generator = generator.apply(thisArg, _arguments || [])).next());\n  });\n}\nvar ENTRIES = \"ENTRIES\";\nvar KEYS = \"KEYS\";\nvar VALUES = \"VALUES\";\nvar LEAF = \"\";\nvar TreeIterator = class {\n  constructor(set, type) {\n    const node = set._tree;\n    const keys = Array.from(node.keys());\n    this.set = set;\n    this._type = type;\n    this._path = keys.length > 0 ? [{ node, keys }] : [];\n  }\n  next() {\n    const value = this.dive();\n    this.backtrack();\n    return value;\n  }\n  dive() {\n    if (this._path.length === 0) {\n      return { done: true, value: void 0 };\n    }\n    const { node, keys } = last$1(this._path);\n    if (last$1(keys) === LEAF) {\n      return { done: false, value: this.result() };\n    }\n    const child = node.get(last$1(keys));\n    this._path.push({ node: child, keys: Array.from(child.keys()) });\n    return this.dive();\n  }\n  backtrack() {\n    if (this._path.length === 0) {\n      return;\n    }\n    const keys = last$1(this._path).keys;\n    keys.pop();\n    if (keys.length > 0) {\n      return;\n    }\n    this._path.pop();\n    this.backtrack();\n  }\n  key() {\n    return this.set._prefix + this._path.map(({ keys }) => last$1(keys)).filter((key) => key !== LEAF).join(\"\");\n  }\n  value() {\n    return last$1(this._path).node.get(LEAF);\n  }\n  result() {\n    switch (this._type) {\n      case VALUES:\n        return this.value();\n      case KEYS:\n        return this.key();\n      default:\n        return [this.key(), this.value()];\n    }\n  }\n  [Symbol.iterator]() {\n    return this;\n  }\n};\nvar last$1 = (array) => {\n  return array[array.length - 1];\n};\nvar fuzzySearch = (node, query, maxDistance) => {\n  const results = /* @__PURE__ */ new Map();\n  if (query === void 0)\n    return results;\n  const n = query.length + 1;\n  const m = n + maxDistance;\n  const matrix = new Uint8Array(m * n).fill(maxDistance + 1);\n  for (let j = 0; j < n; ++j)\n    matrix[j] = j;\n  for (let i = 1; i < m; ++i)\n    matrix[i * n] = i;\n  recurse(node, query, maxDistance, results, matrix, 1, n, \"\");\n  return results;\n};\nvar recurse = (node, query, maxDistance, results, matrix, m, n, prefix) => {\n  const offset = m * n;\n  key: for (const key of node.keys()) {\n    if (key === LEAF) {\n      const distance = matrix[offset - 1];\n      if (distance <= maxDistance) {\n        results.set(prefix, [node.get(key), distance]);\n      }\n    } else {\n      let i = m;\n      for (let pos = 0; pos < key.length; ++pos, ++i) {\n        const char = key[pos];\n        const thisRowOffset = n * i;\n        const prevRowOffset = thisRowOffset - n;\n        let minDistance = matrix[thisRowOffset];\n        const jmin = Math.max(0, i - maxDistance - 1);\n        const jmax = Math.min(n - 1, i + maxDistance);\n        for (let j = jmin; j < jmax; ++j) {\n          const different = char !== query[j];\n          const rpl = matrix[prevRowOffset + j] + +different;\n          const del = matrix[prevRowOffset + j + 1] + 1;\n          const ins = matrix[thisRowOffset + j] + 1;\n          const dist = matrix[thisRowOffset + j + 1] = Math.min(rpl, del, ins);\n          if (dist < minDistance)\n            minDistance = dist;\n        }\n        if (minDistance > maxDistance) {\n          continue key;\n        }\n      }\n      recurse(node.get(key), query, maxDistance, results, matrix, i, n, prefix + key);\n    }\n  }\n};\nvar SearchableMap = class _SearchableMap {\n  /**\n   * The constructor is normally called without arguments, creating an empty\n   * map. In order to create a {@link SearchableMap} from an iterable or from an\n   * object, check {@link SearchableMap.from} and {@link\n   * SearchableMap.fromObject}.\n   *\n   * The constructor arguments are for internal use, when creating derived\n   * mutable views of a map at a prefix.\n   */\n  constructor(tree = /* @__PURE__ */ new Map(), prefix = \"\") {\n    this._size = void 0;\n    this._tree = tree;\n    this._prefix = prefix;\n  }\n  /**\n   * Creates and returns a mutable view of this {@link SearchableMap},\n   * containing only entries that share the given prefix.\n   *\n   * ### Usage:\n   *\n   * ```javascript\n   * let map = new SearchableMap()\n   * map.set(\"unicorn\", 1)\n   * map.set(\"universe\", 2)\n   * map.set(\"university\", 3)\n   * map.set(\"unique\", 4)\n   * map.set(\"hello\", 5)\n   *\n   * let uni = map.atPrefix(\"uni\")\n   * uni.get(\"unique\") // => 4\n   * uni.get(\"unicorn\") // => 1\n   * uni.get(\"hello\") // => undefined\n   *\n   * let univer = map.atPrefix(\"univer\")\n   * univer.get(\"unique\") // => undefined\n   * univer.get(\"universe\") // => 2\n   * univer.get(\"university\") // => 3\n   * ```\n   *\n   * @param prefix  The prefix\n   * @return A {@link SearchableMap} representing a mutable view of the original\n   * Map at the given prefix\n   */\n  atPrefix(prefix) {\n    if (!prefix.startsWith(this._prefix)) {\n      throw new Error(\"Mismatched prefix\");\n    }\n    const [node, path] = trackDown(this._tree, prefix.slice(this._prefix.length));\n    if (node === void 0) {\n      const [parentNode, key] = last(path);\n      for (const k of parentNode.keys()) {\n        if (k !== LEAF && k.startsWith(key)) {\n          const node2 = /* @__PURE__ */ new Map();\n          node2.set(k.slice(key.length), parentNode.get(k));\n          return new _SearchableMap(node2, prefix);\n        }\n      }\n    }\n    return new _SearchableMap(node, prefix);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear\n   */\n  clear() {\n    this._size = void 0;\n    this._tree.clear();\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete\n   * @param key  Key to delete\n   */\n  delete(key) {\n    this._size = void 0;\n    return remove(this._tree, key);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries\n   * @return An iterator iterating through `[key, value]` entries.\n   */\n  entries() {\n    return new TreeIterator(this, ENTRIES);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach\n   * @param fn  Iteration function\n   */\n  forEach(fn) {\n    for (const [key, value] of this) {\n      fn(key, value, this);\n    }\n  }\n  /**\n   * Returns a Map of all the entries that have a key within the given edit\n   * distance from the search key. The keys of the returned Map are the matching\n   * keys, while the values are two-element arrays where the first element is\n   * the value associated to the key, and the second is the edit distance of the\n   * key to the search key.\n   *\n   * ### Usage:\n   *\n   * ```javascript\n   * let map = new SearchableMap()\n   * map.set('hello', 'world')\n   * map.set('hell', 'yeah')\n   * map.set('ciao', 'mondo')\n   *\n   * // Get all entries that match the key 'hallo' with a maximum edit distance of 2\n   * map.fuzzyGet('hallo', 2)\n   * // => Map(2) { 'hello' => ['world', 1], 'hell' => ['yeah', 2] }\n   *\n   * // In the example, the \"hello\" key has value \"world\" and edit distance of 1\n   * // (change \"e\" to \"a\"), the key \"hell\" has value \"yeah\" and edit distance of 2\n   * // (change \"e\" to \"a\", delete \"o\")\n   * ```\n   *\n   * @param key  The search key\n   * @param maxEditDistance  The maximum edit distance (Levenshtein)\n   * @return A Map of the matching keys to their value and edit distance\n   */\n  fuzzyGet(key, maxEditDistance) {\n    return fuzzySearch(this._tree, key, maxEditDistance);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get\n   * @param key  Key to get\n   * @return Value associated to the key, or `undefined` if the key is not\n   * found.\n   */\n  get(key) {\n    const node = lookup(this._tree, key);\n    return node !== void 0 ? node.get(LEAF) : void 0;\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has\n   * @param key  Key\n   * @return True if the key is in the map, false otherwise\n   */\n  has(key) {\n    const node = lookup(this._tree, key);\n    return node !== void 0 && node.has(LEAF);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys\n   * @return An `Iterable` iterating through keys\n   */\n  keys() {\n    return new TreeIterator(this, KEYS);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set\n   * @param key  Key to set\n   * @param value  Value to associate to the key\n   * @return The {@link SearchableMap} itself, to allow chaining\n   */\n  set(key, value) {\n    if (typeof key !== \"string\") {\n      throw new Error(\"key must be a string\");\n    }\n    this._size = void 0;\n    const node = createPath(this._tree, key);\n    node.set(LEAF, value);\n    return this;\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size\n   */\n  get size() {\n    if (this._size) {\n      return this._size;\n    }\n    this._size = 0;\n    const iter = this.entries();\n    while (!iter.next().done)\n      this._size += 1;\n    return this._size;\n  }\n  /**\n   * Updates the value at the given key using the provided function. The function\n   * is called with the current value at the key, and its return value is used as\n   * the new value to be set.\n   *\n   * ### Example:\n   *\n   * ```javascript\n   * // Increment the current value by one\n   * searchableMap.update('somekey', (currentValue) => currentValue == null ? 0 : currentValue + 1)\n   * ```\n   *\n   * If the value at the given key is or will be an object, it might not require\n   * re-assignment. In that case it is better to use `fetch()`, because it is\n   * faster.\n   *\n   * @param key  The key to update\n   * @param fn  The function used to compute the new value from the current one\n   * @return The {@link SearchableMap} itself, to allow chaining\n   */\n  update(key, fn) {\n    if (typeof key !== \"string\") {\n      throw new Error(\"key must be a string\");\n    }\n    this._size = void 0;\n    const node = createPath(this._tree, key);\n    node.set(LEAF, fn(node.get(LEAF)));\n    return this;\n  }\n  /**\n   * Fetches the value of the given key. If the value does not exist, calls the\n   * given function to create a new value, which is inserted at the given key\n   * and subsequently returned.\n   *\n   * ### Example:\n   *\n   * ```javascript\n   * const map = searchableMap.fetch('somekey', () => new Map())\n   * map.set('foo', 'bar')\n   * ```\n   *\n   * @param key  The key to update\n   * @param initial  A function that creates a new value if the key does not exist\n   * @return The existing or new value at the given key\n   */\n  fetch(key, initial) {\n    if (typeof key !== \"string\") {\n      throw new Error(\"key must be a string\");\n    }\n    this._size = void 0;\n    const node = createPath(this._tree, key);\n    let value = node.get(LEAF);\n    if (value === void 0) {\n      node.set(LEAF, value = initial());\n    }\n    return value;\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values\n   * @return An `Iterable` iterating through values.\n   */\n  values() {\n    return new TreeIterator(this, VALUES);\n  }\n  /**\n   * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/@@iterator\n   */\n  [Symbol.iterator]() {\n    return this.entries();\n  }\n  /**\n   * Creates a {@link SearchableMap} from an `Iterable` of entries\n   *\n   * @param entries  Entries to be inserted in the {@link SearchableMap}\n   * @return A new {@link SearchableMap} with the given entries\n   */\n  static from(entries) {\n    const tree = new _SearchableMap();\n    for (const [key, value] of entries) {\n      tree.set(key, value);\n    }\n    return tree;\n  }\n  /**\n   * Creates a {@link SearchableMap} from the iterable properties of a JavaScript object\n   *\n   * @param object  Object of entries for the {@link SearchableMap}\n   * @return A new {@link SearchableMap} with the given entries\n   */\n  static fromObject(object) {\n    return _SearchableMap.from(Object.entries(object));\n  }\n};\nvar trackDown = (tree, key, path = []) => {\n  if (key.length === 0 || tree == null) {\n    return [tree, path];\n  }\n  for (const k of tree.keys()) {\n    if (k !== LEAF && key.startsWith(k)) {\n      path.push([tree, k]);\n      return trackDown(tree.get(k), key.slice(k.length), path);\n    }\n  }\n  path.push([tree, key]);\n  return trackDown(void 0, \"\", path);\n};\nvar lookup = (tree, key) => {\n  if (key.length === 0 || tree == null) {\n    return tree;\n  }\n  for (const k of tree.keys()) {\n    if (k !== LEAF && key.startsWith(k)) {\n      return lookup(tree.get(k), key.slice(k.length));\n    }\n  }\n};\nvar createPath = (node, key) => {\n  const keyLength = key.length;\n  outer: for (let pos = 0; node && pos < keyLength; ) {\n    for (const k of node.keys()) {\n      if (k !== LEAF && key[pos] === k[0]) {\n        const len = Math.min(keyLength - pos, k.length);\n        let offset = 1;\n        while (offset < len && key[pos + offset] === k[offset])\n          ++offset;\n        const child2 = node.get(k);\n        if (offset === k.length) {\n          node = child2;\n        } else {\n          const intermediate = /* @__PURE__ */ new Map();\n          intermediate.set(k.slice(offset), child2);\n          node.set(key.slice(pos, pos + offset), intermediate);\n          node.delete(k);\n          node = intermediate;\n        }\n        pos += offset;\n        continue outer;\n      }\n    }\n    const child = /* @__PURE__ */ new Map();\n    node.set(key.slice(pos), child);\n    return child;\n  }\n  return node;\n};\nvar remove = (tree, key) => {\n  const [node, path] = trackDown(tree, key);\n  if (node === void 0) {\n    return;\n  }\n  node.delete(LEAF);\n  if (node.size === 0) {\n    cleanup(path);\n  } else if (node.size === 1) {\n    const [key2, value] = node.entries().next().value;\n    merge(path, key2, value);\n  }\n};\nvar cleanup = (path) => {\n  if (path.length === 0) {\n    return;\n  }\n  const [node, key] = last(path);\n  node.delete(key);\n  if (node.size === 0) {\n    cleanup(path.slice(0, -1));\n  } else if (node.size === 1) {\n    const [key2, value] = node.entries().next().value;\n    if (key2 !== LEAF) {\n      merge(path.slice(0, -1), key2, value);\n    }\n  }\n};\nvar merge = (path, key, value) => {\n  if (path.length === 0) {\n    return;\n  }\n  const [node, nodeKey] = last(path);\n  node.set(nodeKey + key, value);\n  node.delete(nodeKey);\n};\nvar last = (array) => {\n  return array[array.length - 1];\n};\nvar OR = \"or\";\nvar AND = \"and\";\nvar AND_NOT = \"and_not\";\nvar MiniSearch = class _MiniSearch {\n  /**\n   * @param options  Configuration options\n   *\n   * ### Examples:\n   *\n   * ```javascript\n   * // Create a search engine that indexes the 'title' and 'text' fields of your\n   * // documents:\n   * const miniSearch = new MiniSearch({ fields: ['title', 'text'] })\n   * ```\n   *\n   * ### ID Field:\n   *\n   * ```javascript\n   * // Your documents are assumed to include a unique 'id' field, but if you want\n   * // to use a different field for document identification, you can set the\n   * // 'idField' option:\n   * const miniSearch = new MiniSearch({ idField: 'key', fields: ['title', 'text'] })\n   * ```\n   *\n   * ### Options and defaults:\n   *\n   * ```javascript\n   * // The full set of options (here with their default value) is:\n   * const miniSearch = new MiniSearch({\n   *   // idField: field that uniquely identifies a document\n   *   idField: 'id',\n   *\n   *   // extractField: function used to get the value of a field in a document.\n   *   // By default, it assumes the document is a flat object with field names as\n   *   // property keys and field values as string property values, but custom logic\n   *   // can be implemented by setting this option to a custom extractor function.\n   *   extractField: (document, fieldName) => document[fieldName],\n   *\n   *   // tokenize: function used to split fields into individual terms. By\n   *   // default, it is also used to tokenize search queries, unless a specific\n   *   // `tokenize` search option is supplied. When tokenizing an indexed field,\n   *   // the field name is passed as the second argument.\n   *   tokenize: (string, _fieldName) => string.split(SPACE_OR_PUNCTUATION),\n   *\n   *   // processTerm: function used to process each tokenized term before\n   *   // indexing. It can be used for stemming and normalization. Return a falsy\n   *   // value in order to discard a term. By default, it is also used to process\n   *   // search queries, unless a specific `processTerm` option is supplied as a\n   *   // search option. When processing a term from a indexed field, the field\n   *   // name is passed as the second argument.\n   *   processTerm: (term, _fieldName) => term.toLowerCase(),\n   *\n   *   // searchOptions: default search options, see the `search` method for\n   *   // details\n   *   searchOptions: undefined,\n   *\n   *   // fields: document fields to be indexed. Mandatory, but not set by default\n   *   fields: undefined\n   *\n   *   // storeFields: document fields to be stored and returned as part of the\n   *   // search results.\n   *   storeFields: []\n   * })\n   * ```\n   */\n  constructor(options) {\n    if ((options === null || options === void 0 ? void 0 : options.fields) == null) {\n      throw new Error('MiniSearch: option \"fields\" must be provided');\n    }\n    const autoVacuum = options.autoVacuum == null || options.autoVacuum === true ? defaultAutoVacuumOptions : options.autoVacuum;\n    this._options = Object.assign(Object.assign(Object.assign({}, defaultOptions), options), { autoVacuum, searchOptions: Object.assign(Object.assign({}, defaultSearchOptions), options.searchOptions || {}), autoSuggestOptions: Object.assign(Object.assign({}, defaultAutoSuggestOptions), options.autoSuggestOptions || {}) });\n    this._index = new SearchableMap();\n    this._documentCount = 0;\n    this._documentIds = /* @__PURE__ */ new Map();\n    this._idToShortId = /* @__PURE__ */ new Map();\n    this._fieldIds = {};\n    this._fieldLength = /* @__PURE__ */ new Map();\n    this._avgFieldLength = [];\n    this._nextId = 0;\n    this._storedFields = /* @__PURE__ */ new Map();\n    this._dirtCount = 0;\n    this._currentVacuum = null;\n    this._enqueuedVacuum = null;\n    this._enqueuedVacuumConditions = defaultVacuumConditions;\n    this.addFields(this._options.fields);\n  }\n  /**\n   * Adds a document to the index\n   *\n   * @param document  The document to be indexed\n   */\n  add(document) {\n    const { extractField, tokenize, processTerm, fields, idField } = this._options;\n    const id = extractField(document, idField);\n    if (id == null) {\n      throw new Error(`MiniSearch: document does not have ID field \"${idField}\"`);\n    }\n    if (this._idToShortId.has(id)) {\n      throw new Error(`MiniSearch: duplicate ID ${id}`);\n    }\n    const shortDocumentId = this.addDocumentId(id);\n    this.saveStoredFields(shortDocumentId, document);\n    for (const field of fields) {\n      const fieldValue = extractField(document, field);\n      if (fieldValue == null)\n        continue;\n      const tokens = tokenize(fieldValue.toString(), field);\n      const fieldId = this._fieldIds[field];\n      const uniqueTerms = new Set(tokens).size;\n      this.addFieldLength(shortDocumentId, fieldId, this._documentCount - 1, uniqueTerms);\n      for (const term of tokens) {\n        const processedTerm = processTerm(term, field);\n        if (Array.isArray(processedTerm)) {\n          for (const t of processedTerm) {\n            this.addTerm(fieldId, shortDocumentId, t);\n          }\n        } else if (processedTerm) {\n          this.addTerm(fieldId, shortDocumentId, processedTerm);\n        }\n      }\n    }\n  }\n  /**\n   * Adds all the given documents to the index\n   *\n   * @param documents  An array of documents to be indexed\n   */\n  addAll(documents) {\n    for (const document of documents)\n      this.add(document);\n  }\n  /**\n   * Adds all the given documents to the index asynchronously.\n   *\n   * Returns a promise that resolves (to `undefined`) when the indexing is done.\n   * This method is useful when index many documents, to avoid blocking the main\n   * thread. The indexing is performed asynchronously and in chunks.\n   *\n   * @param documents  An array of documents to be indexed\n   * @param options  Configuration options\n   * @return A promise resolving to `undefined` when the indexing is done\n   */\n  addAllAsync(documents, options = {}) {\n    const { chunkSize = 10 } = options;\n    const acc = { chunk: [], promise: Promise.resolve() };\n    const { chunk, promise } = documents.reduce(({ chunk: chunk2, promise: promise2 }, document, i) => {\n      chunk2.push(document);\n      if ((i + 1) % chunkSize === 0) {\n        return {\n          chunk: [],\n          promise: promise2.then(() => new Promise((resolve) => setTimeout(resolve, 0))).then(() => this.addAll(chunk2))\n        };\n      } else {\n        return { chunk: chunk2, promise: promise2 };\n      }\n    }, acc);\n    return promise.then(() => this.addAll(chunk));\n  }\n  /**\n   * Removes the given document from the index.\n   *\n   * The document to remove must NOT have changed between indexing and removal,\n   * otherwise the index will be corrupted.\n   *\n   * This method requires passing the full document to be removed (not just the\n   * ID), and immediately removes the document from the inverted index, allowing\n   * memory to be released. A convenient alternative is {@link\n   * MiniSearch#discard}, which needs only the document ID, and has the same\n   * visible effect, but delays cleaning up the index until the next vacuuming.\n   *\n   * @param document  The document to be removed\n   */\n  remove(document) {\n    const { tokenize, processTerm, extractField, fields, idField } = this._options;\n    const id = extractField(document, idField);\n    if (id == null) {\n      throw new Error(`MiniSearch: document does not have ID field \"${idField}\"`);\n    }\n    const shortId = this._idToShortId.get(id);\n    if (shortId == null) {\n      throw new Error(`MiniSearch: cannot remove document with ID ${id}: it is not in the index`);\n    }\n    for (const field of fields) {\n      const fieldValue = extractField(document, field);\n      if (fieldValue == null)\n        continue;\n      const tokens = tokenize(fieldValue.toString(), field);\n      const fieldId = this._fieldIds[field];\n      const uniqueTerms = new Set(tokens).size;\n      this.removeFieldLength(shortId, fieldId, this._documentCount, uniqueTerms);\n      for (const term of tokens) {\n        const processedTerm = processTerm(term, field);\n        if (Array.isArray(processedTerm)) {\n          for (const t of processedTerm) {\n            this.removeTerm(fieldId, shortId, t);\n          }\n        } else if (processedTerm) {\n          this.removeTerm(fieldId, shortId, processedTerm);\n        }\n      }\n    }\n    this._storedFields.delete(shortId);\n    this._documentIds.delete(shortId);\n    this._idToShortId.delete(id);\n    this._fieldLength.delete(shortId);\n    this._documentCount -= 1;\n  }\n  /**\n   * Removes all the given documents from the index. If called with no arguments,\n   * it removes _all_ documents from the index.\n   *\n   * @param documents  The documents to be removed. If this argument is omitted,\n   * all documents are removed. Note that, for removing all documents, it is\n   * more efficient to call this method with no arguments than to pass all\n   * documents.\n   */\n  removeAll(documents) {\n    if (documents) {\n      for (const document of documents)\n        this.remove(document);\n    } else if (arguments.length > 0) {\n      throw new Error(\"Expected documents to be present. Omit the argument to remove all documents.\");\n    } else {\n      this._index = new SearchableMap();\n      this._documentCount = 0;\n      this._documentIds = /* @__PURE__ */ new Map();\n      this._idToShortId = /* @__PURE__ */ new Map();\n      this._fieldLength = /* @__PURE__ */ new Map();\n      this._avgFieldLength = [];\n      this._storedFields = /* @__PURE__ */ new Map();\n      this._nextId = 0;\n    }\n  }\n  /**\n   * Discards the document with the given ID, so it won't appear in search results\n   *\n   * It has the same visible effect of {@link MiniSearch.remove} (both cause the\n   * document to stop appearing in searches), but a different effect on the\n   * internal data structures:\n   *\n   *   - {@link MiniSearch#remove} requires passing the full document to be\n   *   removed as argument, and removes it from the inverted index immediately.\n   *\n   *   - {@link MiniSearch#discard} instead only needs the document ID, and\n   *   works by marking the current version of the document as discarded, so it\n   *   is immediately ignored by searches. This is faster and more convenient\n   *   than {@link MiniSearch#remove}, but the index is not immediately\n   *   modified. To take care of that, vacuuming is performed after a certain\n   *   number of documents are discarded, cleaning up the index and allowing\n   *   memory to be released.\n   *\n   * After discarding a document, it is possible to re-add a new version, and\n   * only the new version will appear in searches. In other words, discarding\n   * and re-adding a document works exactly like removing and re-adding it. The\n   * {@link MiniSearch.replace} method can also be used to replace a document\n   * with a new version.\n   *\n   * #### Details about vacuuming\n   *\n   * Repetite calls to this method would leave obsolete document references in\n   * the index, invisible to searches. Two mechanisms take care of cleaning up:\n   * clean up during search, and vacuuming.\n   *\n   *   - Upon search, whenever a discarded ID is found (and ignored for the\n   *   results), references to the discarded document are removed from the\n   *   inverted index entries for the search terms. This ensures that subsequent\n   *   searches for the same terms do not need to skip these obsolete references\n   *   again.\n   *\n   *   - In addition, vacuuming is performed automatically by default (see the\n   *   `autoVacuum` field in {@link Options}) after a certain number of\n   *   documents are discarded. Vacuuming traverses all terms in the index,\n   *   cleaning up all references to discarded documents. Vacuuming can also be\n   *   triggered manually by calling {@link MiniSearch#vacuum}.\n   *\n   * @param id  The ID of the document to be discarded\n   */\n  discard(id) {\n    const shortId = this._idToShortId.get(id);\n    if (shortId == null) {\n      throw new Error(`MiniSearch: cannot discard document with ID ${id}: it is not in the index`);\n    }\n    this._idToShortId.delete(id);\n    this._documentIds.delete(shortId);\n    this._storedFields.delete(shortId);\n    (this._fieldLength.get(shortId) || []).forEach((fieldLength, fieldId) => {\n      this.removeFieldLength(shortId, fieldId, this._documentCount, fieldLength);\n    });\n    this._fieldLength.delete(shortId);\n    this._documentCount -= 1;\n    this._dirtCount += 1;\n    this.maybeAutoVacuum();\n  }\n  maybeAutoVacuum() {\n    if (this._options.autoVacuum === false) {\n      return;\n    }\n    const { minDirtFactor, minDirtCount, batchSize, batchWait } = this._options.autoVacuum;\n    this.conditionalVacuum({ batchSize, batchWait }, { minDirtCount, minDirtFactor });\n  }\n  /**\n   * Discards the documents with the given IDs, so they won't appear in search\n   * results\n   *\n   * It is equivalent to calling {@link MiniSearch#discard} for all the given\n   * IDs, but with the optimization of triggering at most one automatic\n   * vacuuming at the end.\n   *\n   * Note: to remove all documents from the index, it is faster and more\n   * convenient to call {@link MiniSearch.removeAll} with no argument, instead\n   * of passing all IDs to this method.\n   */\n  discardAll(ids) {\n    const autoVacuum = this._options.autoVacuum;\n    try {\n      this._options.autoVacuum = false;\n      for (const id of ids) {\n        this.discard(id);\n      }\n    } finally {\n      this._options.autoVacuum = autoVacuum;\n    }\n    this.maybeAutoVacuum();\n  }\n  /**\n   * It replaces an existing document with the given updated version\n   *\n   * It works by discarding the current version and adding the updated one, so\n   * it is functionally equivalent to calling {@link MiniSearch#discard}\n   * followed by {@link MiniSearch#add}. The ID of the updated document should\n   * be the same as the original one.\n   *\n   * Since it uses {@link MiniSearch#discard} internally, this method relies on\n   * vacuuming to clean up obsolete document references from the index, allowing\n   * memory to be released (see {@link MiniSearch#discard}).\n   *\n   * @param updatedDocument  The updated document to replace the old version\n   * with\n   */\n  replace(updatedDocument) {\n    const { idField, extractField } = this._options;\n    const id = extractField(updatedDocument, idField);\n    this.discard(id);\n    this.add(updatedDocument);\n  }\n  /**\n   * Triggers a manual vacuuming, cleaning up references to discarded documents\n   * from the inverted index\n   *\n   * Vacuuming is only useful for applications that use the {@link\n   * MiniSearch#discard} or {@link MiniSearch#replace} methods.\n   *\n   * By default, vacuuming is performed automatically when needed (controlled by\n   * the `autoVacuum` field in {@link Options}), so there is usually no need to\n   * call this method, unless one wants to make sure to perform vacuuming at a\n   * specific moment.\n   *\n   * Vacuuming traverses all terms in the inverted index in batches, and cleans\n   * up references to discarded documents from the posting list, allowing memory\n   * to be released.\n   *\n   * The method takes an optional object as argument with the following keys:\n   *\n   *   - `batchSize`: the size of each batch (1000 by default)\n   *\n   *   - `batchWait`: the number of milliseconds to wait between batches (10 by\n   *   default)\n   *\n   * On large indexes, vacuuming could have a non-negligible cost: batching\n   * avoids blocking the thread for long, diluting this cost so that it is not\n   * negatively affecting the application. Nonetheless, this method should only\n   * be called when necessary, and relying on automatic vacuuming is usually\n   * better.\n   *\n   * It returns a promise that resolves (to undefined) when the clean up is\n   * completed. If vacuuming is already ongoing at the time this method is\n   * called, a new one is enqueued immediately after the ongoing one, and a\n   * corresponding promise is returned. However, no more than one vacuuming is\n   * enqueued on top of the ongoing one, even if this method is called more\n   * times (enqueuing multiple ones would be useless).\n   *\n   * @param options  Configuration options for the batch size and delay. See\n   * {@link VacuumOptions}.\n   */\n  vacuum(options = {}) {\n    return this.conditionalVacuum(options);\n  }\n  conditionalVacuum(options, conditions) {\n    if (this._currentVacuum) {\n      this._enqueuedVacuumConditions = this._enqueuedVacuumConditions && conditions;\n      if (this._enqueuedVacuum != null) {\n        return this._enqueuedVacuum;\n      }\n      this._enqueuedVacuum = this._currentVacuum.then(() => {\n        const conditions2 = this._enqueuedVacuumConditions;\n        this._enqueuedVacuumConditions = defaultVacuumConditions;\n        return this.performVacuuming(options, conditions2);\n      });\n      return this._enqueuedVacuum;\n    }\n    if (this.vacuumConditionsMet(conditions) === false) {\n      return Promise.resolve();\n    }\n    this._currentVacuum = this.performVacuuming(options);\n    return this._currentVacuum;\n  }\n  performVacuuming(options, conditions) {\n    return __awaiter(this, void 0, void 0, function* () {\n      const initialDirtCount = this._dirtCount;\n      if (this.vacuumConditionsMet(conditions)) {\n        const batchSize = options.batchSize || defaultVacuumOptions.batchSize;\n        const batchWait = options.batchWait || defaultVacuumOptions.batchWait;\n        let i = 1;\n        for (const [term, fieldsData] of this._index) {\n          for (const [fieldId, fieldIndex] of fieldsData) {\n            for (const [shortId] of fieldIndex) {\n              if (this._documentIds.has(shortId)) {\n                continue;\n              }\n              if (fieldIndex.size <= 1) {\n                fieldsData.delete(fieldId);\n              } else {\n                fieldIndex.delete(shortId);\n              }\n            }\n          }\n          if (this._index.get(term).size === 0) {\n            this._index.delete(term);\n          }\n          if (i % batchSize === 0) {\n            yield new Promise((resolve) => setTimeout(resolve, batchWait));\n          }\n          i += 1;\n        }\n        this._dirtCount -= initialDirtCount;\n      }\n      yield null;\n      this._currentVacuum = this._enqueuedVacuum;\n      this._enqueuedVacuum = null;\n    });\n  }\n  vacuumConditionsMet(conditions) {\n    if (conditions == null) {\n      return true;\n    }\n    let { minDirtCount, minDirtFactor } = conditions;\n    minDirtCount = minDirtCount || defaultAutoVacuumOptions.minDirtCount;\n    minDirtFactor = minDirtFactor || defaultAutoVacuumOptions.minDirtFactor;\n    return this.dirtCount >= minDirtCount && this.dirtFactor >= minDirtFactor;\n  }\n  /**\n   * Is `true` if a vacuuming operation is ongoing, `false` otherwise\n   */\n  get isVacuuming() {\n    return this._currentVacuum != null;\n  }\n  /**\n   * The number of documents discarded since the most recent vacuuming\n   */\n  get dirtCount() {\n    return this._dirtCount;\n  }\n  /**\n   * A number between 0 and 1 giving an indication about the proportion of\n   * documents that are discarded, and can therefore be cleaned up by vacuuming.\n   * A value close to 0 means that the index is relatively clean, while a higher\n   * value means that the index is relatively dirty, and vacuuming could release\n   * memory.\n   */\n  get dirtFactor() {\n    return this._dirtCount / (1 + this._documentCount + this._dirtCount);\n  }\n  /**\n   * Returns `true` if a document with the given ID is present in the index and\n   * available for search, `false` otherwise\n   *\n   * @param id  The document ID\n   */\n  has(id) {\n    return this._idToShortId.has(id);\n  }\n  /**\n   * Returns the stored fields (as configured in the `storeFields` constructor\n   * option) for the given document ID. Returns `undefined` if the document is\n   * not present in the index.\n   *\n   * @param id  The document ID\n   */\n  getStoredFields(id) {\n    const shortId = this._idToShortId.get(id);\n    if (shortId == null) {\n      return void 0;\n    }\n    return this._storedFields.get(shortId);\n  }\n  /**\n   * Search for documents matching the given search query.\n   *\n   * The result is a list of scored document IDs matching the query, sorted by\n   * descending score, and each including data about which terms were matched and\n   * in which fields.\n   *\n   * ### Basic usage:\n   *\n   * ```javascript\n   * // Search for \"zen art motorcycle\" with default options: terms have to match\n   * // exactly, and individual terms are joined with OR\n   * miniSearch.search('zen art motorcycle')\n   * // => [ { id: 2, score: 2.77258, match: { ... } }, { id: 4, score: 1.38629, match: { ... } } ]\n   * ```\n   *\n   * ### Restrict search to specific fields:\n   *\n   * ```javascript\n   * // Search only in the 'title' field\n   * miniSearch.search('zen', { fields: ['title'] })\n   * ```\n   *\n   * ### Field boosting:\n   *\n   * ```javascript\n   * // Boost a field\n   * miniSearch.search('zen', { boost: { title: 2 } })\n   * ```\n   *\n   * ### Prefix search:\n   *\n   * ```javascript\n   * // Search for \"moto\" with prefix search (it will match documents\n   * // containing terms that start with \"moto\" or \"neuro\")\n   * miniSearch.search('moto neuro', { prefix: true })\n   * ```\n   *\n   * ### Fuzzy search:\n   *\n   * ```javascript\n   * // Search for \"ismael\" with fuzzy search (it will match documents containing\n   * // terms similar to \"ismael\", with a maximum edit distance of 0.2 term.length\n   * // (rounded to nearest integer)\n   * miniSearch.search('ismael', { fuzzy: 0.2 })\n   * ```\n   *\n   * ### Combining strategies:\n   *\n   * ```javascript\n   * // Mix of exact match, prefix search, and fuzzy search\n   * miniSearch.search('ismael mob', {\n   *  prefix: true,\n   *  fuzzy: 0.2\n   * })\n   * ```\n   *\n   * ### Advanced prefix and fuzzy search:\n   *\n   * ```javascript\n   * // Perform fuzzy and prefix search depending on the search term. Here\n   * // performing prefix and fuzzy search only on terms longer than 3 characters\n   * miniSearch.search('ismael mob', {\n   *  prefix: term => term.length > 3\n   *  fuzzy: term => term.length > 3 ? 0.2 : null\n   * })\n   * ```\n   *\n   * ### Combine with AND:\n   *\n   * ```javascript\n   * // Combine search terms with AND (to match only documents that contain both\n   * // \"motorcycle\" and \"art\")\n   * miniSearch.search('motorcycle art', { combineWith: 'AND' })\n   * ```\n   *\n   * ### Combine with AND_NOT:\n   *\n   * There is also an AND_NOT combinator, that finds documents that match the\n   * first term, but do not match any of the other terms. This combinator is\n   * rarely useful with simple queries, and is meant to be used with advanced\n   * query combinations (see later for more details).\n   *\n   * ### Filtering results:\n   *\n   * ```javascript\n   * // Filter only results in the 'fiction' category (assuming that 'category'\n   * // is a stored field)\n   * miniSearch.search('motorcycle art', {\n   *   filter: (result) => result.category === 'fiction'\n   * })\n   * ```\n   *\n   * ### Wildcard query\n   *\n   * Searching for an empty string (assuming the default tokenizer) returns no\n   * results. Sometimes though, one needs to match all documents, like in a\n   * \"wildcard\" search. This is possible by passing the special value\n   * {@link MiniSearch.wildcard} as the query:\n   *\n   * ```javascript\n   * // Return search results for all documents\n   * miniSearch.search(MiniSearch.wildcard)\n   * ```\n   *\n   * Note that search options such as `filter` and `boostDocument` are still\n   * applied, influencing which results are returned, and their order:\n   *\n   * ```javascript\n   * // Return search results for all documents in the 'fiction' category\n   * miniSearch.search(MiniSearch.wildcard, {\n   *   filter: (result) => result.category === 'fiction'\n   * })\n   * ```\n   *\n   * ### Advanced combination of queries:\n   *\n   * It is possible to combine different subqueries with OR, AND, and AND_NOT,\n   * and even with different search options, by passing a query expression\n   * tree object as the first argument, instead of a string.\n   *\n   * ```javascript\n   * // Search for documents that contain \"zen\" and (\"motorcycle\" or \"archery\")\n   * miniSearch.search({\n   *   combineWith: 'AND',\n   *   queries: [\n   *     'zen',\n   *     {\n   *       combineWith: 'OR',\n   *       queries: ['motorcycle', 'archery']\n   *     }\n   *   ]\n   * })\n   *\n   * // Search for documents that contain (\"apple\" or \"pear\") but not \"juice\" and\n   * // not \"tree\"\n   * miniSearch.search({\n   *   combineWith: 'AND_NOT',\n   *   queries: [\n   *     {\n   *       combineWith: 'OR',\n   *       queries: ['apple', 'pear']\n   *     },\n   *     'juice',\n   *     'tree'\n   *   ]\n   * })\n   * ```\n   *\n   * Each node in the expression tree can be either a string, or an object that\n   * supports all {@link SearchOptions} fields, plus a `queries` array field for\n   * subqueries.\n   *\n   * Note that, while this can become complicated to do by hand for complex or\n   * deeply nested queries, it provides a formalized expression tree API for\n   * external libraries that implement a parser for custom query languages.\n   *\n   * @param query  Search query\n   * @param searchOptions  Search options. Each option, if not given, defaults to the corresponding value of `searchOptions` given to the constructor, or to the library default.\n   */\n  search(query, searchOptions = {}) {\n    const { searchOptions: globalSearchOptions } = this._options;\n    const searchOptionsWithDefaults = Object.assign(Object.assign({}, globalSearchOptions), searchOptions);\n    const rawResults = this.executeQuery(query, searchOptions);\n    const results = [];\n    for (const [docId, { score, terms, match }] of rawResults) {\n      const quality = terms.length || 1;\n      const result = {\n        id: this._documentIds.get(docId),\n        score: score * quality,\n        terms: Object.keys(match),\n        queryTerms: terms,\n        match\n      };\n      Object.assign(result, this._storedFields.get(docId));\n      if (searchOptionsWithDefaults.filter == null || searchOptionsWithDefaults.filter(result)) {\n        results.push(result);\n      }\n    }\n    if (query === _MiniSearch.wildcard && searchOptionsWithDefaults.boostDocument == null) {\n      return results;\n    }\n    results.sort(byScore);\n    return results;\n  }\n  /**\n   * Provide suggestions for the given search query\n   *\n   * The result is a list of suggested modified search queries, derived from the\n   * given search query, each with a relevance score, sorted by descending score.\n   *\n   * By default, it uses the same options used for search, except that by\n   * default it performs prefix search on the last term of the query, and\n   * combine terms with `'AND'` (requiring all query terms to match). Custom\n   * options can be passed as a second argument. Defaults can be changed upon\n   * calling the {@link MiniSearch} constructor, by passing a\n   * `autoSuggestOptions` option.\n   *\n   * ### Basic usage:\n   *\n   * ```javascript\n   * // Get suggestions for 'neuro':\n   * miniSearch.autoSuggest('neuro')\n   * // => [ { suggestion: 'neuromancer', terms: [ 'neuromancer' ], score: 0.46240 } ]\n   * ```\n   *\n   * ### Multiple words:\n   *\n   * ```javascript\n   * // Get suggestions for 'zen ar':\n   * miniSearch.autoSuggest('zen ar')\n   * // => [\n   * //  { suggestion: 'zen archery art', terms: [ 'zen', 'archery', 'art' ], score: 1.73332 },\n   * //  { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 }\n   * // ]\n   * ```\n   *\n   * ### Fuzzy suggestions:\n   *\n   * ```javascript\n   * // Correct spelling mistakes using fuzzy search:\n   * miniSearch.autoSuggest('neromancer', { fuzzy: 0.2 })\n   * // => [ { suggestion: 'neuromancer', terms: [ 'neuromancer' ], score: 1.03998 } ]\n   * ```\n   *\n   * ### Filtering:\n   *\n   * ```javascript\n   * // Get suggestions for 'zen ar', but only within the 'fiction' category\n   * // (assuming that 'category' is a stored field):\n   * miniSearch.autoSuggest('zen ar', {\n   *   filter: (result) => result.category === 'fiction'\n   * })\n   * // => [\n   * //  { suggestion: 'zen archery art', terms: [ 'zen', 'archery', 'art' ], score: 1.73332 },\n   * //  { suggestion: 'zen art', terms: [ 'zen', 'art' ], score: 1.21313 }\n   * // ]\n   * ```\n   *\n   * @param queryString  Query string to be expanded into suggestions\n   * @param options  Search options. The supported options and default values\n   * are the same as for the {@link MiniSearch#search} method, except that by\n   * default prefix search is performed on the last term in the query, and terms\n   * are combined with `'AND'`.\n   * @return  A sorted array of suggestions sorted by relevance score.\n   */\n  autoSuggest(queryString, options = {}) {\n    options = Object.assign(Object.assign({}, this._options.autoSuggestOptions), options);\n    const suggestions = /* @__PURE__ */ new Map();\n    for (const { score, terms } of this.search(queryString, options)) {\n      const phrase = terms.join(\" \");\n      const suggestion = suggestions.get(phrase);\n      if (suggestion != null) {\n        suggestion.score += score;\n        suggestion.count += 1;\n      } else {\n        suggestions.set(phrase, { score, terms, count: 1 });\n      }\n    }\n    const results = [];\n    for (const [suggestion, { score, terms, count }] of suggestions) {\n      results.push({ suggestion, terms, score: score / count });\n    }\n    results.sort(byScore);\n    return results;\n  }\n  /**\n   * Total number of documents available to search\n   */\n  get documentCount() {\n    return this._documentCount;\n  }\n  /**\n   * Number of terms in the index\n   */\n  get termCount() {\n    return this._index.size;\n  }\n  /**\n   * Deserializes a JSON index (serialized with `JSON.stringify(miniSearch)`)\n   * and instantiates a MiniSearch instance. It should be given the same options\n   * originally used when serializing the index.\n   *\n   * ### Usage:\n   *\n   * ```javascript\n   * // If the index was serialized with:\n   * let miniSearch = new MiniSearch({ fields: ['title', 'text'] })\n   * miniSearch.addAll(documents)\n   *\n   * const json = JSON.stringify(miniSearch)\n   * // It can later be deserialized like this:\n   * miniSearch = MiniSearch.loadJSON(json, { fields: ['title', 'text'] })\n   * ```\n   *\n   * @param json  JSON-serialized index\n   * @param options  configuration options, same as the constructor\n   * @return An instance of MiniSearch deserialized from the given JSON.\n   */\n  static loadJSON(json, options) {\n    if (options == null) {\n      throw new Error(\"MiniSearch: loadJSON should be given the same options used when serializing the index\");\n    }\n    return this.loadJS(JSON.parse(json), options);\n  }\n  /**\n   * Async equivalent of {@link MiniSearch.loadJSON}\n   *\n   * This function is an alternative to {@link MiniSearch.loadJSON} that returns\n   * a promise, and loads the index in batches, leaving pauses between them to avoid\n   * blocking the main thread. It tends to be slower than the synchronous\n   * version, but does not block the main thread, so it can be a better choice\n   * when deserializing very large indexes.\n   *\n   * @param json  JSON-serialized index\n   * @param options  configuration options, same as the constructor\n   * @return A Promise that will resolve to an instance of MiniSearch deserialized from the given JSON.\n   */\n  static loadJSONAsync(json, options) {\n    return __awaiter(this, void 0, void 0, function* () {\n      if (options == null) {\n        throw new Error(\"MiniSearch: loadJSON should be given the same options used when serializing the index\");\n      }\n      return this.loadJSAsync(JSON.parse(json), options);\n    });\n  }\n  /**\n   * Returns the default value of an option. It will throw an error if no option\n   * with the given name exists.\n   *\n   * @param optionName  Name of the option\n   * @return The default value of the given option\n   *\n   * ### Usage:\n   *\n   * ```javascript\n   * // Get default tokenizer\n   * MiniSearch.getDefault('tokenize')\n   *\n   * // Get default term processor\n   * MiniSearch.getDefault('processTerm')\n   *\n   * // Unknown options will throw an error\n   * MiniSearch.getDefault('notExisting')\n   * // => throws 'MiniSearch: unknown option \"notExisting\"'\n   * ```\n   */\n  static getDefault(optionName) {\n    if (defaultOptions.hasOwnProperty(optionName)) {\n      return getOwnProperty(defaultOptions, optionName);\n    } else {\n      throw new Error(`MiniSearch: unknown option \"${optionName}\"`);\n    }\n  }\n  /**\n   * @ignore\n   */\n  static loadJS(js, options) {\n    const { index, documentIds, fieldLength, storedFields, serializationVersion } = js;\n    const miniSearch = this.instantiateMiniSearch(js, options);\n    miniSearch._documentIds = objectToNumericMap(documentIds);\n    miniSearch._fieldLength = objectToNumericMap(fieldLength);\n    miniSearch._storedFields = objectToNumericMap(storedFields);\n    for (const [shortId, id] of miniSearch._documentIds) {\n      miniSearch._idToShortId.set(id, shortId);\n    }\n    for (const [term, data] of index) {\n      const dataMap = /* @__PURE__ */ new Map();\n      for (const fieldId of Object.keys(data)) {\n        let indexEntry = data[fieldId];\n        if (serializationVersion === 1) {\n          indexEntry = indexEntry.ds;\n        }\n        dataMap.set(parseInt(fieldId, 10), objectToNumericMap(indexEntry));\n      }\n      miniSearch._index.set(term, dataMap);\n    }\n    return miniSearch;\n  }\n  /**\n   * @ignore\n   */\n  static loadJSAsync(js, options) {\n    return __awaiter(this, void 0, void 0, function* () {\n      const { index, documentIds, fieldLength, storedFields, serializationVersion } = js;\n      const miniSearch = this.instantiateMiniSearch(js, options);\n      miniSearch._documentIds = yield objectToNumericMapAsync(documentIds);\n      miniSearch._fieldLength = yield objectToNumericMapAsync(fieldLength);\n      miniSearch._storedFields = yield objectToNumericMapAsync(storedFields);\n      for (const [shortId, id] of miniSearch._documentIds) {\n        miniSearch._idToShortId.set(id, shortId);\n      }\n      let count = 0;\n      for (const [term, data] of index) {\n        const dataMap = /* @__PURE__ */ new Map();\n        for (const fieldId of Object.keys(data)) {\n          let indexEntry = data[fieldId];\n          if (serializationVersion === 1) {\n            indexEntry = indexEntry.ds;\n          }\n          dataMap.set(parseInt(fieldId, 10), yield objectToNumericMapAsync(indexEntry));\n        }\n        if (++count % 1e3 === 0)\n          yield wait(0);\n        miniSearch._index.set(term, dataMap);\n      }\n      return miniSearch;\n    });\n  }\n  /**\n   * @ignore\n   */\n  static instantiateMiniSearch(js, options) {\n    const { documentCount, nextId, fieldIds, averageFieldLength, dirtCount, serializationVersion } = js;\n    if (serializationVersion !== 1 && serializationVersion !== 2) {\n      throw new Error(\"MiniSearch: cannot deserialize an index created with an incompatible version\");\n    }\n    const miniSearch = new _MiniSearch(options);\n    miniSearch._documentCount = documentCount;\n    miniSearch._nextId = nextId;\n    miniSearch._idToShortId = /* @__PURE__ */ new Map();\n    miniSearch._fieldIds = fieldIds;\n    miniSearch._avgFieldLength = averageFieldLength;\n    miniSearch._dirtCount = dirtCount || 0;\n    miniSearch._index = new SearchableMap();\n    return miniSearch;\n  }\n  /**\n   * @ignore\n   */\n  executeQuery(query, searchOptions = {}) {\n    if (query === _MiniSearch.wildcard) {\n      return this.executeWildcardQuery(searchOptions);\n    }\n    if (typeof query !== \"string\") {\n      const options2 = Object.assign(Object.assign(Object.assign({}, searchOptions), query), { queries: void 0 });\n      const results2 = query.queries.map((subquery) => this.executeQuery(subquery, options2));\n      return this.combineResults(results2, options2.combineWith);\n    }\n    const { tokenize, processTerm, searchOptions: globalSearchOptions } = this._options;\n    const options = Object.assign(Object.assign({ tokenize, processTerm }, globalSearchOptions), searchOptions);\n    const { tokenize: searchTokenize, processTerm: searchProcessTerm } = options;\n    const terms = searchTokenize(query).flatMap((term) => searchProcessTerm(term)).filter((term) => !!term);\n    const queries = terms.map(termToQuerySpec(options));\n    const results = queries.map((query2) => this.executeQuerySpec(query2, options));\n    return this.combineResults(results, options.combineWith);\n  }\n  /**\n   * @ignore\n   */\n  executeQuerySpec(query, searchOptions) {\n    const options = Object.assign(Object.assign({}, this._options.searchOptions), searchOptions);\n    const boosts = (options.fields || this._options.fields).reduce((boosts2, field) => Object.assign(Object.assign({}, boosts2), { [field]: getOwnProperty(options.boost, field) || 1 }), {});\n    const { boostDocument, weights, maxFuzzy, bm25: bm25params } = options;\n    const { fuzzy: fuzzyWeight, prefix: prefixWeight } = Object.assign(Object.assign({}, defaultSearchOptions.weights), weights);\n    const data = this._index.get(query.term);\n    const results = this.termResults(query.term, query.term, 1, query.termBoost, data, boosts, boostDocument, bm25params);\n    let prefixMatches;\n    let fuzzyMatches;\n    if (query.prefix) {\n      prefixMatches = this._index.atPrefix(query.term);\n    }\n    if (query.fuzzy) {\n      const fuzzy = query.fuzzy === true ? 0.2 : query.fuzzy;\n      const maxDistance = fuzzy < 1 ? Math.min(maxFuzzy, Math.round(query.term.length * fuzzy)) : fuzzy;\n      if (maxDistance)\n        fuzzyMatches = this._index.fuzzyGet(query.term, maxDistance);\n    }\n    if (prefixMatches) {\n      for (const [term, data2] of prefixMatches) {\n        const distance = term.length - query.term.length;\n        if (!distance) {\n          continue;\n        }\n        fuzzyMatches === null || fuzzyMatches === void 0 ? void 0 : fuzzyMatches.delete(term);\n        const weight = prefixWeight * term.length / (term.length + 0.3 * distance);\n        this.termResults(query.term, term, weight, query.termBoost, data2, boosts, boostDocument, bm25params, results);\n      }\n    }\n    if (fuzzyMatches) {\n      for (const term of fuzzyMatches.keys()) {\n        const [data2, distance] = fuzzyMatches.get(term);\n        if (!distance) {\n          continue;\n        }\n        const weight = fuzzyWeight * term.length / (term.length + distance);\n        this.termResults(query.term, term, weight, query.termBoost, data2, boosts, boostDocument, bm25params, results);\n      }\n    }\n    return results;\n  }\n  /**\n   * @ignore\n   */\n  executeWildcardQuery(searchOptions) {\n    const results = /* @__PURE__ */ new Map();\n    const options = Object.assign(Object.assign({}, this._options.searchOptions), searchOptions);\n    for (const [shortId, id] of this._documentIds) {\n      const score = options.boostDocument ? options.boostDocument(id, \"\", this._storedFields.get(shortId)) : 1;\n      results.set(shortId, {\n        score,\n        terms: [],\n        match: {}\n      });\n    }\n    return results;\n  }\n  /**\n   * @ignore\n   */\n  combineResults(results, combineWith = OR) {\n    if (results.length === 0) {\n      return /* @__PURE__ */ new Map();\n    }\n    const operator = combineWith.toLowerCase();\n    const combinator = combinators[operator];\n    if (!combinator) {\n      throw new Error(`Invalid combination operator: ${combineWith}`);\n    }\n    return results.reduce(combinator) || /* @__PURE__ */ new Map();\n  }\n  /**\n   * Allows serialization of the index to JSON, to possibly store it and later\n   * deserialize it with {@link MiniSearch.loadJSON}.\n   *\n   * Normally one does not directly call this method, but rather call the\n   * standard JavaScript `JSON.stringify()` passing the {@link MiniSearch}\n   * instance, and JavaScript will internally call this method. Upon\n   * deserialization, one must pass to {@link MiniSearch.loadJSON} the same\n   * options used to create the original instance that was serialized.\n   *\n   * ### Usage:\n   *\n   * ```javascript\n   * // Serialize the index:\n   * let miniSearch = new MiniSearch({ fields: ['title', 'text'] })\n   * miniSearch.addAll(documents)\n   * const json = JSON.stringify(miniSearch)\n   *\n   * // Later, to deserialize it:\n   * miniSearch = MiniSearch.loadJSON(json, { fields: ['title', 'text'] })\n   * ```\n   *\n   * @return A plain-object serializable representation of the search index.\n   */\n  toJSON() {\n    const index = [];\n    for (const [term, fieldIndex] of this._index) {\n      const data = {};\n      for (const [fieldId, freqs] of fieldIndex) {\n        data[fieldId] = Object.fromEntries(freqs);\n      }\n      index.push([term, data]);\n    }\n    return {\n      documentCount: this._documentCount,\n      nextId: this._nextId,\n      documentIds: Object.fromEntries(this._documentIds),\n      fieldIds: this._fieldIds,\n      fieldLength: Object.fromEntries(this._fieldLength),\n      averageFieldLength: this._avgFieldLength,\n      storedFields: Object.fromEntries(this._storedFields),\n      dirtCount: this._dirtCount,\n      index,\n      serializationVersion: 2\n    };\n  }\n  /**\n   * @ignore\n   */\n  termResults(sourceTerm, derivedTerm, termWeight, termBoost, fieldTermData, fieldBoosts, boostDocumentFn, bm25params, results = /* @__PURE__ */ new Map()) {\n    if (fieldTermData == null)\n      return results;\n    for (const field of Object.keys(fieldBoosts)) {\n      const fieldBoost = fieldBoosts[field];\n      const fieldId = this._fieldIds[field];\n      const fieldTermFreqs = fieldTermData.get(fieldId);\n      if (fieldTermFreqs == null)\n        continue;\n      let matchingFields = fieldTermFreqs.size;\n      const avgFieldLength = this._avgFieldLength[fieldId];\n      for (const docId of fieldTermFreqs.keys()) {\n        if (!this._documentIds.has(docId)) {\n          this.removeTerm(fieldId, docId, derivedTerm);\n          matchingFields -= 1;\n          continue;\n        }\n        const docBoost = boostDocumentFn ? boostDocumentFn(this._documentIds.get(docId), derivedTerm, this._storedFields.get(docId)) : 1;\n        if (!docBoost)\n          continue;\n        const termFreq = fieldTermFreqs.get(docId);\n        const fieldLength = this._fieldLength.get(docId)[fieldId];\n        const rawScore = calcBM25Score(termFreq, matchingFields, this._documentCount, fieldLength, avgFieldLength, bm25params);\n        const weightedScore = termWeight * termBoost * fieldBoost * docBoost * rawScore;\n        const result = results.get(docId);\n        if (result) {\n          result.score += weightedScore;\n          assignUniqueTerm(result.terms, sourceTerm);\n          const match = getOwnProperty(result.match, derivedTerm);\n          if (match) {\n            match.push(field);\n          } else {\n            result.match[derivedTerm] = [field];\n          }\n        } else {\n          results.set(docId, {\n            score: weightedScore,\n            terms: [sourceTerm],\n            match: { [derivedTerm]: [field] }\n          });\n        }\n      }\n    }\n    return results;\n  }\n  /**\n   * @ignore\n   */\n  addTerm(fieldId, documentId, term) {\n    const indexData = this._index.fetch(term, createMap);\n    let fieldIndex = indexData.get(fieldId);\n    if (fieldIndex == null) {\n      fieldIndex = /* @__PURE__ */ new Map();\n      fieldIndex.set(documentId, 1);\n      indexData.set(fieldId, fieldIndex);\n    } else {\n      const docs = fieldIndex.get(documentId);\n      fieldIndex.set(documentId, (docs || 0) + 1);\n    }\n  }\n  /**\n   * @ignore\n   */\n  removeTerm(fieldId, documentId, term) {\n    if (!this._index.has(term)) {\n      this.warnDocumentChanged(documentId, fieldId, term);\n      return;\n    }\n    const indexData = this._index.fetch(term, createMap);\n    const fieldIndex = indexData.get(fieldId);\n    if (fieldIndex == null || fieldIndex.get(documentId) == null) {\n      this.warnDocumentChanged(documentId, fieldId, term);\n    } else if (fieldIndex.get(documentId) <= 1) {\n      if (fieldIndex.size <= 1) {\n        indexData.delete(fieldId);\n      } else {\n        fieldIndex.delete(documentId);\n      }\n    } else {\n      fieldIndex.set(documentId, fieldIndex.get(documentId) - 1);\n    }\n    if (this._index.get(term).size === 0) {\n      this._index.delete(term);\n    }\n  }\n  /**\n   * @ignore\n   */\n  warnDocumentChanged(shortDocumentId, fieldId, term) {\n    for (const fieldName of Object.keys(this._fieldIds)) {\n      if (this._fieldIds[fieldName] === fieldId) {\n        this._options.logger(\"warn\", `MiniSearch: document with ID ${this._documentIds.get(shortDocumentId)} has changed before removal: term \"${term}\" was not present in field \"${fieldName}\". Removing a document after it has changed can corrupt the index!`, \"version_conflict\");\n        return;\n      }\n    }\n  }\n  /**\n   * @ignore\n   */\n  addDocumentId(documentId) {\n    const shortDocumentId = this._nextId;\n    this._idToShortId.set(documentId, shortDocumentId);\n    this._documentIds.set(shortDocumentId, documentId);\n    this._documentCount += 1;\n    this._nextId += 1;\n    return shortDocumentId;\n  }\n  /**\n   * @ignore\n   */\n  addFields(fields) {\n    for (let i = 0; i < fields.length; i++) {\n      this._fieldIds[fields[i]] = i;\n    }\n  }\n  /**\n   * @ignore\n   */\n  addFieldLength(documentId, fieldId, count, length) {\n    let fieldLengths = this._fieldLength.get(documentId);\n    if (fieldLengths == null)\n      this._fieldLength.set(documentId, fieldLengths = []);\n    fieldLengths[fieldId] = length;\n    const averageFieldLength = this._avgFieldLength[fieldId] || 0;\n    const totalFieldLength = averageFieldLength * count + length;\n    this._avgFieldLength[fieldId] = totalFieldLength / (count + 1);\n  }\n  /**\n   * @ignore\n   */\n  removeFieldLength(documentId, fieldId, count, length) {\n    if (count === 1) {\n      this._avgFieldLength[fieldId] = 0;\n      return;\n    }\n    const totalFieldLength = this._avgFieldLength[fieldId] * count - length;\n    this._avgFieldLength[fieldId] = totalFieldLength / (count - 1);\n  }\n  /**\n   * @ignore\n   */\n  saveStoredFields(documentId, doc) {\n    const { storeFields, extractField } = this._options;\n    if (storeFields == null || storeFields.length === 0) {\n      return;\n    }\n    let documentFields = this._storedFields.get(documentId);\n    if (documentFields == null)\n      this._storedFields.set(documentId, documentFields = {});\n    for (const fieldName of storeFields) {\n      const fieldValue = extractField(doc, fieldName);\n      if (fieldValue !== void 0)\n        documentFields[fieldName] = fieldValue;\n    }\n  }\n};\nMiniSearch.wildcard = Symbol(\"*\");\nvar getOwnProperty = (object, property) => Object.prototype.hasOwnProperty.call(object, property) ? object[property] : void 0;\nvar combinators = {\n  [OR]: (a, b) => {\n    for (const docId of b.keys()) {\n      const existing = a.get(docId);\n      if (existing == null) {\n        a.set(docId, b.get(docId));\n      } else {\n        const { score, terms, match } = b.get(docId);\n        existing.score = existing.score + score;\n        existing.match = Object.assign(existing.match, match);\n        assignUniqueTerms(existing.terms, terms);\n      }\n    }\n    return a;\n  },\n  [AND]: (a, b) => {\n    const combined = /* @__PURE__ */ new Map();\n    for (const docId of b.keys()) {\n      const existing = a.get(docId);\n      if (existing == null)\n        continue;\n      const { score, terms, match } = b.get(docId);\n      assignUniqueTerms(existing.terms, terms);\n      combined.set(docId, {\n        score: existing.score + score,\n        terms: existing.terms,\n        match: Object.assign(existing.match, match)\n      });\n    }\n    return combined;\n  },\n  [AND_NOT]: (a, b) => {\n    for (const docId of b.keys())\n      a.delete(docId);\n    return a;\n  }\n};\nvar defaultBM25params = { k: 1.2, b: 0.7, d: 0.5 };\nvar calcBM25Score = (termFreq, matchingCount, totalCount, fieldLength, avgFieldLength, bm25params) => {\n  const { k, b, d } = bm25params;\n  const invDocFreq = Math.log(1 + (totalCount - matchingCount + 0.5) / (matchingCount + 0.5));\n  return invDocFreq * (d + termFreq * (k + 1) / (termFreq + k * (1 - b + b * fieldLength / avgFieldLength)));\n};\nvar termToQuerySpec = (options) => (term, i, terms) => {\n  const fuzzy = typeof options.fuzzy === \"function\" ? options.fuzzy(term, i, terms) : options.fuzzy || false;\n  const prefix = typeof options.prefix === \"function\" ? options.prefix(term, i, terms) : options.prefix === true;\n  const termBoost = typeof options.boostTerm === \"function\" ? options.boostTerm(term, i, terms) : 1;\n  return { term, fuzzy, prefix, termBoost };\n};\nvar defaultOptions = {\n  idField: \"id\",\n  extractField: (document, fieldName) => document[fieldName],\n  tokenize: (text) => text.split(SPACE_OR_PUNCTUATION),\n  processTerm: (term) => term.toLowerCase(),\n  fields: void 0,\n  searchOptions: void 0,\n  storeFields: [],\n  logger: (level, message) => {\n    if (typeof (console === null || console === void 0 ? void 0 : console[level]) === \"function\")\n      console[level](message);\n  },\n  autoVacuum: true\n};\nvar defaultSearchOptions = {\n  combineWith: OR,\n  prefix: false,\n  fuzzy: false,\n  maxFuzzy: 6,\n  boost: {},\n  weights: { fuzzy: 0.45, prefix: 0.375 },\n  bm25: defaultBM25params\n};\nvar defaultAutoSuggestOptions = {\n  combineWith: AND,\n  prefix: (term, i, terms) => i === terms.length - 1\n};\nvar defaultVacuumOptions = { batchSize: 1e3, batchWait: 10 };\nvar defaultVacuumConditions = { minDirtFactor: 0.1, minDirtCount: 20 };\nvar defaultAutoVacuumOptions = Object.assign(Object.assign({}, defaultVacuumOptions), defaultVacuumConditions);\nvar assignUniqueTerm = (target, term) => {\n  if (!target.includes(term))\n    target.push(term);\n};\nvar assignUniqueTerms = (target, source) => {\n  for (const term of source) {\n    if (!target.includes(term))\n      target.push(term);\n  }\n};\nvar byScore = ({ score: a }, { score: b }) => b - a;\nvar createMap = () => /* @__PURE__ */ new Map();\nvar objectToNumericMap = (object) => {\n  const map = /* @__PURE__ */ new Map();\n  for (const key of Object.keys(object)) {\n    map.set(parseInt(key, 10), object[key]);\n  }\n  return map;\n};\nvar objectToNumericMapAsync = (object) => __awaiter(void 0, void 0, void 0, function* () {\n  const map = /* @__PURE__ */ new Map();\n  let count = 0;\n  for (const key of Object.keys(object)) {\n    map.set(parseInt(key, 10), object[key]);\n    if (++count % 1e3 === 0) {\n      yield wait(0);\n    }\n  }\n  return map;\n});\nvar wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\nvar SPACE_OR_PUNCTUATION = /[\\n\\r\\p{Z}\\p{P}]+/u;\nexport {\n  MiniSearch as default\n};\n//# sourceMappingURL=vitepress___minisearch.js.map\n"
  },
  {
    "path": "docs/.vitepress/cache/deps/vue.js",
    "content": "import {\n  BaseTransition,\n  BaseTransitionPropsValidators,\n  Comment,\n  DeprecationTypes,\n  EffectScope,\n  ErrorCodes,\n  ErrorTypeStrings,\n  Fragment,\n  KeepAlive,\n  ReactiveEffect,\n  Static,\n  Suspense,\n  Teleport,\n  Text,\n  TrackOpTypes,\n  Transition,\n  TransitionGroup,\n  TriggerOpTypes,\n  VueElement,\n  assertNumber,\n  callWithAsyncErrorHandling,\n  callWithErrorHandling,\n  camelize,\n  capitalize,\n  cloneVNode,\n  compatUtils,\n  compile,\n  computed,\n  createApp,\n  createBaseVNode,\n  createBlock,\n  createCommentVNode,\n  createElementBlock,\n  createHydrationRenderer,\n  createPropsRestProxy,\n  createRenderer,\n  createSSRApp,\n  createSlots,\n  createStaticVNode,\n  createTextVNode,\n  createVNode,\n  customRef,\n  defineAsyncComponent,\n  defineComponent,\n  defineCustomElement,\n  defineEmits,\n  defineExpose,\n  defineModel,\n  defineOptions,\n  defineProps,\n  defineSSRCustomElement,\n  defineSlots,\n  devtools,\n  effect,\n  effectScope,\n  getCurrentInstance,\n  getCurrentScope,\n  getCurrentWatcher,\n  getTransitionRawChildren,\n  guardReactiveProps,\n  h,\n  handleError,\n  hasInjectionContext,\n  hydrate,\n  hydrateOnIdle,\n  hydrateOnInteraction,\n  hydrateOnMediaQuery,\n  hydrateOnVisible,\n  initCustomFormatter,\n  initDirectivesForSSR,\n  inject,\n  isMemoSame,\n  isProxy,\n  isReactive,\n  isReadonly,\n  isRef,\n  isRuntimeOnly,\n  isShallow,\n  isVNode,\n  markRaw,\n  mergeDefaults,\n  mergeModels,\n  mergeProps,\n  nextTick,\n  normalizeClass,\n  normalizeProps,\n  normalizeStyle,\n  onActivated,\n  onBeforeMount,\n  onBeforeUnmount,\n  onBeforeUpdate,\n  onDeactivated,\n  onErrorCaptured,\n  onMounted,\n  onRenderTracked,\n  onRenderTriggered,\n  onScopeDispose,\n  onServerPrefetch,\n  onUnmounted,\n  onUpdated,\n  onWatcherCleanup,\n  openBlock,\n  popScopeId,\n  provide,\n  proxyRefs,\n  pushScopeId,\n  queuePostFlushCb,\n  reactive,\n  readonly,\n  ref,\n  registerRuntimeCompiler,\n  render,\n  renderList,\n  renderSlot,\n  resolveComponent,\n  resolveDirective,\n  resolveDynamicComponent,\n  resolveFilter,\n  resolveTransitionHooks,\n  setBlockTracking,\n  setDevtoolsHook,\n  setTransitionHooks,\n  shallowReactive,\n  shallowReadonly,\n  shallowRef,\n  ssrContextKey,\n  ssrUtils,\n  stop,\n  toDisplayString,\n  toHandlerKey,\n  toHandlers,\n  toRaw,\n  toRef,\n  toRefs,\n  toValue,\n  transformVNodeArgs,\n  triggerRef,\n  unref,\n  useAttrs,\n  useCssModule,\n  useCssVars,\n  useHost,\n  useId,\n  useModel,\n  useSSRContext,\n  useShadowRoot,\n  useSlots,\n  useTemplateRef,\n  useTransitionState,\n  vModelCheckbox,\n  vModelDynamic,\n  vModelRadio,\n  vModelSelect,\n  vModelText,\n  vShow,\n  version,\n  warn,\n  watch,\n  watchEffect,\n  watchPostEffect,\n  watchSyncEffect,\n  withAsyncContext,\n  withCtx,\n  withDefaults,\n  withDirectives,\n  withKeys,\n  withMemo,\n  withModifiers,\n  withScopeId\n} from \"./chunk-CQOUZRMK.js\";\nexport {\n  BaseTransition,\n  BaseTransitionPropsValidators,\n  Comment,\n  DeprecationTypes,\n  EffectScope,\n  ErrorCodes,\n  ErrorTypeStrings,\n  Fragment,\n  KeepAlive,\n  ReactiveEffect,\n  Static,\n  Suspense,\n  Teleport,\n  Text,\n  TrackOpTypes,\n  Transition,\n  TransitionGroup,\n  TriggerOpTypes,\n  VueElement,\n  assertNumber,\n  callWithAsyncErrorHandling,\n  callWithErrorHandling,\n  camelize,\n  capitalize,\n  cloneVNode,\n  compatUtils,\n  compile,\n  computed,\n  createApp,\n  createBlock,\n  createCommentVNode,\n  createElementBlock,\n  createBaseVNode as createElementVNode,\n  createHydrationRenderer,\n  createPropsRestProxy,\n  createRenderer,\n  createSSRApp,\n  createSlots,\n  createStaticVNode,\n  createTextVNode,\n  createVNode,\n  customRef,\n  defineAsyncComponent,\n  defineComponent,\n  defineCustomElement,\n  defineEmits,\n  defineExpose,\n  defineModel,\n  defineOptions,\n  defineProps,\n  defineSSRCustomElement,\n  defineSlots,\n  devtools,\n  effect,\n  effectScope,\n  getCurrentInstance,\n  getCurrentScope,\n  getCurrentWatcher,\n  getTransitionRawChildren,\n  guardReactiveProps,\n  h,\n  handleError,\n  hasInjectionContext,\n  hydrate,\n  hydrateOnIdle,\n  hydrateOnInteraction,\n  hydrateOnMediaQuery,\n  hydrateOnVisible,\n  initCustomFormatter,\n  initDirectivesForSSR,\n  inject,\n  isMemoSame,\n  isProxy,\n  isReactive,\n  isReadonly,\n  isRef,\n  isRuntimeOnly,\n  isShallow,\n  isVNode,\n  markRaw,\n  mergeDefaults,\n  mergeModels,\n  mergeProps,\n  nextTick,\n  normalizeClass,\n  normalizeProps,\n  normalizeStyle,\n  onActivated,\n  onBeforeMount,\n  onBeforeUnmount,\n  onBeforeUpdate,\n  onDeactivated,\n  onErrorCaptured,\n  onMounted,\n  onRenderTracked,\n  onRenderTriggered,\n  onScopeDispose,\n  onServerPrefetch,\n  onUnmounted,\n  onUpdated,\n  onWatcherCleanup,\n  openBlock,\n  popScopeId,\n  provide,\n  proxyRefs,\n  pushScopeId,\n  queuePostFlushCb,\n  reactive,\n  readonly,\n  ref,\n  registerRuntimeCompiler,\n  render,\n  renderList,\n  renderSlot,\n  resolveComponent,\n  resolveDirective,\n  resolveDynamicComponent,\n  resolveFilter,\n  resolveTransitionHooks,\n  setBlockTracking,\n  setDevtoolsHook,\n  setTransitionHooks,\n  shallowReactive,\n  shallowReadonly,\n  shallowRef,\n  ssrContextKey,\n  ssrUtils,\n  stop,\n  toDisplayString,\n  toHandlerKey,\n  toHandlers,\n  toRaw,\n  toRef,\n  toRefs,\n  toValue,\n  transformVNodeArgs,\n  triggerRef,\n  unref,\n  useAttrs,\n  useCssModule,\n  useCssVars,\n  useHost,\n  useId,\n  useModel,\n  useSSRContext,\n  useShadowRoot,\n  useSlots,\n  useTemplateRef,\n  useTransitionState,\n  vModelCheckbox,\n  vModelDynamic,\n  vModelRadio,\n  vModelSelect,\n  vModelText,\n  vShow,\n  version,\n  warn,\n  watch,\n  watchEffect,\n  watchPostEffect,\n  watchSyncEffect,\n  withAsyncContext,\n  withCtx,\n  withDefaults,\n  withDirectives,\n  withKeys,\n  withMemo,\n  withModifiers,\n  withScopeId\n};\n//# sourceMappingURL=vue.js.map\n"
  },
  {
    "path": "docs/.vitepress/config.mts",
    "content": "import { defineConfig } from 'vitepress'\n\n// https://vitepress.dev/reference/site-config\nexport default defineConfig({\n  title: 'FileCodeBox',\n  description: '简单高效的文件分享工具',\n  lang: 'zh-CN',\n  lastUpdated: true,\n  locales: {\n    root: {\n      label: '简体中文',\n      lang: 'zh-CN',\n      title: 'FileCodeBox',\n      description: '匿名口令分享文本，文件',\n      themeConfig: {\n        logo: '/logo_small.png',\n        nav: [\n          { text: '首页', link: '/' },\n          { text: '指南', link: '/guide/getting-started' },\n          { text: 'API', link: '/api/' },\n          { text: '优秀案例', link: '/showcase' },\n          { text: 'Demo', link: 'https://share.lanol.cn' },\n          {\n            text: '了解更多',\n            items: [\n              { text: '更新日志', link: '/changelog' },\n              { text: '贡献指南', link: '/contributing' },\n            ],\n          },\n        ],\n\n        sidebar: {\n          '/guide/': [\n            {\n              text: '介绍',\n              items: [\n                {\n                  text: '什么是 FileCodeBox',\n                  link: '/guide/introduction',\n                },\n                { text: '快速开始', link: '/guide/getting-started' },\n              ],\n            },\n            {\n              text: '基础功能',\n              items: [\n                { text: '文件上传', link: '/guide/upload' },\n                { text: '文件分享', link: '/guide/share' },\n                { text: '文件管理', link: '/guide/management' },\n              ],\n            },\n            {\n              text: '高级特性',\n              items: [\n                { text: '存储配置', link: '/guide/storage' },\n                { text: '安全设置', link: '/guide/security' },\n                { text: '系统配置', link: '/guide/configuration' },\n              ],\n            },\n          ],\n          '/api/': [\n            {\n              text: 'API 参考',\n              items: [\n                { text: '分享接口', link: '/api/#分享接口' },\n                { text: '管理接口', link: '/api/#管理接口' },\n                { text: '错误响应', link: '/api/#错误响应' },\n                { text: '状态码说明', link: '/api/#状态码说明' },\n              ],\n            },\n          ],\n          '/showcase': [\n            {\n              text: '优秀案例',\n              items: [\n                { text: '案例展示', link: '/showcase' },\n              ],\n            },\n          ],\n        },\n        socialLinks: [\n          { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' },\n        ],\n        footer: {\n          message: '基于 LGPL-3.0 license 发布',\n          copyright: 'Copyright © 2022-present FileCodeBox',\n        },\n        search: {\n          provider: 'local',\n        },\n        outline: {\n          level: [2, 3],\n          label: '目录',\n        },\n      },\n    },\n    en: {\n      label: 'English',\n      lang: 'en-US',\n      title: 'FileCodeBox',\n      description: 'Simple and efficient file sharing tool',\n      themeConfig: {\n        logo: '/logo_small.png',\n        nav: [\n          { text: 'Home', link: '/en/' },\n          { text: 'Guide', link: '/en/guide/getting-started' },\n          { text: 'API', link: '/en/api/' },\n          { text: 'Showcase', link: '/en/showcase' },\n          { text: 'Demo', link: 'https://share.lanol.cn' },\n          {\n            text: 'More',\n            items: [\n              { text: 'Changelog', link: '/en/changelog' },\n              { text: 'Contributing', link: '/en/contributing' },\n            ],\n          },\n        ],\n\n        sidebar: {\n          '/en/guide/': [\n            {\n              text: 'Introduction',\n              items: [\n                {\n                  text: 'What is FileCodeBox',\n                  link: '/en/guide/introduction',\n                },\n                {\n                  text: 'Getting Started',\n                  link: '/en/guide/getting-started',\n                },\n              ],\n            },\n            {\n              text: 'Basic Features',\n              items: [\n                { text: 'File Upload', link: '/en/guide/upload' },\n                { text: 'File Sharing', link: '/en/guide/share' },\n                { text: 'File Management', link: '/en/guide/management' },\n              ],\n            },\n            {\n              text: 'Advanced Features',\n              items: [\n                { text: 'Storage Configuration', link: '/en/guide/storage' },\n                { text: 'Security Settings', link: '/en/guide/security' },\n                {\n                  text: 'System Configuration',\n                  link: '/en/guide/configuration',\n                },\n              ],\n            },\n          ],\n          '/en/api/': [\n            {\n              text: 'API Reference',\n              items: [\n                { text: 'Share API', link: '/en/api/#share-api' },\n                { text: 'Admin API', link: '/en/api/#admin-api' },\n                { text: 'Error Response', link: '/en/api/#error-response' },\n                { text: 'Status Codes', link: '/en/api/#status-codes' },\n              ],\n            },\n          ],\n          '/en/showcase': [\n            {\n              text: 'Showcase',\n              items: [\n                { text: 'Case Studies', link: '/en/showcase' },\n              ],\n            },\n          ],\n        },\n        socialLinks: [\n          { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' },\n        ],\n        footer: {\n          message: 'Released under the LGPL-3.0 license',\n          copyright: 'Copyright © 2022-present FileCodeBox',\n        },\n        search: {\n          provider: 'local',\n        },\n        outline: {\n          level: [2, 3],\n          label: '目录',\n        },\n      },\n    },\n  },\n\n  themeConfig: {\n    // 语言切换器\n    langMenuLabel: '切换语言',\n\n    // 社交链接\n    socialLinks: [\n      { icon: 'github', link: 'https://github.com/vastsa/FileCodeBox' },\n    ],\n\n    // 页脚\n    footer: {\n      message: '基于 LGPL-3.0 license 发布',\n      copyright: 'Copyright © 2022-present FileCodeBox',\n    },\n\n    // 搜索\n    search: {\n      provider: 'local',\n      options: {\n        locales: {\n          zh: {\n            translations: {\n              button: {\n                buttonText: '搜索文档',\n                buttonAriaLabel: '搜索文档',\n              },\n              modal: {\n                noResultsText: '无法找到相关结果',\n                resetButtonTitle: '清除查询条件',\n                footer: {\n                  selectText: '选择',\n                  navigateText: '切换',\n                },\n              },\n            },\n          },\n          en: {\n            translations: {\n              button: {\n                buttonText: 'Search',\n                buttonAriaLabel: 'Search docs',\n              },\n              modal: {\n                noResultsText: 'No results found',\n                resetButtonTitle: 'Clear query',\n                footer: {\n                  selectText: 'to select',\n                  navigateText: 'to navigate',\n                },\n              },\n            },\n          },\n        },\n      },\n    },\n\n    outline: {\n      level: [2, 3],\n      label: '目录',\n    },\n  },\n})\n"
  },
  {
    "path": "docs/.vitepress/theme/custom.css",
    "content": "\n        :root {\n            /* --- 核心色板：清晨薄雾 (Teal -> Sky) --- */\n            --vp-c-brand-1: #14b8a6; /* Teal 500 */\n            --vp-c-brand-2: #0ea5e9; /* Sky 500 */\n            --vp-c-brand-3: #0f766e; /* Teal 700 */\n            --vp-c-brand-soft: rgba(20, 184, 166, 0.14);\n\n            /* --- 背景色：极简与通透 --- */\n            --vp-c-bg: #ffffff;\n            --vp-c-bg-alt: #f8fafc; /* Slate 50 */\n            --vp-c-bg-elv: #ffffff;\n            \n            /* --- 文字颜色：柔和的深灰，而非纯黑 --- */\n            --vp-c-text-1: #334155; /* Slate 700 */\n            --vp-c-text-2: #64748b; /* Slate 500 */\n            --vp-c-text-3: #94a3b8; /* Slate 400 */\n\n            /* --- 边框与分隔 --- */\n            --vp-c-border: #e2e8f0;\n            --vp-c-divider: #f1f5f9;\n\n            /* --- 阴影：扩散柔光 --- */\n            --vp-shadow-1: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);\n            --vp-shadow-2: 0 10px 15px -3px rgba(0, 0, 0, 0.05), 0 4px 6px -2px rgba(0, 0, 0, 0.025);\n            --vp-shadow-brand: 0 4px 14px 0 rgba(20, 184, 166, 0.3);\n\n            /* --- 布局参数 --- */\n            --vp-nav-height: 64px;\n            --vp-sidebar-width: 280px;\n            --vp-radius-default: 12px;\n            --vp-radius-small: 6px;\n        }\n\n        /* * ==========================================\n         * 暗黑模式重构 (Deep Ocean Theme)\n         * ==========================================\n         */\n        .dark {\n            /* 背景：更深邃的蓝黑色，更有沉浸感 */\n            --vp-c-bg: #020617; /* Slate 950 */\n            --vp-c-bg-alt: #0f172a; /* Slate 900 */\n            --vp-c-bg-elv: #1e293b; /* Slate 800 */\n            \n            /* 文字：降低纯白刺眼感，提高舒适度 */\n            --vp-c-text-1: #f8fafc; /* Slate 50 */\n            --vp-c-text-2: #94a3b8; /* Slate 400 */\n            --vp-c-text-3: #64748b; /* Slate 500 */\n\n            /* 边框：更细腻的深色线条 */\n            --vp-c-border: #1e293b;\n            --vp-c-divider: #0f172a;\n\n            /* 品牌色：调亮，产生荧光感 */\n            --vp-c-brand-1: #2dd4bf; /* Teal 400 */\n            --vp-c-brand-2: #38bdf8; /* Sky 400 */\n            --vp-c-brand-3: #14b8a6; /* Teal 500 */\n            --vp-c-brand-soft: rgba(45, 212, 191, 0.15); /* 淡淡的荧光底色 */\n\n            /* 阴影：暗色环境下的微光 */\n            --vp-shadow-1: 0 4px 6px -1px rgba(0, 0, 0, 0.5);\n            --vp-shadow-2: 0 10px 15px -3px rgba(0, 0, 0, 0.5);\n            --vp-shadow-brand: 0 0 20px rgba(45, 212, 191, 0.3); /* 品牌色发光 */\n        }\n\n        /* 全局基础设置 */\n        body {\n            font-family: 'Noto Sans SC', sans-serif;\n            background-color: var(--vp-c-bg-alt);\n            color: var(--vp-c-text-1);\n            margin: 0;\n            transition: background-color 0.3s, color 0.3s;\n            overflow-x: hidden;\n        }\n\n        a { text-decoration: none; color: inherit; transition: color 0.2s; }\n\n        /* -------------------------------------------\n           布局结构 (模拟 VitePress 布局)\n           ------------------------------------------- */\n        .app-container {\n            display: flex;\n            flex-direction: column;\n            min-height: 100vh;\n        }\n\n        /* 1. 导航栏：毛玻璃 + 悬浮感 */\n        .VPNavBar {\n            position: fixed;\n            top: 0;\n            left: 0;\n            right: 0;\n            height: var(--vp-nav-height);\n            background-color: rgba(255, 255, 255, 0.85); /* 稍微增加不透明度 */\n            backdrop-filter: blur(20px) saturate(180%);\n            -webkit-backdrop-filter: blur(20px) saturate(180%);\n            border-bottom: 1px solid rgba(226, 232, 240, 0.6);\n            z-index: 50;\n            display: flex;\n            align-items: center;\n            padding: 0 24px;\n            justify-content: space-between;\n        }\n        .dark .VPNavBar {\n            background-color: rgba(2, 6, 23, 0.85); /* 深色背景半透 */\n            border-bottom: 1px solid rgba(30, 41, 59, 0.7);\n        }\n\n        .nav-logo {\n            font-weight: 700;\n            font-size: 1.2rem;\n            background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        .nav-links {\n            display: flex;\n            gap: 24px;\n            font-size: 0.9rem;\n            font-weight: 500;\n            color: var(--vp-c-text-2);\n        }\n\n        .nav-link:hover {\n            color: var(--vp-c-brand-1);\n        }\n        \n        .nav-link.active {\n            color: var(--vp-c-brand-1);\n            position: relative;\n        }\n        .nav-link.active::after {\n            content: '';\n            position: absolute;\n            bottom: -22px;\n            left: 0;\n            width: 100%;\n            height: 2px;\n            background: var(--vp-c-brand-1);\n            border-radius: 2px 2px 0 0;\n            box-shadow: 0 -2px 10px var(--vp-c-brand-1); /* 激活状态发光 */\n        }\n\n        /* 2. 侧边栏：极简卡片式 */\n        .VPSidebar {\n            position: fixed;\n            top: var(--vp-nav-height);\n            left: 0;\n            bottom: 0;\n            width: var(--vp-sidebar-width);\n            background-color: var(--vp-c-bg); /* 实色背景 */\n            border-right: 1px solid var(--vp-c-border);\n            padding: 32px 24px;\n            overflow-y: auto;\n            transform: translateX(0);\n            transition: transform 0.3s ease;\n            z-index: 40;\n        }\n        .dark .VPSidebar {\n            background-color: var(--vp-c-bg-alt); /* 侧边栏比内容区稍微亮一点点/或者暗一点 */\n        }\n        \n        @media (max-width: 960px) {\n            .VPSidebar { transform: translateX(-100%); }\n            .VPSidebar.open { transform: translateX(0); }\n        }\n\n        .sidebar-group-title {\n            font-weight: 700;\n            color: var(--vp-c-text-1);\n            margin-bottom: 12px;\n            margin-top: 24px;\n            font-size: 1rem;\n        }\n        .sidebar-group-title:first-child { margin-top: 0; }\n\n        .sidebar-link {\n            display: block;\n            padding: 8px 12px;\n            margin: 4px 0;\n            border-radius: var(--vp-radius-small);\n            color: var(--vp-c-text-2);\n            font-size: 0.95rem;\n            transition: all 0.2s;\n        }\n\n        .sidebar-link:hover {\n            background-color: var(--vp-c-bg-alt);\n            color: var(--vp-c-brand-1);\n        }\n        .dark .sidebar-link:hover {\n            background-color: var(--vp-c-bg-elv);\n        }\n\n        .sidebar-link.active {\n            background-color: var(--vp-c-brand-soft);\n            color: var(--vp-c-brand-3);\n            font-weight: 600;\n        }\n        .dark .sidebar-link.active {\n            color: var(--vp-c-brand-1);\n            background-color: rgba(45, 212, 191, 0.1); /* 暗色模式下更通透 */\n            box-shadow: inset 2px 0 0 0 var(--vp-c-brand-1); /* 左侧高亮条 */\n        }\n\n        /* 3. 主内容区：居中与留白 */\n        .VPContent {\n            margin-top: var(--vp-nav-height);\n            margin-left: var(--vp-sidebar-width);\n            padding: 48px 64px;\n            min-height: calc(100vh - var(--vp-nav-height));\n            background-color: var(--vp-c-bg);\n            transition: margin-left 0.3s ease;\n        }\n        \n        @media (max-width: 960px) {\n            .VPContent { margin-left: 0; padding: 32px 24px; }\n        }\n\n        /* 内容排版 (Typography) */\n        .vp-doc h1 {\n            font-size: 2.5rem;\n            font-weight: 800;\n            margin-bottom: 1.5rem;\n            line-height: 1.2;\n            background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n            display: inline-block;\n            /* 暗色模式下加一点文字阴影增强可读性 */\n            filter: drop-shadow(0 0 2em rgba(20, 184, 166, 0.2)); \n        }\n        .dark .vp-doc h1 {\n             filter: drop-shadow(0 0 25px rgba(45, 212, 191, 0.2));\n        }\n\n        .vp-doc h2 {\n            font-size: 1.8rem;\n            margin-top: 3rem;\n            margin-bottom: 1rem;\n            padding-bottom: 0.5rem;\n            border-bottom: 1px solid var(--vp-c-border);\n            color: var(--vp-c-text-1);\n        }\n        \n        .vp-doc h3 {\n            font-size: 1.4rem;\n            margin-top: 2rem;\n            margin-bottom: 0.8rem;\n            color: var(--vp-c-text-1);\n        }\n\n        .vp-doc p {\n            line-height: 1.8;\n            margin-bottom: 1.2rem;\n            color: var(--vp-c-text-2);\n            font-size: 1.05rem;\n        }\n\n        /* 代码块美化 */\n        .vp-doc pre {\n            background-color: #1e293b; /* Dark Slate */\n            border-radius: var(--vp-radius-default);\n            padding: 20px;\n            overflow-x: auto;\n            color: #f8fafc;\n            margin: 24px 0;\n            border: 1px solid rgba(0,0,0,0.1);\n            box-shadow: var(--vp-shadow-1);\n        }\n        .dark .vp-doc pre {\n            background-color: #0f172a; /* 更深一层的背景 */\n            border: 1px solid #1e293b;\n        }\n        \n        .vp-doc code {\n            font-family: 'Menlo', 'Monaco', 'Courier New', monospace;\n            font-size: 0.9em;\n        }\n        .vp-doc p code {\n            background-color: var(--vp-c-brand-soft);\n            color: var(--vp-c-brand-3);\n            padding: 2px 6px;\n            border-radius: 4px;\n            font-size: 0.9em;\n        }\n        .dark .vp-doc p code { \n            color: var(--vp-c-brand-1); \n            background-color: rgba(45, 212, 191, 0.1);\n        }\n\n        /* 引用块 (Blockquote) */\n        .vp-doc blockquote {\n            margin: 1.5rem 0;\n            padding: 16px 24px;\n            background-color: var(--vp-c-bg-alt);\n            border-left: 4px solid var(--vp-c-brand-1);\n            border-radius: 0 var(--vp-radius-default) var(--vp-radius-default) 0;\n            color: var(--vp-c-text-2);\n        }\n        .dark .vp-doc blockquote {\n            background-color: rgba(30, 41, 59, 0.5);\n        }\n\n        /* 按钮与卡片 (Features) */\n        .vp-button {\n            display: inline-block;\n            padding: 0 24px;\n            height: 48px;\n            line-height: 48px;\n            border-radius: 24px; /* Pill shape */\n            font-weight: 600;\n            font-size: 1rem;\n            transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);\n            cursor: pointer;\n            border: none;\n        }\n\n        .vp-button.brand {\n            background: linear-gradient(135deg, var(--vp-c-brand-1), var(--vp-c-brand-2));\n            color: white;\n            box-shadow: var(--vp-shadow-brand);\n        }\n        .dark .vp-button.brand {\n            color: #020617; /* 暗色模式下按钮文字用深色，因为背景亮 */\n        }\n\n        .vp-button.brand:hover {\n            transform: translateY(-2px);\n            box-shadow: 0 8px 25px -4px rgba(20, 184, 166, 0.5);\n        }\n        .dark .vp-button.brand:hover {\n             box-shadow: 0 0 30px -4px rgba(45, 212, 191, 0.6); /* 发光增强 */\n        }\n\n        .vp-button.alt {\n            background-color: var(--vp-c-bg-alt);\n            color: var(--vp-c-text-1);\n            border: 1px solid var(--vp-c-border);\n        }\n        .dark .vp-button.alt {\n            background-color: transparent;\n            border-color: var(--vp-c-text-2);\n            color: var(--vp-c-text-1);\n        }\n        \n        .vp-button.alt:hover {\n            border-color: var(--vp-c-brand-1);\n            color: var(--vp-c-brand-1);\n        }\n\n        /* 工具类 */\n        .theme-toggle {\n            background: none;\n            border: none;\n            cursor: pointer;\n            font-size: 1.5rem;\n            color: var(--vp-c-text-2);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            padding: 8px;\n            border-radius: 50%;\n            transition: background 0.2s;\n        }\n        .theme-toggle:hover {\n            background-color: var(--vp-c-bg-alt);\n            color: var(--vp-c-brand-1);\n        }\n        .dark .theme-toggle:hover {\n            background-color: var(--vp-c-bg-elv);\n        }\n        \n        .mobile-menu-btn {\n            display: none;\n            background: none;\n            border: none;\n            font-size: 1.5rem;\n            color: var(--vp-c-text-1);\n            cursor: pointer;\n        }\n        \n        @media (max-width: 960px) {\n            .mobile-menu-btn { display: block; }\n            .nav-links { display: none; }\n        }\n        \n        /* 首页 Hero 样式模拟 */\n        .hero-section {\n            text-align: center;\n            padding: 80px 20px;\n            max-width: 800px;\n            margin: 0 auto;\n        }\n        .hero-title {\n            font-size: 3.5rem;\n            font-weight: 900;\n            line-height: 1.1;\n            margin-bottom: 24px;\n        }\n        .hero-title span {\n            background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));\n            -webkit-background-clip: text;\n            -webkit-text-fill-color: transparent;\n        }\n        .hero-desc {\n            font-size: 1.5rem;\n            color: var(--vp-c-text-2);\n            margin-bottom: 40px;\n        }\n        .hero-actions {\n            display: flex;\n            gap: 16px;\n            justify-content: center;\n        }\n        \n        /* Features Grid */\n        .features-grid {\n            display: grid;\n            grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n            gap: 24px;\n            margin-top: 64px;\n        }\n        .feature-card {\n            background-color: var(--vp-c-bg-alt);\n            padding: 24px;\n            border-radius: var(--vp-radius-default);\n            transition: transform 0.3s, box-shadow 0.3s;\n            border: 1px solid transparent;\n        }\n        .dark .feature-card {\n            background-color: rgba(30, 41, 59, 0.3); /* 微微透明 */\n            border: 1px solid rgba(255,255,255,0.05);\n        }\n        \n        .feature-card:hover {\n            background-color: var(--vp-c-bg);\n            transform: translateY(-4px);\n            box-shadow: var(--vp-shadow-2);\n            border-color: var(--vp-c-brand-soft);\n        }\n        .dark .feature-card:hover {\n            background-color: var(--vp-c-bg-elv);\n            border-color: var(--vp-c-brand-1);\n            box-shadow: 0 0 15px rgba(45, 212, 191, 0.1);\n        }\n        \n        .feature-icon {\n            font-size: 2rem;\n            margin-bottom: 16px;\n            color: var(--vp-c-brand-1);\n            background-color: var(--vp-c-brand-soft);\n            width: 48px;\n            height: 48px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            border-radius: 12px;\n        }\n        .feature-title {\n            font-weight: 700;\n            margin-bottom: 8px;\n            color: var(--vp-c-text-1);\n        }\n        .feature-details {\n            color: var(--vp-c-text-2);\n            font-size: 0.95rem;\n            line-height: 1.6;\n        }\n"
  },
  {
    "path": "docs/.vitepress/theme/index.ts",
    "content": "import DefaultTheme from 'vitepress/theme'\nimport './custom.css'\n\nexport default {\n  ...DefaultTheme,\n}\n"
  },
  {
    "path": "docs/api/index.md",
    "content": "# FileCodeBox API 文档\n\n## API 版本: 2.1.0\n\n## 目录\n- [认证](#认证)\n- [分享接口](#分享接口)\n- [管理接口](#管理接口)\n\n## 认证\n\n部分接口需要在请求头中携带 `Authorization` 进行认证：\n\n```\nAuthorization: Bearer <token>\n```\n\n### 获取 Token\n\n当管理面板关闭了游客上传（`openUpload=0`）时，需要先登录获取 token：\n\n```bash\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n```\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"token\": \"xxx.xxx.xxx\",\n    \"token_type\": \"Bearer\"\n  }\n}\n```\n\n## 分享接口\n\n### 分享文本\n\n**POST** `/share/text/`\n\n分享文本内容，获取分享码。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 默认值 | 描述 |\n|-------|------|------|--------|------|\n| text | string | 是 | - | 要分享的文本内容 |\n| expire_value | integer | 否 | 1 | 过期时间值 |\n| expire_style | string | 否 | \"day\" | 过期时间单位(day/hour/minute/count/forever) |\n\n**cURL 示例：**\n\n```bash\n# 游客上传（openUpload=1 时）\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=这是要分享的文本内容\" \\\n  -F \"expire_value=1\" \\\n  -F \"expire_style=day\"\n\n# 需要认证时（openUpload=0 时）\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"text=这是要分享的文本内容\"\n```\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\"\n  }\n}\n```\n\n### 分享文件\n\n**POST** `/share/file/`\n\n上传并分享文件，获取分享码。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 默认值 | 描述 |\n|-------|------|------|--------|------|\n| file | file | 是 | - | 要上传的文件 |\n| expire_value | integer | 否 | 1 | 过期时间值 |\n| expire_style | string | 否 | \"day\" | 过期时间单位(day/hour/minute/count/forever) |\n\n**cURL 示例：**\n\n```bash\n# 上传文件（默认1天有效期）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\"\n\n# 上传文件（7天有效期）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=7\" \\\n  -F \"expire_style=day\"\n\n# 上传文件（可下载10次）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n\n# 需要认证时\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"file=@/path/to/file.txt\"\n```\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\"\n  }\n}\n```\n\n### 获取文件信息\n\n**GET** `/share/select/`\n\n通过分享码获取文件信息（直接下载文件）。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 描述 |\n|-------|------|------|------|\n| code | string | 是 | 文件分享码 |\n\n**cURL 示例：**\n\n```bash\n# 通过取件码下载文件\ncurl -L \"http://localhost:12345/share/select/?code=abc123\" -o downloaded_file\n```\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\",\n    \"size\": 1024,\n    \"text\": \"文件内容或下载链接\"\n  }\n}\n```\n\n### 选择文件\n\n**POST** `/share/select/`\n\n通过分享码选择文件。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 描述 |\n|-------|------|------|------|\n| code | string | 是 | 文件分享码 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\",\n    \"size\": 1024,\n    \"text\": \"文件内容或下载链接\"\n  }\n}\n```\n\n### 下载文件\n\n**GET** `/share/download`\n\n下载分享的文件。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 描述 |\n|-------|------|------|------|\n| key | string | 是 | 下载密钥 |\n| code | string | 是 | 文件分享码 |\n\n## 管理接口\n\n### 管理员登录\n\n**POST** `/admin/login`\n\n管理员登录获取token。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 描述 |\n|-------|------|------|------|\n| password | string | 是 | 管理员密码 |\n\n### 仪表盘数据\n\n**GET** `/admin/dashboard`\n\n获取系统仪表盘数据。\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"totalFiles\": 100,\n    \"storageUsed\": \"1.5GB\",\n    \"sysUptime\": \"10天\",\n    \"yesterdayCount\": 50,\n    \"yesterdaySize\": \"500MB\",\n    \"todayCount\": 30,\n    \"todaySize\": \"300MB\"\n  }\n}\n```\n\n### 文件列表\n\n**GET** `/admin/file/list`\n\n获取系统中的文件列表。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 默认值 | 描述 |\n|-------|------|------|--------|------|\n| page | integer | 否 | 1 | 当前页码 |\n| size | integer | 否 | 10 | 每页数量 |\n| keyword | string | 否 | \"\" | 搜索关键词 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"page\": 1,\n    \"size\": 10,\n    \"total\": 100,\n    \"data\": [\n      {\n        \"id\": 1,\n        \"name\": \"example.txt\",\n        \"size\": 1024,\n        \"created_at\": \"2024-01-01 12:00:00\"\n      }\n    ]\n  }\n}\n```\n\n### 删除文件\n\n**DELETE** `/admin/file/delete`\n\n删除系统中的文件。\n\n**请求参数：**\n\n| 参数名 | 类型 | 必填 | 描述 |\n|-------|------|------|------|\n| id | integer | 是 | 文件ID |\n\n### 获取配置\n\n**GET** `/admin/config/get`\n\n获取系统配置信息。\n\n### 更新配置\n\n**PATCH** `/admin/config/update`\n\n更新系统配置信息。\n\n## 错误响应\n\n当发生错误时，API会返回对应的错误信息：\n\n```json\n{\n  \"code\": 422,\n  \"detail\": [\n    {\n      \"loc\": [\"body\", \"password\"],\n      \"msg\": \"密码不能为空\",\n      \"type\": \"value_error\"\n    }\n  ]\n}\n```\n\n## 状态码说明\n\n- 200: 请求成功\n- 401: 未授权\n- 403: 禁止访问\n- 404: 资源不存在\n- 422: 请求参数验证错误\n- 500: 服务器内部错误 "
  },
  {
    "path": "docs/api/presign-upload.md",
    "content": "# 预签名上传 API 文档\n\n## 概述\n\n预签名上传功能提供统一的文件上传接口，根据后端存储类型自动选择最优上传方式：\n\n- **S3 存储**: 返回预签名 URL，客户端直传 S3（减少服务器带宽压力）\n- **其他存储**: 返回代理上传 URL，通过服务器中转上传\n\n## 上传流程\n\n### 流程图\n\n```\n┌─────────┐     1. 初始化上传      ┌─────────┐\n│  客户端  │ ──────────────────────▶ │  服务器  │\n└─────────┘                        └─────────┘\n     │                                  │\n     │◀─────── 返回 upload_url + mode ──┤\n     │                                  │\n     │  ┌─────────────────────────────────────────┐\n     │  │ if mode == \"direct\" (S3存储)            │\n     │  │   2a. PUT 文件到 upload_url (S3)        │\n     │  │   3a. POST /confirm 确认上传            │\n     │  │                                         │\n     │  │ if mode == \"proxy\" (其他存储)           │\n     │  │   2b. PUT 文件到 upload_url (服务器)    │\n     │  │   (自动返回分享码，无需确认)            │\n     │  └─────────────────────────────────────────┘\n     │\n     ▼\n  获取分享码\n```\n\n---\n\n## API 端点\n\n### 1. 初始化上传\n\n初始化预签名上传会话，获取上传 URL 和模式。\n\n**请求**\n\n```\nPOST /presign/upload/init\nContent-Type: application/json\n```\n\n**请求体**\n\n| 字段         | 类型    | 必填 | 默认值 | 说明                                    |\n| ------------ | ------- | ---- | ------ | --------------------------------------- |\n| file_name    | string  | ✅   | -      | 文件名（含扩展名）                      |\n| file_size    | integer | ✅   | -      | 文件大小（字节）                        |\n| expire_value | integer | ❌   | 1      | 过期时间值                              |\n| expire_style | string  | ❌   | \"day\"  | 过期类型：day/hour/minute/forever/count |\n\n**请求示例**\n\n```json\n{\n  \"file_name\": \"document.pdf\",\n  \"file_size\": 1048576,\n  \"expire_value\": 7,\n  \"expire_style\": \"day\"\n}\n```\n\n**响应**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"upload_id\": \"a1b2c3d4e5f6...\",\n    \"upload_url\": \"https://bucket.s3.amazonaws.com/path?X-Amz-Signature=...\",\n    \"mode\": \"direct\",\n    \"save_path\": \"share/data/2024/01/01/uuid/document.pdf\",\n    \"expires_in\": 900\n  }\n}\n```\n\n**响应字段说明**\n\n| 字段       | 类型    | 说明                                                  |\n| ---------- | ------- | ----------------------------------------------------- |\n| upload_id  | string  | 上传会话 ID，后续操作需要                             |\n| upload_url | string  | 上传目标 URL                                          |\n| mode       | string  | 上传模式：`direct`（直传 S3）或 `proxy`（服务器代理） |\n| save_path  | string  | 文件存储路径                                          |\n| expires_in | integer | URL 有效期（秒），默认 900 秒（15 分钟）              |\n\n**错误响应**\n\n| 状态码 | 说明                           |\n| ------ | ------------------------------ |\n| 400    | 过期时间类型错误               |\n| 403    | 文件大小超过限制 / IP 频率限制 |\n\n---\n\n### 2a. 直传模式 - 上传文件到 S3\n\n当 `mode == \"direct\"` 时，客户端直接将文件 PUT 到返回的预签名 URL。\n\n**请求**\n\n```\nPUT {upload_url}\nContent-Type: application/octet-stream\n\n[文件二进制内容]\n```\n\n**注意事项**\n\n- 直接使用返回的 `upload_url`，不要修改\n- Content-Type 建议使用 `application/octet-stream`\n- 这是直接请求 S3，不经过服务器\n\n**JavaScript 示例**\n\n```javascript\nconst response = await fetch(uploadUrl, {\n  method: 'PUT',\n  body: file,\n  headers: {\n    'Content-Type': 'application/octet-stream',\n  },\n})\n\nif (response.ok) {\n  // 上传成功，调用确认接口\n}\n```\n\n---\n\n### 2b. 代理模式 - 上传文件到服务器\n\n当 `mode == \"proxy\"` 时，客户端将文件 PUT 到服务器代理端点。\n\n**请求**\n\n```\nPUT /presign/upload/proxy/{upload_id}\nContent-Type: multipart/form-data\n\nfile: [文件]\n```\n\n**路径参数**\n\n| 参数      | 说明                      |\n| --------- | ------------------------- |\n| upload_id | 初始化时返回的上传会话 ID |\n\n**响应**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"document.pdf\"\n  }\n}\n```\n\n**注意**: 代理模式上传成功后直接返回分享码，无需调用确认接口。\n\n**错误响应**\n\n| 状态码 | 说明                                    |\n| ------ | --------------------------------------- |\n| 400    | 文件大小与声明不符 / 会话不支持代理上传 |\n| 404    | 上传会话不存在或已过期                  |\n| 500    | 文件保存失败                            |\n\n---\n\n### 3. 确认上传（仅直传模式）\n\n直传模式下，客户端完成 S3 上传后调用此接口确认并获取分享码。\n\n**请求**\n\n```\nPOST /presign/upload/confirm/{upload_id}\nContent-Type: application/json\n```\n\n**路径参数**\n\n| 参数      | 说明                      |\n| --------- | ------------------------- |\n| upload_id | 初始化时返回的上传会话 ID |\n\n**请求体**\n\n| 字段         | 类型    | 必填 | 默认值 | 说明       |\n| ------------ | ------- | ---- | ------ | ---------- |\n| expire_value | integer | ❌   | 1      | 过期时间值 |\n| expire_style | string  | ❌   | \"day\"  | 过期类型   |\n\n**请求示例**\n\n```json\n{\n  \"expire_value\": 7,\n  \"expire_style\": \"day\"\n}\n```\n\n**响应**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"document.pdf\"\n  }\n}\n```\n\n**错误响应**\n\n| 状态码 | 说明                                          |\n| ------ | --------------------------------------------- |\n| 400    | 会话不支持直传确认                            |\n| 404    | 上传会话不存在或已过期 / 文件未上传或上传失败 |\n\n---\n\n### 4. 查询上传状态\n\n查询上传会话的当前状态。\n\n**请求**\n\n```\nGET /presign/upload/status/{upload_id}\n```\n\n**响应**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"upload_id\": \"a1b2c3d4e5f6...\",\n    \"file_name\": \"document.pdf\",\n    \"file_size\": 1048576,\n    \"mode\": \"direct\",\n    \"created_at\": \"2024-01-01T12:00:00\",\n    \"expires_at\": \"2024-01-01T12:15:00\",\n    \"is_expired\": false\n  }\n}\n```\n\n**错误响应**\n\n| 状态码 | 说明           |\n| ------ | -------------- |\n| 404    | 上传会话不存在 |\n\n---\n\n### 5. 取消上传\n\n取消上传会话并清理相关资源。\n\n**请求**\n\n```\nDELETE /presign/upload/{upload_id}\n```\n\n**响应**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"message\": \"上传会话已取消\"\n  }\n}\n```\n\n**错误响应**\n\n| 状态码 | 说明           |\n| ------ | -------------- |\n| 404    | 上传会话不存在 |\n\n---\n\n## 前端集成示例\n\n### 完整上传流程（JavaScript/TypeScript）\n\n```typescript\ninterface PresignInitResponse {\n  upload_id: string\n  upload_url: string\n  mode: 'direct' | 'proxy'\n  save_path: string\n  expires_in: number\n}\n\ninterface UploadResult {\n  code: string\n  name: string\n}\n\nasync function uploadFile(\n  file: File,\n  expireValue: number = 1,\n  expireStyle: string = 'day'\n): Promise<UploadResult> {\n  // 1. 初始化上传\n  const initResponse = await fetch('/presign/upload/init', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      file_name: file.name,\n      file_size: file.size,\n      expire_value: expireValue,\n      expire_style: expireStyle,\n    }),\n  })\n\n  const initData = await initResponse.json()\n  if (initData.code !== 200) {\n    throw new Error(initData.detail)\n  }\n\n  const { upload_id, upload_url, mode } = initData.detail as PresignInitResponse\n\n  // 2. 根据模式上传文件\n  if (mode === 'direct') {\n    // 直传模式：上传到S3\n    const uploadResponse = await fetch(upload_url, {\n      method: 'PUT',\n      body: file,\n      headers: { 'Content-Type': 'application/octet-stream' },\n    })\n\n    if (!uploadResponse.ok) {\n      throw new Error('S3上传失败')\n    }\n\n    // 3. 确认上传\n    const confirmResponse = await fetch(\n      `/presign/upload/confirm/${upload_id}`,\n      {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({\n          expire_value: expireValue,\n          expire_style: expireStyle,\n        }),\n      }\n    )\n\n    const confirmData = await confirmResponse.json()\n    if (confirmData.code !== 200) {\n      throw new Error(confirmData.detail)\n    }\n\n    return confirmData.detail\n  } else {\n    // 代理模式：上传到服务器\n    const formData = new FormData()\n    formData.append('file', file)\n\n    const uploadResponse = await fetch(upload_url, {\n      method: 'PUT',\n      body: formData,\n    })\n\n    const uploadData = await uploadResponse.json()\n    if (uploadData.code !== 200) {\n      throw new Error(uploadData.detail)\n    }\n\n    return uploadData.detail\n  }\n}\n\n// 使用示例\nconst file = document.querySelector('input[type=\"file\"]').files[0]\nconst result = await uploadFile(file, 7, 'day')\nconsole.log('分享码:', result.code)\n```\n\n### Vue 3 组件示例\n\n```vue\n<template>\n  <div>\n    <input type=\"file\" @change=\"handleFileSelect\" />\n    <button @click=\"upload\" :disabled=\"!selectedFile || uploading\">\n      {{ uploading ? '上传中...' : '上传' }}\n    </button>\n    <div v-if=\"shareCode\">分享码: {{ shareCode }}</div>\n    <div v-if=\"error\" class=\"error\">{{ error }}</div>\n  </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue'\n\nconst selectedFile = ref<File | null>(null)\nconst uploading = ref(false)\nconst shareCode = ref('')\nconst error = ref('')\n\nfunction handleFileSelect(e: Event) {\n  const input = e.target as HTMLInputElement\n  selectedFile.value = input.files?.[0] || null\n}\n\nasync function upload() {\n  if (!selectedFile.value) return\n\n  uploading.value = true\n  error.value = ''\n\n  try {\n    const result = await uploadFile(selectedFile.value)\n    shareCode.value = result.code\n  } catch (e) {\n    error.value = e.message\n  } finally {\n    uploading.value = false\n  }\n}\n</script>\n```\n\n---\n\n## 注意事项\n\n1. **会话有效期**: 上传会话默认 15 分钟后过期，请在有效期内完成上传\n2. **文件大小限制**: 受系统配置 `uploadSize` 限制\n3. **过期类型**: 支持 `day`、`hour`、`minute`、`forever`、`count`\n4. **CORS**: 直传模式下，S3 需要配置正确的 CORS 策略\n5. **重试机制**: 建议实现上传失败重试逻辑\n"
  },
  {
    "path": "docs/changelog.md",
    "content": ""
  },
  {
    "path": "docs/contributing.md",
    "content": ""
  },
  {
    "path": "docs/en/api/index.md",
    "content": "# FileCodeBox API Documentation\n\n## API Version: 2.1.0\n\n## Table of Contents\n- [Authentication](#authentication)\n- [Share API](#share-api)\n- [Admin API](#admin-api)\n\n## Authentication\n\nSome APIs require `Authorization` header for authentication:\n\n```\nAuthorization: Bearer <token>\n```\n\n### Get Token\n\nWhen guest upload is disabled in admin panel (`openUpload=0`), you need to login first:\n\n```bash\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n```\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"token\": \"xxx.xxx.xxx\",\n    \"token_type\": \"Bearer\"\n  }\n}\n```\n\n## Share API\n\n### Share Text\n\n**POST** `/share/text/`\n\nShare text content and get a share code.\n\n**Parameters:**\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| text | string | Yes | - | Text content to share |\n| expire_value | integer | No | 1 | Expiration time value |\n| expire_style | string | No | \"day\" | Expiration time unit(day/hour/minute/count/forever) |\n\n**cURL Example:**\n\n```bash\n# Guest upload (when openUpload=1)\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=This is the text content to share\" \\\n  -F \"expire_value=1\" \\\n  -F \"expire_style=day\"\n\n# When authentication required (openUpload=0)\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"text=This is the text content to share\"\n```\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\"\n  }\n}\n```\n\n### Share File\n\n**POST** `/share/file/`\n\nUpload and share a file, get a share code.\n\n**Parameters:**\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| file | file | Yes | - | File to upload |\n| expire_value | integer | No | 1 | Expiration time value |\n| expire_style | string | No | \"day\" | Expiration time unit(day/hour/minute/count/forever) |\n\n**cURL Example:**\n\n```bash\n# Upload file (default 1 day expiration)\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\"\n\n# Upload file (7 days expiration)\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=7\" \\\n  -F \"expire_style=day\"\n\n# Upload file (10 downloads limit)\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n\n# When authentication required\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"file=@/path/to/file.txt\"\n```\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\"\n  }\n}\n```\n\n### Get File Info\n\n**GET** `/share/select/`\n\nGet file information by share code (direct file download).\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| code | string | Yes | File share code |\n\n**cURL Example:**\n\n```bash\n# Download file by extraction code\ncurl -L \"http://localhost:12345/share/select/?code=abc123\" -o downloaded_file\n```\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\",\n    \"size\": 1024,\n    \"text\": \"File content or download link\"\n  }\n}\n```\n\n### Select File\n\n**POST** `/share/select/`\n\nSelect file by share code.\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| code | string | Yes | File share code |\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abc123\",\n    \"name\": \"example.txt\",\n    \"size\": 1024,\n    \"text\": \"File content or download link\"\n  }\n}\n```\n\n### Download File\n\n**GET** `/share/download`\n\nDownload shared file.\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| key | string | Yes | Download key |\n| code | string | Yes | File share code |\n\n## Admin API\n\n### Admin Login\n\n**POST** `/admin/login`\n\nAdmin login to get token.\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| password | string | Yes | Admin password |\n\n### Dashboard Data\n\n**GET** `/admin/dashboard`\n\nGet system dashboard data.\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"totalFiles\": 100,\n    \"storageUsed\": \"1.5GB\",\n    \"sysUptime\": \"10 days\",\n    \"yesterdayCount\": 50,\n    \"yesterdaySize\": \"500MB\",\n    \"todayCount\": 30,\n    \"todaySize\": \"300MB\"\n  }\n}\n```\n\n### File List\n\n**GET** `/admin/file/list`\n\nGet system file list.\n\n**Parameters:**\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| page | integer | No | 1 | Current page |\n| size | integer | No | 10 | Page size |\n| keyword | string | No | \"\" | Search keyword |\n\n**Response Example:**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"page\": 1,\n    \"size\": 10,\n    \"total\": 100,\n    \"data\": [\n      {\n        \"id\": 1,\n        \"name\": \"example.txt\",\n        \"size\": 1024,\n        \"created_at\": \"2024-01-01 12:00:00\"\n      }\n    ]\n  }\n}\n```\n\n### Delete File\n\n**DELETE** `/admin/file/delete`\n\nDelete file from system.\n\n**Parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| id | integer | Yes | File ID |\n\n### Get Config\n\n**GET** `/admin/config/get`\n\nGet system configuration.\n\n### Update Config\n\n**PATCH** `/admin/config/update`\n\nUpdate system configuration.\n\n## Error Response\n\nWhen an error occurs, the API will return corresponding error message:\n\n```json\n{\n  \"code\": 422,\n  \"detail\": [\n    {\n      \"loc\": [\"body\", \"password\"],\n      \"msg\": \"Password cannot be empty\",\n      \"type\": \"value_error\"\n    }\n  ]\n}\n```\n\n## Status Codes\n\n- 200: Success\n- 401: Unauthorized\n- 403: Forbidden\n- 404: Not Found\n- 422: Validation Error\n- 500: Internal Server Error "
  },
  {
    "path": "docs/en/changelog.md",
    "content": ""
  },
  {
    "path": "docs/en/contributing.md",
    "content": ""
  },
  {
    "path": "docs/en/guide/configuration.md",
    "content": "# Configuration Guide\n\nFileCodeBox provides rich configuration options that can be customized through the admin panel or by directly modifying the configuration. This document details all available configuration options.\n\n## Configuration Methods\n\nFileCodeBox supports two configuration methods:\n\n1. **Admin Panel Configuration** (Recommended): Access `/admin` to enter the admin panel and modify settings on the settings page\n2. **Database Configuration**: Configuration is stored in the `data/filecodebox.db` database\n\n::: tip Note\nOn first startup, the system uses default configuration from `core/settings.py`. Modified configurations are saved to the database.\n:::\n\n## Basic Settings\n\n### Site Information\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `name` | string | `文件快递柜 - FileCodeBox` | Site name, displayed in page title and navigation bar |\n| `description` | string | `开箱即用的文件快传系统` | Site description, used for SEO |\n| `keywords` | string | `FileCodeBox, 文件快递柜...` | Site keywords, used for SEO |\n| `port` | int | `12345` | Service listening port |\n\n### Notification Settings\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `notify_title` | string | `系统通知` | Notification title |\n| `notify_content` | string | Welcome message | Notification content, supports HTML |\n| `page_explain` | string | Legal disclaimer | Footer explanation text |\n| `robotsText` | string | `User-agent: *\\nDisallow: /` | robots.txt content |\n\n## Upload Settings\n\n### File Upload Limits\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `openUpload` | int | `1` | Enable upload functionality (1=enabled, 0=disabled) |\n| `uploadSize` | int | `10485760` | Maximum single file upload size (bytes), default 10MB |\n| `enableChunk` | int | `0` | Enable chunked upload (1=enabled, 0=disabled) |\n\n::: warning Note\n`uploadSize` is in bytes. 10MB = 10 * 1024 * 1024 = 10485760 bytes\n:::\n\n### Upload Rate Limiting\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `uploadMinute` | int | `1` | Upload limit time window (minutes) |\n| `uploadCount` | int | `10` | Maximum uploads allowed within the time window |\n\nExample: Default configuration allows up to 10 uploads per minute.\n\n\n### File Expiration Settings\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `expireStyle` | list | `[\"day\",\"hour\",\"minute\",\"forever\",\"count\"]` | Available expiration methods |\n| `max_save_seconds` | int | `0` | Maximum file retention time (seconds), 0 means no limit |\n\nExpiration methods explained:\n- `day` - Expire by days\n- `hour` - Expire by hours\n- `minute` - Expire by minutes\n- `forever` - Never expire\n- `count` - Expire by download count\n\n## Theme Settings\n\n### Theme Selection\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `themesSelect` | string | `themes/2024` | Currently active theme |\n| `themesChoices` | list | See below | Available themes list |\n\nDefault available themes:\n```json\n[\n  {\n    \"name\": \"2023\",\n    \"key\": \"themes/2023\",\n    \"author\": \"Lan\",\n    \"version\": \"1.0\"\n  },\n  {\n    \"name\": \"2024\",\n    \"key\": \"themes/2024\",\n    \"author\": \"Lan\",\n    \"version\": \"1.0\"\n  }\n]\n```\n\n### Interface Style\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `opacity` | float | `0.9` | Interface opacity (0-1) |\n| `background` | string | `\"\"` | Custom background image URL, empty uses default background |\n\n## Admin Settings\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `admin_token` | string | `FileCodeBox2023` | Admin login password |\n| `showAdminAddr` | int | `0` | Show admin panel entry on homepage (1=show, 0=hide) |\n\n::: danger Security Warning\nAlways change the default `admin_token` in production environments! Using the default password poses serious security risks.\n:::\n\n## Security Settings\n\n### Error Rate Limiting\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `errorMinute` | int | `1` | Error limit time window (minutes) |\n| `errorCount` | int | `1` | Maximum errors allowed within the time window |\n\nThis setting prevents brute-force attacks on extraction codes.\n\n## Storage Settings\n\n### Storage Type\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `file_storage` | string | `local` | Storage backend type |\n| `storage_path` | string | `\"\"` | Custom storage path |\n\nSupported storage types:\n- `local` - Local storage\n- `s3` - S3-compatible storage (AWS S3, Aliyun OSS, MinIO, etc.)\n- `onedrive` - OneDrive storage\n- `webdav` - WebDAV storage\n- `opendal` - OpenDAL storage\n\nFor detailed storage configuration, see [Storage Configuration](/en/guide/storage).\n\n## Configuration Examples\n\n### Example 1: Small Personal Use\n\nSuitable for personal or small team use with relaxed limits:\n\n```python\n{\n    \"name\": \"My File Share\",\n    \"uploadSize\": 52428800,        # 50MB\n    \"uploadMinute\": 5,             # 5 minutes\n    \"uploadCount\": 20,             # Max 20 uploads\n    \"expireStyle\": [\"day\", \"hour\", \"forever\"],\n    \"admin_token\": \"your-secure-password\",\n    \"showAdminAddr\": 1\n}\n```\n\n### Example 2: Public Service\n\nSuitable for public services requiring stricter limits:\n\n```python\n{\n    \"name\": \"Public File Box\",\n    \"uploadSize\": 10485760,        # 10MB\n    \"uploadMinute\": 1,             # 1 minute\n    \"uploadCount\": 5,              # Max 5 uploads\n    \"errorMinute\": 5,              # 5 minutes\n    \"errorCount\": 3,               # Max 3 errors\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400,     # Max retention 1 day\n    \"admin_token\": \"very-secure-password-123\",\n    \"showAdminAddr\": 0\n}\n```\n\n### Example 3: Enterprise Internal Use\n\nSuitable for enterprise internal use with large file and chunked upload support:\n\n```python\n{\n    \"name\": \"Enterprise File Transfer\",\n    \"uploadSize\": 1073741824,      # 1GB\n    \"enableChunk\": 1,              # Enable chunked upload\n    \"uploadMinute\": 10,            # 10 minutes\n    \"uploadCount\": 100,            # Max 100 uploads\n    \"expireStyle\": [\"day\", \"forever\"],\n    \"file_storage\": \"s3\",          # Use S3 storage\n    \"admin_token\": \"enterprise-secure-token\",\n    \"showAdminAddr\": 1\n}\n```\n\n## Next Steps\n\n- [Storage Configuration](/en/guide/storage) - Learn how to configure different storage backends\n- [Security Settings](/en/guide/security) - Learn how to enhance system security\n- [File Sharing](/en/guide/share) - Learn about file sharing features\n"
  },
  {
    "path": "docs/en/guide/getting-started.md",
    "content": "# Getting Started\n\n## Introduction\n\nFileCodeBox is a simple and efficient file sharing tool that supports temporary file transfer, sharing, and management. This guide will help you quickly deploy and use FileCodeBox.\n\n## Features\n\n- 🚀 Quick Deployment: Support Docker one-click deployment\n- 🔒 Secure & Reliable: File access requires extraction code\n- ⏱️ Time Control: Support setting file expiration time\n- 📊 Download Limit: Can limit file download times\n- 🖼️ File Preview: Support preview of images, videos, audio, and other formats\n- 📱 Responsive Design: Perfect adaptation for mobile and desktop\n\n## Deployment Methods\n\n### Docker Deployment\n\n```bash\ndocker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta\n```\n\n### Manual Deployment\n\n1. Clone the repository\n```bash\ngit clone https://github.com/vastsa/FileCodeBox.git\n```\n\n2. Install dependencies\n```bash\ncd FileCodeBox\npip install -r requirements.txt\n```\n\n3. Start the service\n```bash\npython main.py\n```\n\n## Usage\n\n1. Access the System\n   Open browser and visit `http://localhost:12345`\n\n2. Upload Files\n   - Click upload button or drag files to upload area\n   - Set file expiration time and download limit\n   - Get share link and extraction code\n\n3. Download Files\n   - Visit share link\n   - Enter extraction code\n   - Download file\n\n4. Admin Panel\n   - Visit `http://localhost:12345/#/admin`\n   - Enter admin password: `FileCodeBox2023`\n   - Enter admin panel\n   - View system information, file list, user management, etc.\n\n## Next Steps\n\n- [Storage Configuration](/en/guide/storage) - Learn how to configure different storage methods\n- [Security Settings](/en/guide/security) - Learn how to enhance system security\n- [API Documentation](/en/api/) - Learn how to integrate through API "
  },
  {
    "path": "docs/en/guide/introduction.md",
    "content": "<div align=\"center\">\n\n<img src=\"https://fastly.jsdelivr.net/gh/vastsa/FileCodeBox@V1.6/static/banners/img_1.png\" alt=\"FileCodeBox Logo\">\n\n<p><em>Share text and files anonymously with a passcode, like picking up a package</em></p>\n\n[![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/network)\n[![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/issues)\n[![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)\n[![QQ Group](https://img.shields.io/badge/QQ%20Group-739673698-blue.svg)](https://qm.qq.com/q/PemPzhdEIM)\n[![Python Version](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-green.svg)](https://fastapi.tiangolo.com)\n[![Vue Version](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg)](https://v3.vuejs.org)\n\n</div>\n\n## 📝 Introduction\n\nFileCodeBox is a lightweight file sharing tool developed with FastAPI + Vue3. It allows users to share text and files easily, where recipients only need a passcode to retrieve the files, just like picking up a package from a delivery locker.\n\n## 🖼️ Preview\n\n<div align=\"center\">\n<h3>\n<a href=\"https://github.com/vastsa/FileCodeBoxFronted\" target=\"_blank\">\n<img src=\"https://img.shields.io/badge/Frontend-FileCodeBoxFronted-blue?style=for-the-badge&logo=github\" alt=\"Frontend Repository\">\n</a>\n&nbsp;&nbsp;&nbsp;\n<a href=\"https://share.lanol.cn\" target=\"_blank\">\n<img src=\"https://img.shields.io/badge/Demo-share.lanol.cn-green?style=for-the-badge&logo=internet-explorer\" alt=\"Demo Site\">\n</a>\n</h3>\n</div>\n\n\n## 🎯 Use Cases\n\n<table>\n<tr>\n<td align=\"center\">\n<h4>📁 Temporary File Sharing</h4>\nQuick file sharing without registration\n</td>\n<td align=\"center\">\n<h4>📝 Quick Text Sharing</h4>\nShare code snippets and text content\n</td>\n<td align=\"center\">\n<h4>🕶️ Anonymous Transfer</h4>\nPrivacy-protected file transfer\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>💾 Temporary Storage</h4>\nFile storage with expiration time\n</td>\n<td align=\"center\">\n<h4>🔄 Cross-platform Transfer</h4>\nQuick file transfer between devices\n</td>\n<td align=\"center\">\n<h4>🌐 Private Share Service</h4>\nBuild your own file sharing service\n</td>\n</tr>\n</table>\n\n## ✨ Core Features\n\n<table>\n<tr>\n<td align=\"center\">\n<h4>🚀 Lightweight</h4>\nBased on FastAPI + SQLite3 + Vue3 + ElementUI\n</td>\n<td align=\"center\">\n<h4>📤 Easy Upload</h4>\nSupport copy-paste and drag-drop\n</td>\n<td align=\"center\">\n<h4>📦 Multiple Types</h4>\nSupport text and various file types\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>🔒 Security</h4>\n\n- IP upload limits\n- Error attempt limits\n- File expiration\n</td>\n<td align=\"center\">\n<h4>🎫 Passcode Sharing</h4>\nRandom codes with customizable limits\n</td>\n<td align=\"center\">\n<h4>🌍 Multi-language</h4>\nSupport for Simplified Chinese, Traditional Chinese, and English\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>🎭 Anonymous</h4>\nNo registration required\n</td>\n<td align=\"center\">\n<h4>🛠 Admin Panel</h4>\nFile and system management\n</td>\n<td align=\"center\">\n<h4>🐳 Docker</h4>\nOne-click deployment\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>💾 Storage Options</h4>\nLocal, S3, OneDrive support\n</td>\n<td align=\"center\">\n<h4>📱 Responsive</h4>\nMobile-friendly design\n</td>\n<td align=\"center\">\n<h4>💻 CLI Support</h4>\nCommand-line download\n</td>\n</tr>\n</table>\n\n## 🚀 Quick Start\n\n### Docker Deployment\n\n```bash\ndocker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta\n```\n\n### Manual Deployment\n\n1. Clone the repository\n```bash\ngit clone https://github.com/vastsa/FileCodeBox.git\n```\n\n2. Install dependencies\n```bash\ncd FileCodeBox\npip install -r requirements.txt\n```\n\n3. Start the service\n```bash\npython main.py\n```\n\n## 📖 Usage Guide\n\n### Share Files\n1. Open the website, click \"Share File\"\n2. Select or drag files\n3. Set expiration time and count\n4. Get the passcode\n\n### Retrieve Files\n1. Open the website, enter passcode\n2. Click retrieve\n3. Download file or view text\n\n### Admin Panel\n1. Visit `/admin`\n2. Enter admin password\n3. Manage files and settings\n\n## 🛠 Development Guide\n\n### Project Structure\n```\nFileCodeBox/\n├── apps/           # Application code\n│   ├── admin/     # Admin backend\n│   └── base/      # Base functions\n├── core/          # Core functions\n├── data/          # Data directory\n└── fcb-fronted/   # Frontend code\n```\n\n### Development Environment\n- Python 3.8+\n- Node.js 14+\n- Vue 3\n- FastAPI\n\n### Local Development\n1. Backend development\n```bash\npython main.py\n```\n\n2. Frontend development\n```bash\ncd fcb-fronted\nnpm install\nnpm run dev\n```\n\n## 🤝 Contributing\n\n1. Fork the project\n2. Create your feature branch `git checkout -b feature/xxx`\n3. Commit your changes `git commit -m 'Add xxx'`\n4. Push to the branch `git push origin feature/xxx`\n5. Open a Pull Request\n\n## ❓ FAQ\n\n### Q: How to modify upload size limit?\nA: Change `uploadSize` in admin panel\n\n### Q: How to configure storage engine?\nA: Select storage engine and configure parameters in admin panel\n\n### Q: How to backup data?\nA: Backup the `data` directory\n\nFor more questions, visit [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题)\n\n## 😀 Project Statistics and Analytics\n\n<div align=\"center\">\n<a href=\"https://hellogithub.com/repository/75ad7ffedd404a6485b4d621ec5b47e6\" target=\"_blank\"><img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=75ad7ffedd404a6485b4d621ec5b47e6&claim_uid=beSz6INEkCM4mDH\" alt=\"Featured｜HelloGitHub\" style=\"width: 200px; height: 45px;\" width=\"200\" height=\"45\" /></a>\n\n![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg)\n\n[![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date)\n</div>\n\n## 📜 Disclaimer\n\nThis project is open-source for learning purposes only. It should not be used for any illegal purposes. The author is not responsible for any consequences. Please retain the project address and copyright information when using it."
  },
  {
    "path": "docs/en/guide/management.md",
    "content": "# Admin Panel\n\nFileCodeBox provides a fully-featured admin panel that allows administrators to conveniently manage files, view system status, and modify configurations. This document introduces the various features and usage of the admin panel.\n\n## Accessing the Admin Panel\n\n### Login Method\n\nThe admin panel is located at the `/admin` path. Access method:\n\n1. Visit `http://your-domain.com/admin` in your browser\n2. Enter the admin password (the value of the `admin_token` configuration)\n3. Click the login button\n\n::: tip Tip\nThe default admin password is `FileCodeBox2023`. Be sure to change this password in production environments. See [Security Settings](/en/guide/security) for details.\n:::\n\n### Show Admin Entry\n\nBy default, the admin panel entry is not shown on the homepage. You can control whether to show it via configuration:\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `showAdminAddr` | int | `0` | Show admin entry on homepage (1=show, 0=hide) |\n\n::: warning Security Recommendation\nFor public services, it's recommended to keep `showAdminAddr` at `0` and access the admin panel directly via the `/admin` path to reduce the risk of malicious scanning.\n:::\n\n### Authentication Mechanism\n\nThe admin panel uses JWT (JSON Web Token) for authentication:\n\n1. After successful login, the server returns a Token containing admin identity\n2. Subsequent requests carry the Token via `Authorization: Bearer <token>` header\n3. Token is used to verify admin identity, ensuring only authorized users can access admin functions\n\n## Dashboard\n\nAfter logging in, you first see the dashboard page, which displays the overall system status.\n\n### Statistics\n\nThe dashboard displays the following key metrics:\n\n| Metric | Description |\n|--------|-------------|\n| **Total Files** (`totalFiles`) | Total number of files stored in the system |\n| **Storage Used** (`storageUsed`) | Total storage space occupied by all files (bytes) |\n| **System Uptime** (`sysUptime`) | Time when the system was first started |\n| **Yesterday's Uploads** (`yesterdayCount`) | Number of files uploaded yesterday |\n| **Yesterday's Upload Size** (`yesterdaySize`) | Total size of files uploaded yesterday (bytes) |\n| **Today's Uploads** (`todayCount`) | Number of files uploaded today so far |\n| **Today's Upload Size** (`todaySize`) | Total size of files uploaded today (bytes) |\n\n### Metric Notes\n\n- **Total Files**: Includes all unexpired files and text shares\n- **Storage Used**: Shows actual storage space occupied by files, excluding database and other system files\n- **Yesterday/Today Statistics**: Calculated based on file creation time, useful for understanding system usage trends\n\n::: tip Tip\nStorage usage is displayed in bytes. For example, `10485760` represents approximately 10MB.\n:::\n\n## File Management\n\n### File List\n\nThe file management page displays all shared files in the system, supporting pagination and search.\n\n**List information includes:**\n- File ID\n- Extraction code (code)\n- Filename prefix (prefix)\n- File extension (suffix)\n- File size\n- Creation time\n- Expiration time\n- Remaining download count\n\n### Search Files\n\nUse the search function to quickly find specific files:\n\n1. Enter keywords in the search box\n2. System performs fuzzy matching based on filename prefix (prefix)\n3. Search results update in real-time\n\n**Search examples:**\n- Enter `report` to find all files with \"report\" in the filename\n- Enter `.pdf` to find all PDF files (if the filename contains this string)\n\n### Pagination\n\nThe file list supports paginated display:\n\n| Parameter | Default | Description |\n|-----------|---------|-------------|\n| `page` | `1` | Current page number |\n| `size` | `10` | Items per page |\n\n\n### Delete Files\n\nAdministrators can delete any file:\n\n1. Find the file to delete in the file list\n2. Click the delete button\n3. Confirm the delete operation\n\n::: danger Warning\nDelete operations are irreversible! Files will be permanently deleted from the storage backend, and database records will also be removed.\n:::\n\n**Delete process:**\n1. System first deletes the actual file from the storage backend (local/S3/OneDrive, etc.)\n2. Then deletes the file record from the database\n3. After deletion, the corresponding extraction code becomes invalid\n\n### Download Files\n\nAdministrators can directly download any file:\n\n1. Find the target file in the file list\n2. Click the download button\n3. File will be downloaded via browser\n\nFor text shares, the system returns text content directly instead of downloading a file.\n\n### Modify File Information\n\nAdministrators can modify some information of shared files:\n\n| Modifiable Field | Description |\n|------------------|-------------|\n| `code` | Extraction code (must be unique, cannot duplicate other files) |\n| `prefix` | Filename prefix |\n| `suffix` | File extension |\n| `expired_at` | Expiration time |\n| `expired_count` | Remaining download count |\n\n**Modify extraction code:**\n```\nOriginal code: abc123\nNew code: myfile2024\n```\n\n::: warning Note\nWhen modifying extraction codes, the system checks if the new code is already in use. If an identical extraction code exists, the modification will fail.\n:::\n\n## Local File Management\n\nIn addition to managing shared files, the admin panel also provides local file management functionality for managing files in the `data/local` directory.\n\n### View Local Files\n\nThe local file list displays all files in the `data/local` directory:\n\n| Information | Description |\n|-------------|-------------|\n| Filename | Complete filename |\n| Creation Time | File creation time |\n| File Size | File size (bytes) |\n\n### Share Local Files\n\nYou can quickly share local files:\n\n1. Select the file to share in the local file list\n2. Set expiration method and value\n3. Click the share button\n4. System generates extraction code\n\n**Share parameters:**\n\n| Parameter | Description |\n|-----------|-------------|\n| `filename` | Filename to share |\n| `expire_style` | Expiration method (day/hour/minute/forever/count) |\n| `expire_value` | Expiration value (days/hours/minutes/download count) |\n\n### Delete Local Files\n\nYou can delete files in the `data/local` directory:\n\n1. Find the file to delete in the local file list\n2. Click the delete button\n3. Confirm deletion\n\n::: tip Use Cases\nLocal file management is useful for:\n- Sharing files after batch uploading to the server\n- Managing files uploaded to the server through other means\n- Cleaning up unnecessary local files\n:::\n\n## System Settings\n\n### View Configuration\n\nOn the system settings page, you can view the current values of all configuration items. Configuration items are grouped by category:\n\n- Basic settings (site name, description, etc.)\n- Upload settings (file size limits, rate limits, etc.)\n- Storage settings (storage type, path, etc.)\n- Theme settings (theme selection, opacity, etc.)\n- Security settings (admin password, error limits, etc.)\n\n### Modify Configuration\n\nAdministrators can modify most configurations through the admin panel:\n\n1. Go to the system settings page\n2. Find the configuration item to modify\n3. Enter the new value\n4. Click the save button\n\n**Modifiable configuration items:**\n\n| Category | Example Settings |\n|----------|------------------|\n| Basic Settings | `name`, `description`, `keywords`, `notify_title`, `notify_content` |\n| Upload Settings | `uploadSize`, `uploadMinute`, `uploadCount`, `openUpload`, `enableChunk` |\n| Expiration Settings | `expireStyle`, `max_save_seconds` |\n| Theme Settings | `themesSelect`, `opacity`, `background` |\n| Security Settings | `admin_token`, `showAdminAddr`, `errorMinute`, `errorCount` |\n| Storage Settings | `file_storage`, `storage_path` and storage backend-specific configurations |\n\n::: warning Note\n- `admin_token` (admin password) cannot be set to empty\n- `themesChoices` (theme list) cannot be modified through the admin panel\n- After modifying storage settings, existing files will not be automatically migrated\n:::\n\n### Configuration Effect\n\nConfiguration changes take effect immediately without restarting the service. Configurations are saved in the database and persist after restart.\n\n**Configuration storage location:**\n- Database: `data/filecodebox.db`\n- Table name: `keyvalue`\n- Key name: `settings`\n\n## API Endpoints\n\nAll admin panel functions are implemented through REST APIs. Here are the main endpoints:\n\n### Authentication Endpoint\n\n**Login**\n```\nPOST /admin/login\nContent-Type: application/json\n\n{\n    \"password\": \"your-admin-password\"\n}\n```\n\nResponse:\n```json\n{\n    \"code\": 200,\n    \"detail\": {\n        \"token\": \"eyJhbGciOiJIUzI1NiIs...\",\n        \"token_type\": \"Bearer\"\n    }\n}\n```\n\n### Dashboard Endpoint\n\n**Get Statistics**\n```\nGET /admin/dashboard\nAuthorization: Bearer <token>\n```\n\n### File Management Endpoints\n\n**Get File List**\n```\nGET /admin/file/list?page=1&size=10&keyword=\nAuthorization: Bearer <token>\n```\n\n**Delete File**\n```\nDELETE /admin/file/delete\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"id\": 123\n}\n```\n\n**Download File**\n```\nGET /admin/file/download?id=123\nAuthorization: Bearer <token>\n```\n\n**Modify File Information**\n```\nPATCH /admin/file/update\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"id\": 123,\n    \"code\": \"newcode\",\n    \"expired_at\": \"2024-12-31T23:59:59\"\n}\n```\n\n### Local File Endpoints\n\n**Get Local File List**\n```\nGET /admin/local/lists\nAuthorization: Bearer <token>\n```\n\n**Delete Local File**\n```\nDELETE /admin/local/delete\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"filename\": \"example.txt\"\n}\n```\n\n**Share Local File**\n```\nPOST /admin/local/share\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"filename\": \"example.txt\",\n    \"expire_style\": \"day\",\n    \"expire_value\": 7\n}\n```\n\n### Configuration Endpoints\n\n**Get Configuration**\n```\nGET /admin/config/get\nAuthorization: Bearer <token>\n```\n\n**Update Configuration**\n```\nPATCH /admin/config/update\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"admin_token\": \"new-password\",\n    \"uploadSize\": 52428800\n}\n```\n\n## Common Issues\n\n### Forgot Admin Password\n\nIf you forgot the admin password, you can reset it through the following methods:\n\n1. Stop the FileCodeBox service\n2. Open `data/filecodebox.db` using an SQLite tool\n3. Query the record with `key='settings'` in the `keyvalue` table\n4. Modify the `admin_token` value in the JSON\n5. Restart the service\n\n```sql\n-- View current configuration\nSELECT * FROM keyvalue WHERE key = 'settings';\n\n-- Or delete configuration to restore default password\nDELETE FROM keyvalue WHERE key = 'settings';\n```\n\n### File Deletion Failed\n\nIf an error occurs when deleting files, possible reasons:\n\n1. **Storage backend connection failed**: Check if storage configuration is correct\n2. **File no longer exists**: File may have been manually deleted\n3. **Insufficient permissions**: Check write permissions for storage directory\n\n### Configuration Changes Not Taking Effect\n\nIf configuration changes don't take effect:\n\n1. Check if you clicked the save button\n2. Refresh the page to see if configuration was saved\n3. Check browser console for error messages\n4. Confirm configuration value format is correct (e.g., don't enter strings for numeric types)\n\n## Next Steps\n\n- [Configuration Guide](/en/guide/configuration) - Learn detailed descriptions of all configuration options\n- [Security Settings](/en/guide/security) - Learn how to enhance system security\n- [Storage Configuration](/en/guide/storage) - Configure different storage backends\n"
  },
  {
    "path": "docs/en/guide/security.md",
    "content": "# Security Settings\n\nFileCodeBox provides multiple layers of security mechanisms to protect your file sharing service. This document explains how to properly configure security options to ensure secure system operation.\n\n## Admin Password\n\n### Change Default Password\n\n::: danger Important Security Warning\nFileCodeBox's default admin password is `FileCodeBox2023`. **You must change this password immediately in production environments!** Using the default password allows anyone to access your admin panel.\n:::\n\nThere are two ways to change the admin password:\n\n**Method 1: Via Admin Panel (Recommended)**\n\n1. Access `/admin` to enter the admin panel\n2. Log in with the current password\n3. Go to the \"System Settings\" page\n4. Find the `admin_token` configuration item\n5. Enter a new secure password and save\n\n**Method 2: Via Database**\n\nConfiguration is stored in the `keyvalue` table of the `data/filecodebox.db` database. You can directly modify the `admin_token` value.\n\n### Password Security Recommendations\n\n- Use a strong password with at least 16 characters\n- Include uppercase and lowercase letters, numbers, and special characters\n- Avoid common words or personal information\n- Change password regularly\n\n```python\n# Recommended password format example\n\"admin_token\": \"Xk9#mP2$vL5@nQ8&wR3\"\n```\n\n### Hide Admin Entry\n\nBy default, the admin panel entry is hidden. You can control whether to show the admin entry on the homepage via the `showAdminAddr` configuration:\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `showAdminAddr` | int | `0` | Show admin entry (1=show, 0=hide) |\n\n::: tip Recommendation\nFor public services, it's recommended to keep `showAdminAddr` at `0` and access the admin panel directly via the `/admin` path.\n:::\n\n## IP Rate Limiting\n\nFileCodeBox has built-in IP-based rate limiting mechanisms to effectively prevent abuse and attacks.\n\n### Upload Rate Limiting\n\nLimit the number of uploads from a single IP within a specified time:\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `uploadMinute` | int | `1` | Upload limit time window (minutes) |\n| `uploadCount` | int | `10` | Maximum uploads allowed within the time window |\n\n**How it works:**\n- System records upload requests from each IP\n- When an IP's upload count reaches `uploadCount` within `uploadMinute` minutes\n- Subsequent upload requests from that IP will be rejected with HTTP 423 error\n- Counter resets after the time window expires\n\n**Configuration examples:**\n\n```python\n# Relaxed configuration: Max 20 uploads in 5 minutes\n{\n    \"uploadMinute\": 5,\n    \"uploadCount\": 20\n}\n\n# Strict configuration: Max 3 uploads in 1 minute\n{\n    \"uploadMinute\": 1,\n    \"uploadCount\": 3\n}\n```\n\n\n### Error Rate Limiting\n\nLimit the number of error attempts from a single IP to prevent brute-force attacks on extraction codes:\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `errorMinute` | int | `1` | Error limit time window (minutes) |\n| `errorCount` | int | `1` | Maximum errors allowed within the time window |\n\n**How it works:**\n- When a user enters an incorrect extraction code, the system records the error count for that IP\n- When error count reaches `errorCount`, that IP will be temporarily locked\n- Lock duration is `errorMinute` minutes\n- During lockout, all extraction requests from that IP will be rejected\n\n**Configuration example:**\n\n```python\n# Anti-brute-force configuration: Max 3 errors in 5 minutes\n{\n    \"errorMinute\": 5,\n    \"errorCount\": 3\n}\n```\n\n::: warning Note\nThe default configuration `errorMinute=1, errorCount=1` is very strict, meaning you need to wait 1 minute after entering one incorrect extraction code before retrying. Adjust this configuration based on actual needs.\n:::\n\n## Upload Restrictions\n\n### File Size Limit\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `uploadSize` | int | `10485760` | Maximum single file upload size (bytes), default 10MB |\n| `openUpload` | int | `1` | Enable upload functionality (1=enabled, 0=disabled) |\n\n**Common size conversions:**\n- 10MB = 10 * 1024 * 1024 = `10485760`\n- 50MB = 50 * 1024 * 1024 = `52428800`\n- 100MB = 100 * 1024 * 1024 = `104857600`\n- 1GB = 1024 * 1024 * 1024 = `1073741824`\n\n### File Expiration Settings\n\nThrough file expiration mechanisms, you can automatically clean up expired files, reducing storage usage and security risks:\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `expireStyle` | list | `[\"day\",\"hour\",\"minute\",\"forever\",\"count\"]` | Available expiration methods |\n| `max_save_seconds` | int | `0` | Maximum file retention time (seconds), 0 means no limit |\n\n**Expiration methods explained:**\n- `day` - Expire by days\n- `hour` - Expire by hours\n- `minute` - Expire by minutes\n- `forever` - Never expire (requires alphanumeric extraction code)\n- `count` - Expire by download count\n\n**Security recommendations:**\n\nFor public services, it's recommended to:\n1. Remove the `forever` option to avoid permanent file storage\n2. Set `max_save_seconds` to limit maximum retention time\n3. Prefer using `count` method for automatic deletion after download\n\n```python\n# Recommended configuration for public services\n{\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400  # Max retention 1 day\n}\n```\n\n### Disable Upload Functionality\n\nIn some cases, you may need to temporarily disable upload functionality:\n\n```python\n{\n    \"openUpload\": 0  # Disable upload functionality\n}\n```\n\n## Reverse Proxy Security Configuration\n\nIn production environments, Nginx or other reverse proxy servers are typically used. Here are security configuration recommendations:\n\n### Nginx Configuration Example\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain.com;\n    \n    # Force HTTPS redirect\n    return 301 https://$server_name$request_uri;\n}\n\nserver {\n    listen 443 ssl http2;\n    server_name your-domain.com;\n    \n    # SSL certificate configuration\n    ssl_certificate /path/to/cert.pem;\n    ssl_certificate_key /path/to/key.pem;\n    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;\n    ssl_prefer_server_ciphers on;\n    \n    # Security headers\n    add_header X-Frame-Options \"SAMEORIGIN\" always;\n    add_header X-Content-Type-Options \"nosniff\" always;\n    add_header X-XSS-Protection \"1; mode=block\" always;\n    add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;\n    \n    # Limit request body size (match uploadSize configuration)\n    client_max_body_size 100M;\n    \n    # Pass real IP\n    location / {\n        proxy_pass http://127.0.0.1:12345;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n    \n    # Static resource caching\n    location /assets {\n        proxy_pass http://127.0.0.1:12345;\n        proxy_cache_valid 200 7d;\n        add_header Cache-Control \"public, max-age=604800\";\n    }\n}\n```\n\n### Key Security Configuration Notes\n\n**1. Pass Real IP**\n\nFileCodeBox's IP limiting functionality depends on obtaining the client's real IP. The system obtains IP in the following order:\n1. `X-Real-IP` request header\n2. `X-Forwarded-For` request header\n3. Direct client connection IP\n\nEnsure the reverse proxy correctly sets these headers:\n\n```nginx\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n```\n\n**2. Request Body Size Limit**\n\nNginx's `client_max_body_size` should match or be slightly larger than FileCodeBox's `uploadSize` configuration:\n\n```nginx\nclient_max_body_size 100M;  # Allow max 100MB uploads\n```\n\n**3. HTTPS Encryption**\n\nIt's strongly recommended to enable HTTPS in production environments:\n- Protect uploaded file content\n- Protect admin login credentials\n- Prevent man-in-the-middle attacks\n\n### Caddy Configuration Example\n\n```nginx\nyour-domain.com {\n    reverse_proxy localhost:12345\n    \n    header {\n        X-Frame-Options \"SAMEORIGIN\"\n        X-Content-Type-Options \"nosniff\"\n        X-XSS-Protection \"1; mode=block\"\n        Strict-Transport-Security \"max-age=31536000; includeSubDomains\"\n    }\n}\n```\n\n## Security Checklist\n\nBefore deploying FileCodeBox, confirm the following security configurations:\n\n- [ ] Changed default admin password `admin_token`\n- [ ] Hidden admin entry `showAdminAddr: 0`\n- [ ] Configured appropriate upload rate limiting\n- [ ] Configured error rate limiting to prevent brute-force attacks\n- [ ] Set reasonable file size limits\n- [ ] Configured file expiration policy\n- [ ] Enabled HTTPS encryption\n- [ ] Reverse proxy correctly passes real IP\n- [ ] Set security response headers\n\n## Recommended Security Configurations\n\n### Public Service Configuration\n\n```python\n{\n    \"admin_token\": \"your-very-secure-password\",\n    \"showAdminAddr\": 0,\n    \"uploadSize\": 10485760,           # 10MB\n    \"uploadMinute\": 1,\n    \"uploadCount\": 5,\n    \"errorMinute\": 5,\n    \"errorCount\": 3,\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400,        # Max 1 day\n    \"openUpload\": 1\n}\n```\n\n### Internal Service Configuration\n\n```python\n{\n    \"admin_token\": \"internal-secure-password\",\n    \"showAdminAddr\": 1,\n    \"uploadSize\": 104857600,          # 100MB\n    \"uploadMinute\": 5,\n    \"uploadCount\": 50,\n    \"errorMinute\": 1,\n    \"errorCount\": 5,\n    \"expireStyle\": [\"day\", \"hour\", \"forever\"],\n    \"max_save_seconds\": 0,            # No limit\n    \"openUpload\": 1\n}\n```\n\n## Next Steps\n\n- [Configuration Guide](/en/guide/configuration) - Learn about all configuration options\n- [Storage Configuration](/en/guide/storage) - Configure secure storage backends\n- [File Sharing](/en/guide/share) - Learn about file sharing features\n"
  },
  {
    "path": "docs/en/guide/share.md",
    "content": "# File Sharing\n\nFileCodeBox provides simple and easy-to-use file and text sharing functionality. Users can securely share and retrieve files using extraction codes.\n\n## Sharing Methods\n\nFileCodeBox supports two sharing methods:\n\n1. **Text Sharing** - Share text content directly, suitable for code snippets, configuration files, etc.\n2. **File Sharing** - Upload files for sharing, supports various file formats\n\n## Text Sharing\n\n### How to Use\n\n1. Select the \"Text Share\" tab on the homepage\n2. Enter or paste the content to share in the text box\n3. Select expiration method and time\n4. Click the \"Share\" button\n5. Get the extraction code\n\n### Text Size Limit\n\n::: warning Note\nThe maximum content size for text sharing is **222KB** (227,328 bytes). If content exceeds this limit, it's recommended to use file sharing instead.\n:::\n\nText content size is calculated using UTF-8 encoding. Chinese characters typically occupy 3 bytes.\n\n### API Endpoint\n\n**POST** `/share/text/`\n\nRequest parameters:\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `text` | string | Yes | Text content to share |\n| `expire_value` | int | No | Expiration value, default 1 |\n| `expire_style` | string | No | Expiration method, default `day` |\n\nResponse example:\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\"\n  }\n}\n```\n\n## File Sharing\n\n### How to Use\n\n1. Select the \"File Share\" tab on the homepage\n2. Click the upload area or drag files to the upload area\n3. Select expiration method and time\n4. Click the \"Upload\" button\n5. Get the extraction code\n\n### File Size Limit\n\nThe default maximum single file upload size is **10MB**. Administrators can modify this limit via the `uploadSize` configuration.\n\n::: tip Tip\nIf you need to upload large files, contact the administrator to enable chunked upload functionality or adjust the `uploadSize` configuration.\n:::\n\n### Supported Upload Methods\n\n- **Click Upload** - Click the upload area to select files\n- **Drag Upload** - Drag files to the upload area\n- **Paste Upload** - Paste images from clipboard (supported by some themes)\n\n### API Endpoint\n\n**POST** `/share/file/`\n\nRequest parameters (multipart/form-data):\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `file` | file | Yes | File to upload |\n| `expire_value` | int | No | Expiration value, default 1 |\n| `expire_style` | string | No | Expiration method, default `day` |\n\nResponse example:\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"654321\",\n    \"name\": \"example.pdf\"\n  }\n}\n```\n\n\n## Expiration Settings\n\nFileCodeBox supports multiple flexible expiration methods:\n\n| Expiration Method | Parameter Value | Description |\n|-------------------|-----------------|-------------|\n| By Days | `day` | File expires after specified days |\n| By Hours | `hour` | File expires after specified hours |\n| By Minutes | `minute` | File expires after specified minutes |\n| Never Expire | `forever` | File is permanently valid |\n| By Count | `count` | File expires after specified download count |\n\n::: info Note\n- Administrators can control available expiration methods via the `expireStyle` configuration\n- Administrators can limit maximum file retention time via the `max_save_seconds` configuration\n:::\n\n### Expiration Method Examples\n\n```bash\n# File expires after 3 days\nexpire_value=3, expire_style=day\n\n# File expires after 12 hours\nexpire_value=12, expire_style=hour\n\n# File expires after 30 minutes\nexpire_value=30, expire_style=minute\n\n# File never expires\nexpire_value=1, expire_style=forever\n\n# File expires after 5 downloads\nexpire_value=5, expire_style=count\n```\n\n## Retrieving Files\n\n### How to Use\n\n1. Enter the extraction code in the \"Retrieve File\" area on the homepage\n2. Click the \"Retrieve\" button\n3. System displays file information (filename, size, etc.)\n4. Click the \"Download\" button to download the file, or view text content directly\n\n### Extraction Code Notes\n\n- Extraction codes are typically **6-digit numbers**\n- Files that never expire use **alphanumeric** extraction codes\n- Extraction codes are case-sensitive (for alphanumeric codes)\n\n### API Endpoints\n\n**Query File Information**\n\n**POST** `/share/select/`\n\nRequest parameters:\n\n```json\n{\n  \"code\": \"123456\"\n}\n```\n\nResponse example (file):\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"example.pdf\",\n    \"size\": 1048576,\n    \"text\": \"https://example.com/download/...\"\n  }\n}\n```\n\nResponse example (text):\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"Text\",\n    \"size\": 1024,\n    \"text\": \"This is the shared text content...\"\n  }\n}\n```\n\n**Direct File Download**\n\n**GET** `/share/select/?code=123456`\n\nThis endpoint returns file content directly, suitable for direct browser access.\n\n## Chunked Upload (Large Files)\n\nFor large file uploads, FileCodeBox supports chunked upload functionality. This feature requires administrator enablement (`enableChunk=1`).\n\n### Chunked Upload Flow\n\n```mermaid\nsequenceDiagram\n    participant C as Client\n    participant S as Server\n    \n    C->>S: 1. Initialize upload (POST /chunk/upload/init/)\n    S-->>C: Return upload_id and chunk info\n    \n    loop Each chunk\n        C->>S: 2. Upload chunk (POST /chunk/upload/chunk/{upload_id}/{chunk_index})\n        S-->>C: Return chunk hash\n    end\n    \n    C->>S: 3. Complete upload (POST /chunk/upload/complete/{upload_id})\n    S-->>C: Return extraction code\n```\n\n### 1. Initialize Upload\n\n**POST** `/chunk/upload/init/`\n\nRequest parameters:\n\n```json\n{\n  \"file_name\": \"large_file.zip\",\n  \"file_size\": 104857600,\n  \"chunk_size\": 5242880,\n  \"file_hash\": \"sha256_hash_of_file\"\n}\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `file_name` | string | Yes | Filename |\n| `file_size` | int | Yes | Total file size (bytes) |\n| `chunk_size` | int | No | Chunk size, default 5MB |\n| `file_hash` | string | Yes | SHA256 hash of the file |\n\nResponse example:\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"existed\": false,\n    \"upload_id\": \"abc123def456\",\n    \"chunk_size\": 5242880,\n    \"total_chunks\": 20,\n    \"uploaded_chunks\": []\n  }\n}\n```\n\n### 2. Upload Chunk\n\n**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`\n\n- `upload_id` - Upload session ID returned during initialization\n- `chunk_index` - Chunk index, starting from 0\n\nRequest body: Chunk file data (multipart/form-data)\n\nResponse example:\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"chunk_hash\": \"sha256_hash_of_chunk\"\n  }\n}\n```\n\n### 3. Complete Upload\n\n**POST** `/chunk/upload/complete/{upload_id}`\n\nRequest parameters:\n\n```json\n{\n  \"expire_value\": 1,\n  \"expire_style\": \"day\"\n}\n```\n\nResponse example:\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"789012\",\n    \"name\": \"large_file.zip\"\n  }\n}\n```\n\n### Resume Upload\n\nChunked upload supports resume functionality. If upload is interrupted:\n\n1. Call the initialization endpoint again with the same `file_hash`\n2. Server returns `uploaded_chunks` list containing already uploaded chunk indices\n3. Client only needs to upload chunks not in the list\n\n## Error Handling\n\n### Common Error Codes\n\n| Error Code | Description | Solution |\n|------------|-------------|----------|\n| 403 | File size exceeds limit | Reduce file size or contact administrator to adjust limit |\n| 403 | Content too large | Text exceeds 222KB, use file sharing instead |\n| 403 | Upload rate limit | Wait a while before retrying |\n| 404 | File not found | Check if extraction code is correct |\n| 404 | File expired | File has expired or download count exhausted |\n\n### Rate Limiting\n\nTo prevent abuse, the system has rate limits on upload and retrieval operations:\n\n- **Upload limit**: Default max 10 uploads per minute\n- **Error limit**: Default max 1 error attempt per minute\n\n::: tip Tip\nIf you encounter rate limiting, wait for the limit time window to pass before retrying.\n:::\n\n## Next Steps\n\n- [Configuration Guide](/en/guide/configuration) - Learn how to configure sharing-related settings\n- [Storage Configuration](/en/guide/storage) - Learn about file storage methods\n- [Security Settings](/en/guide/security) - Learn about security-related configurations\n- [Admin Panel](/en/guide/management) - Learn how to manage shared files\n"
  },
  {
    "path": "docs/en/guide/storage.md",
    "content": "# Storage Configuration\n\nFileCodeBox supports multiple storage backends. You can choose the appropriate storage method based on your needs. This document details the configuration methods for various storage backends.\n\n## Storage Types Overview\n\n| Storage Type | Config Value | Description |\n|--------------|--------------|-------------|\n| Local Storage | `local` | Default storage method, files saved on local server |\n| S3-Compatible Storage | `s3` | Supports AWS S3, Aliyun OSS, MinIO, etc. |\n| OneDrive | `onedrive` | Microsoft OneDrive cloud storage (work/school accounts only) |\n| WebDAV | `webdav` | Storage services supporting WebDAV protocol |\n| OpenDAL | `opendal` | Integrate more storage services via OpenDAL |\n\n## Local Storage\n\nLocal storage is the default storage method. Files are saved in the server's `data/` directory.\n\n### Configuration Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `file_storage` | string | `local` | Storage type |\n| `storage_path` | string | `\"\"` | Custom storage path (optional) |\n\n### Configuration Example\n\n```bash\nfile_storage=local\nstorage_path=\n```\n\n### Notes\n\n- Files are stored by default in `data/share/data/` directory\n- Subdirectories are automatically created by date: `year/month/day/fileID/`\n- In production, it's recommended to mount the `data/` directory to persistent storage\n\n## S3-Compatible Storage\n\nSupports all S3-compatible object storage services, including AWS S3, Aliyun OSS, MinIO, Tencent Cloud COS, etc.\n\n### Configuration Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `file_storage` | string | - | Set to `s3` |\n| `s3_access_key_id` | string | `\"\"` | Access Key ID |\n| `s3_secret_access_key` | string | `\"\"` | Secret Access Key |\n| `s3_bucket_name` | string | `\"\"` | Bucket name |\n| `s3_endpoint_url` | string | `\"\"` | S3 endpoint URL |\n| `s3_region_name` | string | `auto` | Region name |\n| `s3_signature_version` | string | `s3v2` | Signature version (`s3v2` or `s3v4`) |\n| `s3_hostname` | string | `\"\"` | S3 hostname (alternative) |\n| `s3_proxy` | int | `0` | Download through server proxy (1=yes, 0=no) |\n| `aws_session_token` | string | `\"\"` | AWS session token (optional) |\n\n\n### AWS S3 Configuration Example\n\n```bash\nfile_storage=s3\ns3_access_key_id=AKIAIOSFODNN7EXAMPLE\ns3_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\ns3_bucket_name=my-filecodebox-bucket\ns3_endpoint_url=https://s3.amazonaws.com\ns3_region_name=us-east-1\ns3_signature_version=s3v4\n```\n\n### Aliyun OSS Configuration Example\n\n```bash\nfile_storage=s3\ns3_access_key_id=YourAccessKeyId\ns3_secret_access_key=YourSecretAccessKey\ns3_bucket_name=bucket-name\ns3_endpoint_url=https://bucket-name.oss-cn-hangzhou.aliyuncs.com\ns3_region_name=oss-cn-hangzhou\ns3_signature_version=s3v4\n```\n\n::: tip Aliyun OSS Endpoint Format\nEndpoint URL format: `https://<bucket-name>.<region>.aliyuncs.com`\n\nCommon regions:\n- Hangzhou: `oss-cn-hangzhou`\n- Shanghai: `oss-cn-shanghai`\n- Beijing: `oss-cn-beijing`\n- Shenzhen: `oss-cn-shenzhen`\n:::\n\n### MinIO Configuration Example\n\n```bash\nfile_storage=s3\ns3_access_key_id=minioadmin\ns3_secret_access_key=minioadmin\ns3_bucket_name=filecodebox\ns3_endpoint_url=http://localhost:9000\ns3_region_name=us-east-1\ns3_signature_version=s3v4\n```\n\n::: warning MinIO Notes\n- `s3_endpoint_url` should be the MinIO API interface address\n- `s3_region_name` should match the `Server Location` in MinIO configuration\n- Ensure the bucket is created and has correct access permissions\n:::\n\n### Tencent Cloud COS Configuration Example\n\n```bash\nfile_storage=s3\ns3_access_key_id=YourSecretId\ns3_secret_access_key=YourSecretKey\ns3_bucket_name=bucket-name-1250000000\ns3_endpoint_url=https://cos.ap-guangzhou.myqcloud.com\ns3_region_name=ap-guangzhou\ns3_signature_version=s3v4\n```\n\n### Proxy Download\n\nWhen `s3_proxy=1`, file downloads are proxied through the server instead of directly from S3. This is useful when:\n\n- S3 bucket doesn't allow public access\n- Need to hide the actual storage address\n- Network environment restricts direct S3 access\n\n## OneDrive Storage\n\nOneDrive storage supports saving files to Microsoft OneDrive cloud storage.\n\n::: warning Important Limitation\nOneDrive storage **only supports work or school accounts** and requires admin permissions to authorize the API. Personal accounts cannot use this feature.\n:::\n\n### Configuration Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `file_storage` | string | - | Set to `onedrive` |\n| `onedrive_domain` | string | `\"\"` | Azure AD domain |\n| `onedrive_client_id` | string | `\"\"` | Application (client) ID |\n| `onedrive_username` | string | `\"\"` | Account email |\n| `onedrive_password` | string | `\"\"` | Account password |\n| `onedrive_root_path` | string | `filebox_storage` | Storage root directory in OneDrive |\n| `onedrive_proxy` | int | `0` | Download through server proxy |\n\n### Configuration Example\n\n```bash\nfile_storage=onedrive\nonedrive_domain=contoso.onmicrosoft.com\nonedrive_client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\nonedrive_username=user@contoso.onmicrosoft.com\nonedrive_password=your_password\nonedrive_root_path=filebox_storage\n```\n\n### Azure App Registration Steps\n\nTo use OneDrive storage, you need to register an application in the Azure portal:\n\n#### 1. Get Domain\n\nLog in to [Azure Portal](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade), hover over your account in the top right corner, and the **Domain** shown in the popup is the `onedrive_domain` value.\n\n#### 2. Register Application\n\n1. Click **+ New registration** in the top left\n2. Enter application name (e.g., FileCodeBox)\n3. **Supported account types**: Select \"Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts\"\n4. **Redirect URI**: Select `Web`, enter `http://localhost`\n5. Click **Register**\n\n#### 3. Get Client ID\n\nAfter registration, find the **Application (client) ID** in the **Essentials** section on the app overview page. This is the `onedrive_client_id` value.\n\n#### 4. Configure Authentication\n\n1. Select **Authentication** in the left menu\n2. Find **Allow public client flows**, select **Yes**\n3. Click **Save**\n\n#### 5. Configure API Permissions\n\n1. Select **API permissions** in the left menu\n2. Click **+ Add a permission**\n3. Select **Microsoft Graph** → **Delegated permissions**\n4. Check the following permissions:\n   - `openid`\n   - `Files.Read`\n   - `Files.Read.All`\n   - `Files.ReadWrite`\n   - `Files.ReadWrite.All`\n   - `User.Read`\n5. Click **Add permissions**\n6. Click **Grant admin consent for xxx**\n7. After confirmation, permission status should show **Granted**\n\n### Install Dependencies\n\nUsing OneDrive storage requires additional Python dependencies:\n\n```bash\npip install msal Office365-REST-Python-Client\n```\n\n### Verify Configuration\n\nYou can use the following code to test if the configuration is correct:\n\n```python\nimport msal\nfrom office365.graph_client import GraphClient\n\ndomain = 'your_domain'\nclient_id = 'your_client_id'\nusername = 'your_username'\npassword = 'your_password'\n\ndef acquire_token_pwd():\n    authority_url = f'https://login.microsoftonline.com/{domain}'\n    app = msal.PublicClientApplication(\n        authority=authority_url,\n        client_id=client_id\n    )\n    result = app.acquire_token_by_username_password(\n        username=username,\n        password=password,\n        scopes=['https://graph.microsoft.com/.default']\n    )\n    return result\n\n# Test connection\nclient = GraphClient(acquire_token_pwd)\nme = client.me.get().execute_query()\nprint(f\"Login successful: {me.user_principal_name}\")\n```\n\n\n## WebDAV Storage\n\nWebDAV storage supports saving files to any service that supports the WebDAV protocol, such as Nextcloud, ownCloud, Nutstore, etc.\n\n### Configuration Parameters\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `file_storage` | string | - | Set to `webdav` |\n| `webdav_url` | string | `\"\"` | WebDAV server URL |\n| `webdav_username` | string | `\"\"` | WebDAV username |\n| `webdav_password` | string | `\"\"` | WebDAV password |\n| `webdav_root_path` | string | `filebox_storage` | Storage root directory in WebDAV |\n| `webdav_proxy` | int | `0` | Download through server proxy |\n\n### Configuration Example\n\n```bash\nfile_storage=webdav\nwebdav_url=https://dav.example.com/remote.php/dav/files/username/\nwebdav_username=your_username\nwebdav_password=your_password\nwebdav_root_path=filebox_storage\n```\n\n### Nextcloud Configuration Example\n\n```bash\nfile_storage=webdav\nwebdav_url=https://your-nextcloud.com/remote.php/dav/files/username/\nwebdav_username=your_username\nwebdav_password=your_app_password\nwebdav_root_path=FileCodeBox\n```\n\n::: tip Nextcloud App Password\nIt's recommended to create an app password in Nextcloud instead of using your main password:\n1. Log in to Nextcloud\n2. Go to **Settings** → **Security**\n3. Create a new app password in **Devices & sessions**\n:::\n\n### Nutstore Configuration Example\n\n```bash\nfile_storage=webdav\nwebdav_url=https://dav.jianguoyun.com/dav/\nwebdav_username=your_email@example.com\nwebdav_password=your_app_password\nwebdav_root_path=FileCodeBox\n```\n\n::: tip Nutstore App Password\nNutstore requires an app password:\n1. Log in to Nutstore web version\n2. Go to **Account Info** → **Security Options**\n3. Add an app password\n:::\n\n## OpenDAL Storage\n\nOpenDAL is a unified data access layer that supports multiple storage services. Through OpenDAL, you can use Google Cloud Storage, Azure Blob Storage, and more.\n\n### Configuration Parameters\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `file_storage` | string | Set to `opendal` |\n| `opendal_scheme` | string | Storage service type (e.g., `gcs`, `azblob`) |\n| `opendal_<scheme>_<setting>` | string | Service-specific configuration parameters |\n\n### Install Dependencies\n\n```bash\npip install opendal\n```\n\n### Google Cloud Storage Configuration Example\n\n```bash\nfile_storage=opendal\nopendal_scheme=gcs\nopendal_gcs_root=/filecodebox\nopendal_gcs_bucket=your-bucket-name\nopendal_gcs_credential=base64_encoded_credential\n```\n\n### Azure Blob Storage Configuration Example\n\n```bash\nfile_storage=opendal\nopendal_scheme=azblob\nopendal_azblob_root=/filecodebox\nopendal_azblob_container=your-container\nopendal_azblob_account_name=your_account\nopendal_azblob_account_key=your_key\n```\n\n### Supported Services\n\nOpenDAL supports numerous storage services. For the complete list, see the [OpenDAL Official Documentation](https://opendal.apache.org/docs/rust/opendal/services/index.html).\n\nCommon services include:\n- `gcs` - Google Cloud Storage\n- `azblob` - Azure Blob Storage\n- `obs` - Huawei Cloud OBS\n- `oss` - Aliyun OSS (via OpenDAL)\n- `cos` - Tencent Cloud COS (via OpenDAL)\n- `hdfs` - Hadoop HDFS\n- `ftp` - FTP server\n- `sftp` - SFTP server\n\n::: warning OpenDAL Notes\n1. Services integrated via OpenDAL download through server proxy, consuming both storage service and server bandwidth\n2. Compared to native S3/OneDrive support, OpenDAL may lack some debugging information\n3. OpenDAL is written in Rust with good performance\n:::\n\n## Storage Selection Recommendations\n\n| Scenario | Recommended Storage | Reason |\n|----------|---------------------|--------|\n| Personal/Small deployment | Local storage | Simple and easy, no extra configuration needed |\n| Enterprise intranet | MinIO + S3 | Self-hosted object storage, data control |\n| Public cloud deployment | Corresponding cloud provider S3 | Fast same-region access, low cost |\n| Existing OneDrive | OneDrive | Utilize existing resources |\n| Existing WebDAV | WebDAV | Good compatibility |\n| Special storage needs | OpenDAL | Supports more storage services |\n\n## Common Issues\n\n### S3 Upload Failure\n\n1. Check if Access Key and Secret Key are correct\n2. Confirm bucket name and region configuration are correct\n3. Check bucket access permission settings\n4. Confirm signature version (`s3v2` or `s3v4`) matches service provider requirements\n\n### OneDrive Authentication Failure\n\n1. Confirm using a work/school account, not a personal account\n2. Check if Azure app has been granted admin consent\n3. Confirm API permissions are fully configured\n4. Verify username and password are correct\n\n### WebDAV Connection Failure\n\n1. Check if WebDAV URL format is correct\n2. Confirm username and password (or app password) are correct\n3. Check if server supports WebDAV protocol\n4. Confirm network connection is normal\n"
  },
  {
    "path": "docs/en/guide/upload.md",
    "content": "# File Upload\n\nFileCodeBox provides multiple flexible file upload methods, supporting both regular upload and chunked upload to meet different scenario requirements.\n\n## Upload Methods\n\nFileCodeBox supports the following upload methods:\n\n### Drag and Drop Upload\n\nDrag files directly to the upload area to start uploading. This is the most convenient upload method.\n\n1. Open the FileCodeBox homepage\n2. Drag files from your file manager to the upload area\n3. Release the mouse, file upload begins\n4. Get the extraction code after upload completes\n\n::: tip Tip\nDrag and drop upload supports dragging multiple files simultaneously (depending on theme support).\n:::\n\n### Click Upload\n\nClick the upload area to select files through the system file picker.\n\n1. Click the \"Select File\" button in the upload area\n2. Select the file to upload in the popup file picker\n3. File upload begins after confirming selection\n4. Get the extraction code after upload completes\n\n### Paste Upload\n\nSupports pasting images directly from clipboard for upload (supported by some themes).\n\n1. Copy an image to clipboard (screenshot or copy image)\n2. Use `Ctrl+V` (Windows/Linux) or `Cmd+V` (macOS) to paste in the upload area\n3. Image upload starts automatically\n4. Get the extraction code after upload completes\n\n::: warning Note\nPaste upload only supports image formats, not other file types. Specific support depends on the theme being used.\n:::\n\n## File Size Limits\n\n### Default Limits\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `uploadSize` | 10MB | Maximum single file upload size |\n\n### Modify Upload Limits\n\nAdministrators can modify upload size limits through the admin panel or configuration file:\n\n```python\n# Set maximum upload size to 100MB\nuploadSize = 104857600  # 100 * 1024 * 1024\n```\n\n::: info Note\n`uploadSize` is in bytes. Common conversions:\n- 10MB = 10485760\n- 50MB = 52428800\n- 100MB = 104857600\n- 500MB = 524288000\n- 1GB = 1073741824\n:::\n\n### Exceeding Limit Handling\n\nWhen an uploaded file exceeds the size limit, the system returns a 403 error:\n\n```json\n{\n  \"detail\": \"Size exceeds limit, maximum is 10.00 MB\"\n}\n```\n\n## Regular Upload API\n\n### File Upload Endpoint\n\n**POST** `/share/file/`\n\nContent-Type: `multipart/form-data`\n\n**Request parameters:**\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `file` | file | Yes | File to upload |\n| `expire_value` | int | No | Expiration value, default 1 |\n| `expire_style` | string | No | Expiration method, default `day` |\n\n**Expiration method options:**\n\n| Value | Description |\n|-------|-------------|\n| `day` | Expire by days |\n| `hour` | Expire by hours |\n| `minute` | Expire by minutes |\n| `forever` | Never expire |\n| `count` | Expire by download count |\n\n**Response example:**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"654321\",\n    \"name\": \"example.pdf\"\n  }\n}\n```\n\n**cURL example:**\n\n```bash\n# Upload file (default 1 day expiration)\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\"\n\n# Upload file with 7 days expiration\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\" \\\n  -F \"expire_value=7\" \\\n  -F \"expire_style=day\"\n\n# Upload file with 10 downloads limit\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n\n# Share text\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=This is the text content to share\"\n\n# Download file by extraction code\ncurl -L \"http://localhost:12345/share/select/?code=YOUR_CODE\" -o downloaded_file\n```\n\n::: tip When Authentication Required\nIf guest upload is disabled in admin panel (`openUpload=0`), you need to login first:\n\n```bash\n# 1. Login to get token\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n\n# Returns: {\"code\":200,\"msg\":\"success\",\"detail\":{\"token\":\"xxx.xxx.xxx\",\"token_type\":\"Bearer\"}}\n\n# 2. Upload file with token\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"file=@/path/to/file.pdf\"\n\n# 3. Share text with token\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"text=This is the text content to share\"\n```\n:::\n\n\n## Chunked Upload API\n\nFor large files, FileCodeBox supports chunked upload functionality. Chunked upload splits large files into multiple small chunks for separate uploading, supporting resume capability.\n\n::: warning Prerequisite\nChunked upload functionality requires administrator enablement: `enableChunk=1`\n:::\n\n### Chunked Upload Flow\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│  Initialize │ ──▶ │Upload Chunks│ ──▶ │  Complete   │\n│   /init/    │     │  /chunk/    │     │ /complete/  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                          │\n                          ▼\n                    ┌───────────┐\n                    │   Loop    │\n                    │each chunk │\n                    └───────────┘\n```\n\n### 1. Initialize Upload\n\n**POST** `/chunk/upload/init/`\n\n**Request parameters:**\n\n```json\n{\n  \"file_name\": \"large_file.zip\",\n  \"file_size\": 104857600,\n  \"chunk_size\": 5242880,\n  \"file_hash\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}\n```\n\n| Parameter | Type | Required | Default | Description |\n|-----------|------|----------|---------|-------------|\n| `file_name` | string | Yes | - | Filename |\n| `file_size` | int | Yes | - | Total file size (bytes) |\n| `chunk_size` | int | No | 5MB | Chunk size (bytes) |\n| `file_hash` | string | Yes | - | SHA256 hash of the file |\n\n**Response example:**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"existed\": false,\n    \"upload_id\": \"abc123def456789\",\n    \"chunk_size\": 5242880,\n    \"total_chunks\": 20,\n    \"uploaded_chunks\": []\n  }\n}\n```\n\n| Field | Description |\n|-------|-------------|\n| `existed` | Whether file already exists (instant upload) |\n| `upload_id` | Upload session ID |\n| `chunk_size` | Chunk size |\n| `total_chunks` | Total number of chunks |\n| `uploaded_chunks` | List of already uploaded chunk indices |\n\n### 2. Upload Chunk\n\n**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`\n\n**Path parameters:**\n\n| Parameter | Description |\n|-----------|-------------|\n| `upload_id` | Upload session ID returned during initialization |\n| `chunk_index` | Chunk index, starting from 0 |\n\n**Request body:**\n\nContent-Type: `multipart/form-data`\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `chunk` | file | Chunk data |\n\n**Response example:**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"chunk_hash\": \"a1b2c3d4e5f6...\"\n  }\n}\n```\n\n**cURL example:**\n\n```bash\n# Upload first chunk (index 0)\ncurl -X POST \"http://localhost:12345/chunk/upload/chunk/abc123def456789/0\" \\\n  -F \"chunk=@/path/to/chunk_0\"\n```\n\n### 3. Complete Upload\n\n**POST** `/chunk/upload/complete/{upload_id}`\n\n**Path parameters:**\n\n| Parameter | Description |\n|-----------|-------------|\n| `upload_id` | Upload session ID |\n\n**Request parameters:**\n\n```json\n{\n  \"expire_value\": 7,\n  \"expire_style\": \"day\"\n}\n```\n\n| Parameter | Type | Required | Description |\n|-----------|------|----------|-------------|\n| `expire_value` | int | Yes | Expiration value |\n| `expire_style` | string | Yes | Expiration method |\n\n**Response example:**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"789012\",\n    \"name\": \"large_file.zip\"\n  }\n}\n```\n\n### Resume Upload\n\nChunked upload supports resume functionality. When upload is interrupted:\n\n1. Call the initialization endpoint again with the same `file_hash`\n2. Server returns `uploaded_chunks` list containing already uploaded chunk indices\n3. Client only needs to upload chunks not in the list\n4. Call the complete endpoint after all chunks are uploaded\n\n**Example flow:**\n\n```javascript\n// 1. Initialize upload\nconst initResponse = await fetch('/chunk/upload/init/', {\n  method: 'POST',\n  body: JSON.stringify({\n    file_name: 'large_file.zip',\n    file_size: fileSize,\n    chunk_size: 5 * 1024 * 1024,\n    file_hash: fileHash\n  })\n});\nconst { upload_id, uploaded_chunks, total_chunks } = await initResponse.json();\n\n// 2. Upload incomplete chunks\nfor (let i = 0; i < total_chunks; i++) {\n  if (!uploaded_chunks.includes(i)) {\n    const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);\n    await fetch(`/chunk/upload/chunk/${upload_id}/${i}`, {\n      method: 'POST',\n      body: chunk\n    });\n  }\n}\n\n// 3. Complete upload\nawait fetch(`/chunk/upload/complete/${upload_id}`, {\n  method: 'POST',\n  body: JSON.stringify({\n    expire_value: 7,\n    expire_style: 'day'\n  })\n});\n```\n\n## Error Handling\n\n### Common Errors\n\n| HTTP Status | Error Message | Cause | Solution |\n|-------------|---------------|-------|----------|\n| 403 | Size exceeds limit | File exceeds `uploadSize` limit | Reduce file size or contact administrator to adjust limit |\n| 403 | Upload rate limit | Exceeded IP upload rate limit | Wait for limit time window before retrying |\n| 400 | Invalid expiration type | `expire_style` value not in allowed list | Use a valid expiration method |\n| 404 | Upload session not found | `upload_id` invalid or expired | Re-initialize upload |\n| 400 | Invalid chunk index | `chunk_index` out of range | Check if chunk index is correct |\n| 400 | Incomplete chunks | Chunk count insufficient when completing upload | Ensure all chunks are uploaded |\n\n### Rate Limiting\n\nThe system has rate limits on upload operations to prevent abuse:\n\n| Setting | Default | Description |\n|---------|---------|-------------|\n| `uploadMinute` | 1 | Limit time window (minutes) |\n| `uploadCount` | 10 | Maximum uploads within time window |\n\nWhen rate limit is exceeded, you need to wait for the time window to pass before continuing uploads.\n\n### Error Response Format\n\n```json\n{\n  \"detail\": \"Error message description\"\n}\n```\n\n## Upload Configuration\n\n### Related Settings\n\n| Setting | Type | Default | Description |\n|---------|------|---------|-------------|\n| `openUpload` | int | 1 | Enable upload (1=enabled, 0=disabled) |\n| `uploadSize` | int | 10485760 | Maximum upload size (bytes) |\n| `enableChunk` | int | 0 | Enable chunked upload (1=enabled, 0=disabled) |\n| `uploadMinute` | int | 1 | Upload rate limit time window (minutes) |\n| `uploadCount` | int | 10 | Maximum uploads within time window |\n| `expireStyle` | list | [\"day\",\"hour\",\"minute\",\"forever\",\"count\"] | Allowed expiration methods |\n\n### Configuration Example\n\n```python\n# Allow 100MB file uploads, enable chunked upload\nuploadSize = 104857600\nenableChunk = 1\n\n# Relax upload rate limit: max 50 uploads per 5 minutes\nuploadMinute = 5\nuploadCount = 50\n\n# Only allow expiration by days and count\nexpireStyle = [\"day\", \"count\"]\n```\n\n## Next Steps\n\n- [File Sharing](/en/guide/share) - Learn the complete sharing process\n- [Configuration Guide](/en/guide/configuration) - Learn about all configuration options\n- [Storage Configuration](/en/guide/storage) - Learn about file storage methods\n- [Security Settings](/en/guide/security) - Learn about security-related configurations\n"
  },
  {
    "path": "docs/en/index.md",
    "content": "---\nlayout: home\n\nhero:\n  name: \"FileCodeBox\"\n  text: \"File Express Box\"\n  tagline: Share text and files anonymously with access codes, just like picking up a package\n  image:\n    src: /logo_small.png\n    alt: FileCodeBox\n  actions:\n    - theme: brand\n      text: Get Started\n      link: /en/guide/getting-started\n    - theme: alt\n      text: Live Demo\n      link: https://share.lanol.cn\n    - theme: alt\n      text: View on GitHub\n      link: https://github.com/vastsa/FileCodeBox\n\nfeatures:\n  - icon: 🚀\n    title: Quick Deployment\n    details: Supports one-click Docker deployment, simple and fast, no complex configuration needed\n  - icon: 🔒\n    title: Secure & Reliable\n    details: File access requires an access code, supports expiration time and download limit settings\n  - icon: 💻\n    title: Clean Interface\n    details: Clean user interface with drag-and-drop upload support for excellent user experience\n  - icon: 🛠️\n    title: Feature Rich\n    details: Supports file preview, online playback, image processing, and many other features\n  - icon: 📦\n    title: Storage Extensions\n    details: Supports various storage methods including local storage and object storage\n  - icon: 🔌\n    title: API Support\n    details: Provides complete REST API for easy integration with other systems\n--- "
  },
  {
    "path": "docs/en/showcase.md",
    "content": "# Showcase\n\nHere are some excellent sites built with FileCodeBox. If you've deployed FileCodeBox, feel free to submit a PR to add your site here!\n\n## Official Demo\n\n<div class=\"showcase-grid\">\n\n<div class=\"showcase-item\">\n\n### 🌟 FileCodeBox Demo\n\n- **URL**: [share.lanol.cn](https://share.lanol.cn)\n- **Description**: Official demo site with the latest features\n- **Highlights**: Stable, full-featured\n\n</div>\n\n</div>\n\n## Community Sites\n\n::: tip Submit Your Site\nIf you've built your own file sharing service using FileCodeBox, you can submit it through:\n\n1. Submit a PR on [GitHub](https://github.com/vastsa/FileCodeBox) to edit this page\n2. Open an [Issue](https://github.com/vastsa/FileCodeBox/issues) with your site info\n3. Join QQ Group 739673698 to contact the admin\n:::\n\n<!-- \nSubmission format example:\n\n<div class=\"showcase-item\">\n\n### Site Name\n\n- **URL**: [example.com](https://example.com)\n- **Description**: Brief description of your site\n- **Highlights**: List your site's features\n\n</div>\n-->\n\n<div class=\"showcase-grid\">\n\n<div class=\"showcase-item\">\n\n### QuWenJian\n\n- **URL**: [www.quwenjian.cn/fby.html](https://www.quwenjian.cn/fby.html)\n- **Description**: QuWenJian - Unlimited storage, portable and limitless\n- **Highlights**: Free file transfer station\n- **Operator**: QuWenJian\n\n</div>\n\n<div class=\"showcase-item\">\n\n### Pandora Box\n\n- **URL**: [pan.duo.la](https://pan.duo.la)\n- **Description**: Pandora Box\n- **Highlights**: Classic v1.6 version\n- **Operator**: WuXingQueXinYan\n\n</div>\n\n</div>\n\n## Submission Requirements\n\nTo ensure quality, please make sure your site meets the following criteria:\n\n1. **Stable**: Your site should be stable and accessible\n2. **Legal**: Content must be legal and compliant\n3. **Attribution**: We recommend keeping FileCodeBox attribution\n4. **HTTPS**: We recommend enabling HTTPS\n\n## Submission Template\n\nIf you want to submit your site, please use the following format:\n\n```markdown\n### Site Name\n\n- **URL**: [domain](https://domain)\n- **Description**: One-line description of your site\n- **Highlights**: Special features or highlights\n- **Operator**: Optional, your name or organization\n```\n\n<style>\n.showcase-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n  gap: 20px;\n  margin: 20px 0;\n}\n\n.showcase-item {\n  border: 1px solid var(--vp-c-divider);\n  border-radius: 8px;\n  padding: 20px;\n  transition: all 0.3s ease;\n}\n\n.showcase-item:hover {\n  border-color: var(--vp-c-brand);\n  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);\n}\n\n.showcase-item h3 {\n  margin-top: 0;\n  color: var(--vp-c-brand);\n}\n\n.showcase-item ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n\n.showcase-item li {\n  margin: 8px 0;\n}\n</style>\n"
  },
  {
    "path": "docs/guide/configuration.md",
    "content": "# 配置说明\n\nFileCodeBox 提供了丰富的配置选项，可以通过管理面板或直接修改配置来自定义系统行为。本文档详细介绍所有可用的配置项。\n\n## 配置方式\n\nFileCodeBox 支持两种配置方式：\n\n1. **管理面板配置**（推荐）：访问 `/admin` 进入管理面板，在设置页面修改配置\n2. **数据库配置**：配置存储在 `data/filecodebox.db` 数据库中\n\n::: tip 提示\n首次启动时，系统会使用 `core/settings.py` 中的默认配置。修改后的配置会保存到数据库中。\n:::\n\n## 基础设置\n\n### 站点信息\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `name` | string | `文件快递柜 - FileCodeBox` | 站点名称，显示在页面标题和导航栏 |\n| `description` | string | `开箱即用的文件快传系统` | 站点描述，用于 SEO |\n| `keywords` | string | `FileCodeBox, 文件快递柜...` | 站点关键词，用于 SEO |\n| `port` | int | `12345` | 服务监听端口 |\n\n### 通知设置\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `notify_title` | string | `系统通知` | 通知标题 |\n| `notify_content` | string | 欢迎信息 | 通知内容，支持 HTML |\n| `page_explain` | string | 法律声明 | 页面底部说明文字 |\n| `robotsText` | string | `User-agent: *\\nDisallow: /` | robots.txt 内容 |\n\n## 上传设置\n\n### 文件上传限制\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `openUpload` | int | `1` | 是否开启上传功能（1=开启，0=关闭） |\n| `uploadSize` | int | `10485760` | 单文件最大上传大小（字节），默认 10MB |\n| `enableChunk` | int | `0` | 是否启用分片上传（1=启用，0=禁用） |\n\n::: warning 注意\n`uploadSize` 的单位是字节。10MB = 10 * 1024 * 1024 = 10485760 字节\n:::\n\n### 上传频率限制\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `uploadMinute` | int | `1` | 上传限制的时间窗口（分钟） |\n| `uploadCount` | int | `10` | 在时间窗口内允许的最大上传次数 |\n\n例如：默认配置表示每 1 分钟内最多允许上传 10 次。\n\n### 文件过期设置\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `expireStyle` | list | `[\"day\",\"hour\",\"minute\",\"forever\",\"count\"]` | 可选的过期方式 |\n| `max_save_seconds` | int | `0` | 文件最大保存时间（秒），0 表示不限制 |\n\n过期方式说明：\n- `day` - 按天过期\n- `hour` - 按小时过期\n- `minute` - 按分钟过期\n- `forever` - 永不过期\n- `count` - 按下载次数过期\n\n## 主题设置\n\n### 主题选择\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `themesSelect` | string | `themes/2024` | 当前使用的主题 |\n| `themesChoices` | list | 见下方 | 可用主题列表 |\n\n默认可用主题：\n```json\n[\n  {\n    \"name\": \"2023\",\n    \"key\": \"themes/2023\",\n    \"author\": \"Lan\",\n    \"version\": \"1.0\"\n  },\n  {\n    \"name\": \"2024\",\n    \"key\": \"themes/2024\",\n    \"author\": \"Lan\",\n    \"version\": \"1.0\"\n  }\n]\n```\n\n### 界面样式\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `opacity` | float | `0.9` | 界面透明度（0-1） |\n| `background` | string | `\"\"` | 自定义背景图片 URL，为空则使用默认背景 |\n\n## 管理员设置\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `admin_token` | string | `FileCodeBox2023` | 管理员登录密码 |\n| `showAdminAddr` | int | `0` | 是否在首页显示管理入口（1=显示，0=隐藏） |\n\n::: danger 安全警告\n请务必在生产环境中修改默认的 `admin_token`！使用默认密码会导致严重的安全风险。\n:::\n\n## 安全设置\n\n### 错误次数限制\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `errorMinute` | int | `1` | 错误限制的时间窗口（分钟） |\n| `errorCount` | int | `1` | 在时间窗口内允许的最大错误次数 |\n\n此设置用于防止暴力破解提取码。\n\n## 存储设置\n\n### 存储类型\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `file_storage` | string | `local` | 存储后端类型 |\n| `storage_path` | string | `\"\"` | 自定义存储路径 |\n\n支持的存储类型：\n- `local` - 本地存储\n- `s3` - S3 兼容存储（AWS S3、阿里云 OSS、MinIO 等）\n- `onedrive` - OneDrive 存储\n- `webdav` - WebDAV 存储\n- `opendal` - OpenDAL 存储\n\n详细的存储配置请参考 [存储配置](/guide/storage)。\n\n## 配置示例\n\n### 示例 1：小型个人使用\n\n适合个人或小团队使用，限制较宽松：\n\n```python\n{\n    \"name\": \"我的文件分享\",\n    \"uploadSize\": 52428800,        # 50MB\n    \"uploadMinute\": 5,             # 5分钟\n    \"uploadCount\": 20,             # 最多20次\n    \"expireStyle\": [\"day\", \"hour\", \"forever\"],\n    \"admin_token\": \"your-secure-password\",\n    \"showAdminAddr\": 1\n}\n```\n\n### 示例 2：公开服务\n\n适合公开服务，需要更严格的限制：\n\n```python\n{\n    \"name\": \"公共文件快递柜\",\n    \"uploadSize\": 10485760,        # 10MB\n    \"uploadMinute\": 1,             # 1分钟\n    \"uploadCount\": 5,              # 最多5次\n    \"errorMinute\": 5,              # 5分钟\n    \"errorCount\": 3,               # 最多3次错误\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400,     # 最长保存1天\n    \"admin_token\": \"very-secure-password-123\",\n    \"showAdminAddr\": 0\n}\n```\n\n### 示例 3：企业内部使用\n\n适合企业内部使用，支持大文件和分片上传：\n\n```python\n{\n    \"name\": \"企业文件中转站\",\n    \"uploadSize\": 1073741824,      # 1GB\n    \"enableChunk\": 1,              # 启用分片上传\n    \"uploadMinute\": 10,            # 10分钟\n    \"uploadCount\": 100,            # 最多100次\n    \"expireStyle\": [\"day\", \"forever\"],\n    \"file_storage\": \"s3\",          # 使用S3存储\n    \"admin_token\": \"enterprise-secure-token\",\n    \"showAdminAddr\": 1\n}\n```\n\n## 下一步\n\n- [存储配置](/guide/storage) - 了解如何配置不同的存储后端\n- [安全设置](/guide/security) - 了解如何增强系统安全性\n- [文件分享](/guide/share) - 了解文件分享功能\n"
  },
  {
    "path": "docs/guide/getting-started.md",
    "content": "# 快速开始\n\n## 简介\n\nFileCodeBox 是一个简单高效的文件分享工具，支持文件临时中转、分享和管理。本指南将帮助您快速部署和使用 FileCodeBox。\n\n## 特性\n\n- 🚀 快速部署：支持 Docker 一键部署\n- 🔒 安全可靠：文件访问需要提取码\n- ⏱️ 时效控制：支持设置文件有效期\n- 📊 下载限制：可限制文件下载次数\n- 🖼️ 文件预览：支持图片、视频、音频等多种格式预览\n- 📱 响应式设计：完美适配移动端和桌面端\n\n## 部署方式\n\n### Docker 部署（推荐）\n\n#### 快速启动\n\n```bash\ndocker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:latest\n```\n\n#### Docker Compose\n\n```yml\nversion: \"3\"\nservices:\n  file-code-box:\n    image: lanol/filecodebox:latest\n    volumes:\n      - fcb-data:/app/data:rw\n    restart: unless-stopped\n    ports:\n      - \"12345:12345\"\n    environment:\n      - WORKERS=4\n      - LOG_LEVEL=info\nvolumes:\n  fcb-data:\n    external: false\n```\n\n#### 环境变量\n\n| 变量 | 默认值 | 说明 |\n|------|--------|------|\n| `HOST` | `::` | 监听地址，支持 IPv4/IPv6 双栈 |\n| `PORT` | `12345` | 服务端口 |\n| `WORKERS` | `4` | 工作进程数，建议设置为 CPU 核心数 |\n| `LOG_LEVEL` | `info` | 日志级别：debug/info/warning/error |\n\n#### 自定义配置示例\n\n```bash\ndocker run -d --restart=always \\\n  -p 12345:12345 \\\n  -v /opt/FileCodeBox/:/app/data \\\n  -e WORKERS=8 \\\n  -e LOG_LEVEL=warning \\\n  --name filecodebox \\\n  lanol/filecodebox:latest\n```\n\n### 配置反向代理（Nginx）\n\n```nginx\nlocation / {\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_pass http://localhost:12345;\n}\n```\n\n### 手动部署\n\n1. 克隆项目\n```bash\ngit clone https://github.com/vastsa/FileCodeBox.git\n```\n\n2. 安装依赖\n```bash\ncd FileCodeBox\npip install -r requirements.txt\n```\n\n3. 启动服务\n```bash\npython main.py\n```\n\n\n## 使用方法\n\n1. 访问系统\n   打开浏览器访问 `http://localhost:12345`\n\n2. 上传文件\n   - 点击上传按钮或拖拽文件到上传区域\n   - 设置文件有效期和下载次数限制\n   - 获取分享链接和提取码\n\n3. 下载文件\n   - 访问分享链接\n   - 输入提取码\n   - 下载文件\n\n4. 后台管理\n   - 访问 `http://localhost:12345/#/admin`\n   - 输入管理员密码：`FileCodeBox2023`\n   - 进入后台管理页面\n   - 查看系统信息、文件列表、用户管理等\n\n## 下一步\n\n- [存储配置](/guide/storage) - 了解如何配置不同的存储方式\n- [安全设置](/guide/security) - 了解如何增强系统安全性\n- [API 文档](/api/) - 了解如何通过 API 集成 "
  },
  {
    "path": "docs/guide/introduction.md",
    "content": "<div align=\"center\">\n\n<img src=\"https://fastly.jsdelivr.net/gh/vastsa/FileCodeBox@V1.6/static/banners/img_1.png\" alt=\"FileCodeBox Logo\">\n\n<p><em>匿名口令分享文本和文件，像拿快递一样取文件</em></p>\n\n[![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/network)\n[![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/issues)\n[![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)\n[![QQ Group](https://img.shields.io/badge/QQ%20Group-739673698-blue.svg)](https://qm.qq.com/q/PemPzhdEIM)\n[![Python Version](https://img.shields.io/badge/Python-3.8+-blue.svg)](https://www.python.org)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-green.svg)](https://fastapi.tiangolo.com)\n[![Vue Version](https://img.shields.io/badge/Vue.js-3.x-brightgreen.svg)](https://v3.vuejs.org)\n</div>\n\n\n## 🚀 更新计划\n- [ ] 切片上传，同文件秒传，断点续传\n- [ ] 用户登录重构\n- [x] webdav存储\n- [x] 存储支持自定义路径\n- [x] s3优化，不修改昵称为uuid，新建目录\n\n## 📝 项目简介\n\nFileCodeBox 是一个基于 FastAPI + Vue3 开发的轻量级文件分享工具。它允许用户通过简单的方式分享文本和文件，接收者只需要一个提取码就可以取得文件，就像从快递柜取出快递一样简单。\n\n## 🎯 应用场景\n\n<table>\n<tr>\n<td align=\"center\">\n<h4>📁 临时文件分享</h4>\n快速分享单个文件，无需注册登录\n</td>\n<td align=\"center\">\n<h4>📝 文本快速分享</h4>\n分享代码片段、文本内容等\n</td>\n<td align=\"center\">\n<h4>🕶️ 匿名文件传输</h4>\n保护隐私的文件传输方式\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>💾 临时文件存储</h4>\n支持设置过期时间的文件存储\n</td>\n<td align=\"center\">\n<h4>🔄 跨平台传输</h4>\n在不同设备间快速传输文件\n</td>\n<td align=\"center\">\n<h4>🌐 小型分享服务</h4>\n搭建私有的文件分享服务\n</td>\n</tr>\n</table>\n\n## ✨ 核心特性\n\n<table>\n<tr>\n<td align=\"center\">\n<h4>🚀 轻量简洁</h4>\n基于 FastAPI + SQLite3 + Vue3 + ElementUI，部署简单，性能出色\n</td>\n<td align=\"center\">\n<h4>📤 便捷上传</h4>\n支持复制粘贴、拖拽上传，操作简单直观\n</td>\n<td align=\"center\">\n<h4>📦 多种类型</h4>\n支持文本和各类文件的分享\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>🔒 安全机制</h4>\n\n- IP 限制上传次数\n- 错误次数限制\n- 文件过期机制\n</td>\n<td align=\"center\">\n<h4>🎫 提取码分享</h4>\n随机提取码，可自定义次数及有效期\n</td>\n<td align=\"center\">\n<h4>🌍 多语言支持</h4>\n支持中文简体、繁体及英文\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>🎭 匿名分享</h4>\n无需注册登录，保护隐私\n</td>\n<td align=\"center\">\n<h4>🛠 管理面板</h4>\n文件管理和系统配置\n</td>\n<td align=\"center\">\n<h4>🐳 容器部署</h4>\n支持 Docker 一键部署\n</td>\n</tr>\n<tr>\n<td align=\"center\">\n<h4>💾 存储扩展</h4>\n支持本地存储、S3 协议、OneDrive 等\n</td>\n<td align=\"center\">\n<h4>📱 响应式设计</h4>\n支持移动端访问\n</td>\n<td align=\"center\">\n<h4>💻 终端支持</h4>\n支持命令行下载\n</td>\n</tr>\n</table>\n\n## 🚀 快速开始\n\n### Docker 部署\n\n```bash\ndocker run -d --restart=always -p 12345:12345 -v /opt/FileCodeBox/:/app/data --name filecodebox lanol/filecodebox:beta\n```\n\n### 手动部署\n\n1. 克隆项目\n```bash\ngit clone https://github.com/vastsa/FileCodeBox.git\n```\n\n2. 安装依赖\n```bash\ncd FileCodeBox\npip install -r requirements.txt\n```\n\n3. 启动服务\n```bash\npython main.py\n```\n\n## 📖 使用说明\n\n### 分享文件\n1. 打开网页，点击\"分享文件\"\n2. 选择或拖拽文件\n3. 设置过期时间和次数\n4. 获取提取码\n\n### 获取文件\n1. 打开网页，输入提取码\n2. 点击获取\n3. 下载文件或查看文本\n\n### 管理面板\n1. 访问 `/admin`\n2. 输入管理员密码\n3. 管理文件和配置\n\n## 🛠 开发指南\n\n### 项目结构\n```\nFileCodeBox/\n├── apps/           # 应用代码\n│   ├── admin/     # 管理后台\n│   └── base/      # 基础功能\n├── core/          # 核心功能\n├── data/          # 数据目录\n└── fcb-fronted/   # 前端代码\n```\n\n### 开发环境\n- Python 3.8+\n- Node.js 14+\n- Vue 3\n- FastAPI\n\n### 本地开发\n1. 后端开发\n```bash\npython main.py\n```\n\n2. 前端开发\n```bash\ncd fcb-fronted\nnpm install\nnpm run dev\n```\n\n## 🤝 贡献指南\n\n1. Fork 本项目\n2. 创建新分支 `git checkout -b feature/xxx`\n3. 提交更改 `git commit -m 'Add xxx'`\n4. 推送到分支 `git push origin feature/xxx`\n5. 提交 Pull Request\n\n## ❓ 常见问题\n\n### Q: 如何修改上传大小限制？\nA: 在管理面板中修改配置项 `uploadSize`\n\n### Q: 如何配置存储引擎？\nA: 在管理面板中选择存储引擎并配置相应参数\n\n### Q: 如何备份数据？\nA: 备份 `data` 目录即可\n\n更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题)\n\n## 📊 项目统计\n\n<div align=\"center\">\n<a href=\"https://hellogithub.com/repository/75ad7ffedd404a6485b4d621ec5b47e6\" target=\"_blank\"><img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=75ad7ffedd404a6485b4d621ec5b47e6&claim_uid=beSz6INEkCM4mDH\" alt=\"Featured｜HelloGitHub\" style=\"width: 200px; height: 45px;\" width=\"200\" height=\"45\" /></a>\n\n![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg)\n\n[![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date)\n</div>\n\n## 📜 免责声明\n\n本项目开源仅供学习使用，不得用于任何违法用途，否则后果自负，与作者无关。使用时请保留项目地址和版权信息。\n"
  },
  {
    "path": "docs/guide/management.md",
    "content": "# 管理面板\n\nFileCodeBox 提供了功能完善的管理面板，让管理员可以方便地管理文件、查看系统状态和修改配置。本文档介绍管理面板的各项功能和使用方法。\n\n## 访问管理面板\n\n### 登录方式\n\n管理面板位于 `/admin` 路径。访问方式：\n\n1. 在浏览器中访问 `http://your-domain.com/admin`\n2. 输入管理员密码（`admin_token` 配置项的值）\n3. 点击登录按钮\n\n::: tip 提示\n默认管理员密码是 `FileCodeBox2023`。请务必在生产环境中修改此密码，详见 [安全设置](/guide/security)。\n:::\n\n### 显示管理入口\n\n默认情况下，首页不显示管理面板入口。您可以通过配置控制是否显示：\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `showAdminAddr` | int | `0` | 是否在首页显示管理入口（1=显示，0=隐藏） |\n\n::: warning 安全建议\n在公开服务中，建议保持 `showAdminAddr` 为 `0`，通过直接访问 `/admin` 路径进入管理面板，减少被恶意扫描的风险。\n:::\n\n### 认证机制\n\n管理面板使用 JWT（JSON Web Token）进行身份认证：\n\n1. 登录成功后，服务器返回一个包含管理员身份的 Token\n2. 后续请求通过 `Authorization: Bearer <token>` 头部携带 Token\n3. Token 用于验证管理员身份，确保只有授权用户可以访问管理功能\n\n## 仪表盘\n\n登录后首先看到的是仪表盘页面，展示系统的整体运行状态。\n\n### 统计指标\n\n仪表盘显示以下关键指标：\n\n| 指标 | 说明 |\n|------|------|\n| **文件总数** (`totalFiles`) | 系统中存储的文件总数量 |\n| **存储使用量** (`storageUsed`) | 所有文件占用的总存储空间（字节） |\n| **系统运行时间** (`sysUptime`) | 系统首次启动的时间 |\n| **昨日上传数** (`yesterdayCount`) | 昨天一整天上传的文件数量 |\n| **昨日上传量** (`yesterdaySize`) | 昨天上传文件的总大小（字节） |\n| **今日上传数** (`todayCount`) | 今天到目前为止上传的文件数量 |\n| **今日上传量** (`todaySize`) | 今天上传文件的总大小（字节） |\n\n### 指标说明\n\n- **文件总数**：包括所有未过期的文件和文本分享\n- **存储使用量**：显示实际文件占用的存储空间，不包括数据库等系统文件\n- **昨日/今日统计**：基于文件创建时间计算，用于了解系统使用趋势\n\n::: tip 提示\n存储使用量显示的是字节数。例如 `10485760` 表示约 10MB。\n:::\n\n## 文件管理\n\n### 文件列表\n\n文件管理页面展示系统中所有已分享的文件，支持分页浏览和搜索。\n\n**列表信息包括：**\n- 文件 ID\n- 提取码（code）\n- 文件名前缀（prefix）\n- 文件后缀（suffix）\n- 文件大小\n- 创建时间\n- 过期时间\n- 剩余下载次数\n\n### 搜索文件\n\n使用搜索功能可以快速找到特定文件：\n\n1. 在搜索框中输入关键词\n2. 系统会根据文件名前缀（prefix）进行模糊匹配\n3. 搜索结果实时更新\n\n**搜索示例：**\n- 输入 `report` 可以找到所有文件名包含 \"report\" 的文件\n- 输入 `.pdf` 可以找到所有 PDF 文件（如果文件名包含此字符串）\n\n### 分页浏览\n\n文件列表支持分页显示：\n\n| 参数 | 默认值 | 说明 |\n|------|--------|------|\n| `page` | `1` | 当前页码 |\n| `size` | `10` | 每页显示数量 |\n\n### 删除文件\n\n管理员可以删除任意文件：\n\n1. 在文件列表中找到要删除的文件\n2. 点击删除按钮\n3. 确认删除操作\n\n::: danger 警告\n删除操作不可恢复！文件将从存储后端永久删除，同时删除数据库中的记录。\n:::\n\n**删除流程：**\n1. 系统首先从存储后端（本地/S3/OneDrive 等）删除实际文件\n2. 然后从数据库中删除文件记录\n3. 删除后，对应的提取码将失效\n\n### 下载文件\n\n管理员可以直接下载任意文件：\n\n1. 在文件列表中找到目标文件\n2. 点击下载按钮\n3. 文件将通过浏览器下载\n\n对于文本分享，系统会直接返回文本内容而不是下载文件。\n\n### 修改文件信息\n\n管理员可以修改已分享文件的部分信息：\n\n| 可修改字段 | 说明 |\n|------------|------|\n| `code` | 提取码（必须唯一，不能与其他文件重复） |\n| `prefix` | 文件名前缀 |\n| `suffix` | 文件后缀名 |\n| `expired_at` | 过期时间 |\n| `expired_count` | 剩余下载次数 |\n\n**修改提取码：**\n```\n原提取码：abc123\n新提取码：myfile2024\n```\n\n::: warning 注意\n修改提取码时，系统会检查新提取码是否已被使用。如果已存在相同的提取码，修改将失败。\n:::\n\n## 本地文件管理\n\n除了管理已分享的文件，管理面板还提供了本地文件管理功能，用于管理 `data/local` 目录中的文件。\n\n### 查看本地文件\n\n本地文件列表显示 `data/local` 目录中的所有文件：\n\n| 信息 | 说明 |\n|------|------|\n| 文件名 | 文件的完整名称 |\n| 创建时间 | 文件的创建时间 |\n| 文件大小 | 文件大小（字节） |\n\n### 分享本地文件\n\n可以将本地文件快速分享：\n\n1. 在本地文件列表中选择要分享的文件\n2. 设置过期方式和过期值\n3. 点击分享按钮\n4. 系统生成提取码\n\n**分享参数：**\n\n| 参数 | 说明 |\n|------|------|\n| `filename` | 要分享的文件名 |\n| `expire_style` | 过期方式（day/hour/minute/forever/count） |\n| `expire_value` | 过期值（天数/小时数/分钟数/下载次数） |\n\n### 删除本地文件\n\n可以删除 `data/local` 目录中的文件：\n\n1. 在本地文件列表中找到要删除的文件\n2. 点击删除按钮\n3. 确认删除\n\n::: tip 使用场景\n本地文件管理功能适用于：\n- 批量上传文件到服务器后进行分享\n- 管理通过其他方式上传到服务器的文件\n- 清理不需要的本地文件\n:::\n\n## 系统设置\n\n### 查看配置\n\n在系统设置页面可以查看当前所有配置项的值。配置项按类别分组显示：\n\n- 基础设置（站点名称、描述等）\n- 上传设置（文件大小限制、频率限制等）\n- 存储设置（存储类型、路径等）\n- 主题设置（主题选择、透明度等）\n- 安全设置（管理员密码、错误限制等）\n\n### 修改配置\n\n管理员可以通过管理面板修改大部分配置：\n\n1. 进入系统设置页面\n2. 找到要修改的配置项\n3. 输入新的值\n4. 点击保存按钮\n\n**可修改的配置项：**\n\n| 类别 | 配置项示例 |\n|------|------------|\n| 基础设置 | `name`, `description`, `keywords`, `notify_title`, `notify_content` |\n| 上传设置 | `uploadSize`, `uploadMinute`, `uploadCount`, `openUpload`, `enableChunk` |\n| 过期设置 | `expireStyle`, `max_save_seconds` |\n| 主题设置 | `themesSelect`, `opacity`, `background` |\n| 安全设置 | `admin_token`, `showAdminAddr`, `errorMinute`, `errorCount` |\n| 存储设置 | `file_storage`, `storage_path` 及各存储后端的配置 |\n\n::: warning 注意\n- `admin_token`（管理员密码）不能设置为空\n- `themesChoices`（主题列表）不可通过管理面板修改\n- 修改存储设置后，已有文件不会自动迁移\n:::\n\n### 配置生效\n\n配置修改后立即生效，无需重启服务。配置保存在数据库中，重启后仍然有效。\n\n**配置存储位置：**\n- 数据库：`data/filecodebox.db`\n- 表名：`keyvalue`\n- 键名：`settings`\n\n## API 接口\n\n管理面板的所有功能都通过 REST API 实现，以下是主要接口：\n\n### 认证接口\n\n**登录**\n```\nPOST /admin/login\nContent-Type: application/json\n\n{\n    \"password\": \"your-admin-password\"\n}\n```\n\n响应：\n```json\n{\n    \"code\": 200,\n    \"detail\": {\n        \"token\": \"eyJhbGciOiJIUzI1NiIs...\",\n        \"token_type\": \"Bearer\"\n    }\n}\n```\n\n### 仪表盘接口\n\n**获取统计数据**\n```\nGET /admin/dashboard\nAuthorization: Bearer <token>\n```\n\n### 文件管理接口\n\n**获取文件列表**\n```\nGET /admin/file/list?page=1&size=10&keyword=\nAuthorization: Bearer <token>\n```\n\n**删除文件**\n```\nDELETE /admin/file/delete\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"id\": 123\n}\n```\n\n**下载文件**\n```\nGET /admin/file/download?id=123\nAuthorization: Bearer <token>\n```\n\n**修改文件信息**\n```\nPATCH /admin/file/update\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"id\": 123,\n    \"code\": \"newcode\",\n    \"expired_at\": \"2024-12-31T23:59:59\"\n}\n```\n\n### 本地文件接口\n\n**获取本地文件列表**\n```\nGET /admin/local/lists\nAuthorization: Bearer <token>\n```\n\n**删除本地文件**\n```\nDELETE /admin/local/delete\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"filename\": \"example.txt\"\n}\n```\n\n**分享本地文件**\n```\nPOST /admin/local/share\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"filename\": \"example.txt\",\n    \"expire_style\": \"day\",\n    \"expire_value\": 7\n}\n```\n\n### 配置接口\n\n**获取配置**\n```\nGET /admin/config/get\nAuthorization: Bearer <token>\n```\n\n**更新配置**\n```\nPATCH /admin/config/update\nAuthorization: Bearer <token>\nContent-Type: application/json\n\n{\n    \"admin_token\": \"new-password\",\n    \"uploadSize\": 52428800\n}\n```\n\n## 常见问题\n\n### 忘记管理员密码\n\n如果忘记了管理员密码，可以通过以下方式重置：\n\n1. 停止 FileCodeBox 服务\n2. 使用 SQLite 工具打开 `data/filecodebox.db`\n3. 查询 `keyvalue` 表中 `key='settings'` 的记录\n4. 修改 JSON 中的 `admin_token` 值\n5. 重启服务\n\n```sql\n-- 查看当前配置\nSELECT * FROM keyvalue WHERE key = 'settings';\n\n-- 或者删除配置，恢复默认密码\nDELETE FROM keyvalue WHERE key = 'settings';\n```\n\n### 文件删除失败\n\n如果删除文件时出现错误，可能的原因：\n\n1. **存储后端连接失败**：检查存储配置是否正确\n2. **文件已不存在**：文件可能已被手动删除\n3. **权限不足**：检查存储目录的写入权限\n\n### 配置修改不生效\n\n如果修改配置后没有生效：\n\n1. 检查是否点击了保存按钮\n2. 刷新页面查看配置是否已保存\n3. 检查浏览器控制台是否有错误信息\n4. 确认配置值的格式是否正确（如数字类型不要输入字符串）\n\n## 下一步\n\n- [配置说明](/guide/configuration) - 了解所有配置选项的详细说明\n- [安全设置](/guide/security) - 了解如何增强系统安全性\n- [存储配置](/guide/storage) - 配置不同的存储后端\n"
  },
  {
    "path": "docs/guide/security.md",
    "content": "# 安全设置\n\nFileCodeBox 提供了多层安全机制来保护您的文件分享服务。本文档介绍如何正确配置安全选项，确保系统安全运行。\n\n## 管理员密码\n\n### 修改默认密码\n\n::: danger 重要安全警告\nFileCodeBox 的默认管理员密码是 `FileCodeBox2023`。**在生产环境中必须立即修改此密码！**使用默认密码会导致任何人都可以访问您的管理面板。\n:::\n\n修改管理员密码有两种方式：\n\n**方式一：通过管理面板修改（推荐）**\n\n1. 访问 `/admin` 进入管理面板\n2. 使用当前密码登录\n3. 进入「系统设置」页面\n4. 找到 `admin_token` 配置项\n5. 输入新的安全密码并保存\n\n**方式二：通过数据库修改**\n\n配置存储在 `data/filecodebox.db` 数据库的 `keyvalue` 表中，可以直接修改 `admin_token` 的值。\n\n### 密码安全建议\n\n- 使用至少 16 个字符的强密码\n- 包含大小写字母、数字和特殊字符\n- 避免使用常见词汇或个人信息\n- 定期更换密码\n\n```python\n# 推荐的密码格式示例\n\"admin_token\": \"Xk9#mP2$vL5@nQ8&wR3\"\n```\n\n### 隐藏管理入口\n\n默认情况下，管理面板入口是隐藏的。您可以通过 `showAdminAddr` 配置控制是否在首页显示管理入口：\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `showAdminAddr` | int | `0` | 是否显示管理入口（1=显示，0=隐藏） |\n\n::: tip 建议\n在公开服务中，建议保持 `showAdminAddr` 为 `0`，通过直接访问 `/admin` 路径进入管理面板。\n:::\n\n## IP 速率限制\n\nFileCodeBox 内置了基于 IP 的速率限制机制，可以有效防止滥用和攻击。\n\n### 上传频率限制\n\n限制单个 IP 在指定时间内的上传次数：\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `uploadMinute` | int | `1` | 上传限制的时间窗口（分钟） |\n| `uploadCount` | int | `10` | 在时间窗口内允许的最大上传次数 |\n\n**工作原理：**\n- 系统记录每个 IP 的上传请求\n- 当某 IP 在 `uploadMinute` 分钟内的上传次数达到 `uploadCount` 时\n- 该 IP 的后续上传请求将被拒绝，返回 HTTP 423 错误\n- 等待时间窗口过期后，计数器重置\n\n**配置示例：**\n\n```python\n# 宽松配置：5分钟内最多上传20次\n{\n    \"uploadMinute\": 5,\n    \"uploadCount\": 20\n}\n\n# 严格配置：1分钟内最多上传3次\n{\n    \"uploadMinute\": 1,\n    \"uploadCount\": 3\n}\n```\n\n### 错误次数限制\n\n限制单个 IP 的错误尝试次数，防止暴力破解提取码：\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `errorMinute` | int | `1` | 错误限制的时间窗口（分钟） |\n| `errorCount` | int | `1` | 在时间窗口内允许的最大错误次数 |\n\n**工作原理：**\n- 当用户输入错误的提取码时，系统记录该 IP 的错误次数\n- 当错误次数达到 `errorCount` 时，该 IP 将被暂时锁定\n- 锁定时间为 `errorMinute` 分钟\n- 锁定期间，该 IP 的所有提取请求都将被拒绝\n\n**配置示例：**\n\n```python\n# 防暴力破解配置：5分钟内最多允许3次错误\n{\n    \"errorMinute\": 5,\n    \"errorCount\": 3\n}\n```\n\n::: warning 注意\n默认配置 `errorMinute=1, errorCount=1` 非常严格，意味着输入一次错误的提取码后需要等待1分钟才能重试。根据实际需求调整此配置。\n:::\n\n## 上传限制\n\n### 文件大小限制\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `uploadSize` | int | `10485760` | 单文件最大上传大小（字节），默认 10MB |\n| `openUpload` | int | `1` | 是否开启上传功能（1=开启，0=关闭） |\n\n**常用大小换算：**\n- 10MB = 10 * 1024 * 1024 = `10485760`\n- 50MB = 50 * 1024 * 1024 = `52428800`\n- 100MB = 100 * 1024 * 1024 = `104857600`\n- 1GB = 1024 * 1024 * 1024 = `1073741824`\n\n### 文件过期设置\n\n通过文件过期机制，可以自动清理过期文件，减少存储占用和安全风险：\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `expireStyle` | list | `[\"day\",\"hour\",\"minute\",\"forever\",\"count\"]` | 可选的过期方式 |\n| `max_save_seconds` | int | `0` | 文件最大保存时间（秒），0 表示不限制 |\n\n**过期方式说明：**\n- `day` - 按天数过期\n- `hour` - 按小时过期\n- `minute` - 按分钟过期\n- `forever` - 永不过期（需要字符串提取码）\n- `count` - 按下载次数过期\n\n**安全建议：**\n\n对于公开服务，建议：\n1. 移除 `forever` 选项，避免文件永久存储\n2. 设置 `max_save_seconds` 限制最长保存时间\n3. 优先使用 `count` 方式，下载后自动删除\n\n```python\n# 公开服务推荐配置\n{\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400  # 最长保存1天\n}\n```\n\n### 关闭上传功能\n\n在某些情况下，您可能需要临时关闭上传功能：\n\n```python\n{\n    \"openUpload\": 0  # 关闭上传功能\n}\n```\n\n## 反向代理安全配置\n\n在生产环境中，通常会使用 Nginx 或其他反向代理服务器。以下是安全配置建议：\n\n### Nginx 配置示例\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain.com;\n    \n    # 强制 HTTPS 重定向\n    return 301 https://$server_name$request_uri;\n}\n\nserver {\n    listen 443 ssl http2;\n    server_name your-domain.com;\n    \n    # SSL 证书配置\n    ssl_certificate /path/to/cert.pem;\n    ssl_certificate_key /path/to/key.pem;\n    ssl_protocols TLSv1.2 TLSv1.3;\n    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;\n    ssl_prefer_server_ciphers on;\n    \n    # 安全头部\n    add_header X-Frame-Options \"SAMEORIGIN\" always;\n    add_header X-Content-Type-Options \"nosniff\" always;\n    add_header X-XSS-Protection \"1; mode=block\" always;\n    add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;\n    \n    # 限制请求体大小（与 uploadSize 配置一致）\n    client_max_body_size 100M;\n    \n    # 传递真实 IP\n    location / {\n        proxy_pass http://127.0.0.1:12345;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n    \n    # 静态资源缓存\n    location /assets {\n        proxy_pass http://127.0.0.1:12345;\n        proxy_cache_valid 200 7d;\n        add_header Cache-Control \"public, max-age=604800\";\n    }\n}\n```\n\n### 关键安全配置说明\n\n**1. 传递真实 IP**\n\nFileCodeBox 的 IP 限制功能依赖于获取客户端真实 IP。系统会按以下顺序获取 IP：\n1. `X-Real-IP` 请求头\n2. `X-Forwarded-For` 请求头\n3. 直接连接的客户端 IP\n\n确保反向代理正确设置这些头部：\n\n```nginx\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n```\n\n**2. 请求体大小限制**\n\nNginx 的 `client_max_body_size` 应该与 FileCodeBox 的 `uploadSize` 配置一致或略大：\n\n```nginx\nclient_max_body_size 100M;  # 允许上传最大 100MB\n```\n\n**3. HTTPS 加密**\n\n强烈建议在生产环境中启用 HTTPS：\n- 保护用户上传的文件内容\n- 保护管理员登录凭据\n- 防止中间人攻击\n\n### Caddy 配置示例\n\n```nginx\nyour-domain.com {\n    reverse_proxy localhost:12345\n    \n    header {\n        X-Frame-Options \"SAMEORIGIN\"\n        X-Content-Type-Options \"nosniff\"\n        X-XSS-Protection \"1; mode=block\"\n        Strict-Transport-Security \"max-age=31536000; includeSubDomains\"\n    }\n}\n```\n\n## 安全检查清单\n\n部署 FileCodeBox 前，请确认以下安全配置：\n\n- [ ] 已修改默认管理员密码 `admin_token`\n- [ ] 已隐藏管理入口 `showAdminAddr: 0`\n- [ ] 已配置合适的上传频率限制\n- [ ] 已配置错误次数限制防止暴力破解\n- [ ] 已设置合理的文件大小限制\n- [ ] 已配置文件过期策略\n- [ ] 已启用 HTTPS 加密\n- [ ] 反向代理已正确传递真实 IP\n- [ ] 已设置安全响应头部\n\n## 推荐安全配置\n\n### 公开服务配置\n\n```python\n{\n    \"admin_token\": \"your-very-secure-password\",\n    \"showAdminAddr\": 0,\n    \"uploadSize\": 10485760,           # 10MB\n    \"uploadMinute\": 1,\n    \"uploadCount\": 5,\n    \"errorMinute\": 5,\n    \"errorCount\": 3,\n    \"expireStyle\": [\"hour\", \"minute\", \"count\"],\n    \"max_save_seconds\": 86400,        # 最长1天\n    \"openUpload\": 1\n}\n```\n\n### 内部服务配置\n\n```python\n{\n    \"admin_token\": \"internal-secure-password\",\n    \"showAdminAddr\": 1,\n    \"uploadSize\": 104857600,          # 100MB\n    \"uploadMinute\": 5,\n    \"uploadCount\": 50,\n    \"errorMinute\": 1,\n    \"errorCount\": 5,\n    \"expireStyle\": [\"day\", \"hour\", \"forever\"],\n    \"max_save_seconds\": 0,            # 不限制\n    \"openUpload\": 1\n}\n```\n\n## 下一步\n\n- [配置说明](/guide/configuration) - 了解所有配置选项\n- [存储配置](/guide/storage) - 配置安全的存储后端\n- [文件分享](/guide/share) - 了解文件分享功能\n"
  },
  {
    "path": "docs/guide/share.md",
    "content": "# 文件分享\n\nFileCodeBox 提供了简单易用的文件和文本分享功能。用户可以通过提取码安全地分享和获取文件。\n\n## 分享方式\n\nFileCodeBox 支持两种分享方式：\n\n1. **文本分享** - 直接分享文本内容，适合代码片段、配置文件等\n2. **文件分享** - 上传文件进行分享，支持各种文件格式\n\n## 文本分享\n\n### 使用方法\n\n1. 在首页选择「文本分享」标签\n2. 在文本框中输入或粘贴要分享的内容\n3. 选择过期方式和时间\n4. 点击「分享」按钮\n5. 获取提取码\n\n### 文本大小限制\n\n::: warning 注意\n文本分享的最大内容大小为 **222KB**（227,328 字节）。如果内容超过此限制，建议使用文件分享方式。\n:::\n\n文本内容大小按 UTF-8 编码计算，中文字符通常占用 3 个字节。\n\n### API 接口\n\n**POST** `/share/text/`\n\n请求参数：\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `text` | string | 是 | 要分享的文本内容 |\n| `expire_value` | int | 否 | 过期数值，默认 1 |\n| `expire_style` | string | 否 | 过期方式，默认 `day` |\n\n响应示例：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\"\n  }\n}\n```\n\n## 文件分享\n\n### 使用方法\n\n1. 在首页选择「文件分享」标签\n2. 点击上传区域或拖拽文件到上传区域\n3. 选择过期方式和时间\n4. 点击「上传」按钮\n5. 获取提取码\n\n### 文件大小限制\n\n默认单文件最大上传大小为 **10MB**。管理员可以通过 `uploadSize` 配置项修改此限制。\n\n::: tip 提示\n如果需要上传大文件，请联系管理员启用分片上传功能，或调整 `uploadSize` 配置。\n:::\n\n### 支持的上传方式\n\n- **点击上传** - 点击上传区域选择文件\n- **拖拽上传** - 将文件拖拽到上传区域\n- **粘贴上传** - 从剪贴板粘贴图片（部分主题支持）\n\n### API 接口\n\n**POST** `/share/file/`\n\n请求参数（multipart/form-data）：\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `file` | file | 是 | 要上传的文件 |\n| `expire_value` | int | 否 | 过期数值，默认 1 |\n| `expire_style` | string | 否 | 过期方式，默认 `day` |\n\n响应示例：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"654321\",\n    \"name\": \"example.pdf\"\n  }\n}\n```\n\n## 过期设置\n\nFileCodeBox 支持多种灵活的过期方式：\n\n| 过期方式 | 参数值 | 说明 |\n|----------|--------|------|\n| 按天过期 | `day` | 文件在指定天数后过期 |\n| 按小时过期 | `hour` | 文件在指定小时后过期 |\n| 按分钟过期 | `minute` | 文件在指定分钟后过期 |\n| 永不过期 | `forever` | 文件永久有效 |\n| 按次数过期 | `count` | 文件在被下载指定次数后过期 |\n\n::: info 说明\n- 管理员可以通过 `expireStyle` 配置项控制用户可选的过期方式\n- 管理员可以通过 `max_save_seconds` 配置项限制文件的最长保存时间\n:::\n\n### 过期方式示例\n\n```bash\n# 文件 3 天后过期\nexpire_value=3, expire_style=day\n\n# 文件 12 小时后过期\nexpire_value=12, expire_style=hour\n\n# 文件 30 分钟后过期\nexpire_value=30, expire_style=minute\n\n# 文件永不过期\nexpire_value=1, expire_style=forever\n\n# 文件被下载 5 次后过期\nexpire_value=5, expire_style=count\n```\n\n## 提取文件\n\n### 使用方法\n\n1. 在首页的「提取文件」区域输入提取码\n2. 点击「提取」按钮\n3. 系统会显示文件信息（文件名、大小等）\n4. 点击「下载」按钮下载文件，或直接查看文本内容\n\n### 提取码说明\n\n- 提取码通常为 **6 位数字**\n- 永不过期的文件使用 **字母数字混合** 的提取码\n- 提取码区分大小写（针对字母数字混合的情况）\n\n### API 接口\n\n**查询文件信息**\n\n**POST** `/share/select/`\n\n请求参数：\n\n```json\n{\n  \"code\": \"123456\"\n}\n```\n\n响应示例（文件）：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"example.pdf\",\n    \"size\": 1048576,\n    \"text\": \"https://example.com/download/...\"\n  }\n}\n```\n\n响应示例（文本）：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"123456\",\n    \"name\": \"Text\",\n    \"size\": 1024,\n    \"text\": \"这是分享的文本内容...\"\n  }\n}\n```\n\n**直接下载文件**\n\n**GET** `/share/select/?code=123456`\n\n此接口会直接返回文件内容，适合在浏览器中直接访问。\n\n## 分片上传（大文件）\n\n对于大文件上传，FileCodeBox 支持分片上传功能。此功能需要管理员启用（`enableChunk=1`）。\n\n### 分片上传流程\n\n```mermaid\nsequenceDiagram\n    participant C as 客户端\n    participant S as 服务器\n    \n    C->>S: 1. 初始化上传 (POST /chunk/upload/init/)\n    S-->>C: 返回 upload_id 和分片信息\n    \n    loop 每个分片\n        C->>S: 2. 上传分片 (POST /chunk/upload/chunk/{upload_id}/{chunk_index})\n        S-->>C: 返回分片哈希\n    end\n    \n    C->>S: 3. 完成上传 (POST /chunk/upload/complete/{upload_id})\n    S-->>C: 返回提取码\n```\n\n### 1. 初始化上传\n\n**POST** `/chunk/upload/init/`\n\n请求参数：\n\n```json\n{\n  \"file_name\": \"large_file.zip\",\n  \"file_size\": 104857600,\n  \"chunk_size\": 5242880,\n  \"file_hash\": \"sha256_hash_of_file\"\n}\n```\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `file_name` | string | 是 | 文件名 |\n| `file_size` | int | 是 | 文件总大小（字节） |\n| `chunk_size` | int | 否 | 分片大小，默认 5MB |\n| `file_hash` | string | 是 | 文件的 SHA256 哈希值 |\n\n响应示例：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"existed\": false,\n    \"upload_id\": \"abc123def456\",\n    \"chunk_size\": 5242880,\n    \"total_chunks\": 20,\n    \"uploaded_chunks\": []\n  }\n}\n```\n\n### 2. 上传分片\n\n**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`\n\n- `upload_id` - 初始化时返回的上传会话 ID\n- `chunk_index` - 分片索引，从 0 开始\n\n请求体：分片文件数据（multipart/form-data）\n\n响应示例：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"chunk_hash\": \"sha256_hash_of_chunk\"\n  }\n}\n```\n\n### 3. 完成上传\n\n**POST** `/chunk/upload/complete/{upload_id}`\n\n请求参数：\n\n```json\n{\n  \"expire_value\": 1,\n  \"expire_style\": \"day\"\n}\n```\n\n响应示例：\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"789012\",\n    \"name\": \"large_file.zip\"\n  }\n}\n```\n\n### 断点续传\n\n分片上传支持断点续传。如果上传中断，可以：\n\n1. 重新调用初始化接口，使用相同的 `file_hash`\n2. 服务器会返回已上传的分片列表 `uploaded_chunks`\n3. 客户端只需上传未完成的分片\n\n## 错误处理\n\n### 常见错误码\n\n| 错误码 | 说明 | 解决方案 |\n|--------|------|----------|\n| 403 | 文件大小超过限制 | 减小文件大小或联系管理员调整限制 |\n| 403 | 内容过多 | 文本超过 222KB，请使用文件分享 |\n| 403 | 上传频率限制 | 等待一段时间后重试 |\n| 404 | 文件不存在 | 检查提取码是否正确 |\n| 404 | 文件已过期 | 文件已过期或下载次数已用完 |\n\n### 频率限制\n\n为防止滥用，系统对上传和提取操作有频率限制：\n\n- **上传限制**：默认每分钟最多 10 次上传\n- **错误限制**：默认每分钟最多 1 次错误尝试\n\n::: tip 提示\n如果遇到频率限制，请等待限制时间窗口过后再重试。\n:::\n\n## 下一步\n\n- [配置说明](/guide/configuration) - 了解如何配置分享相关设置\n- [存储配置](/guide/storage) - 了解文件存储方式\n- [安全设置](/guide/security) - 了解安全相关配置\n- [管理面板](/guide/management) - 了解如何管理分享的文件\n"
  },
  {
    "path": "docs/guide/storage-onedrive.md",
    "content": "# OneDrive作为存储的配置方法\n\n**仅支持工作或学校账户，并且需要有管理员权限以授权API**\n\n## 1. 需要配置的参数\n\n```\nfile_storage=onedrive\nonedrive_domain=XXXXXX\nonedrive_client_id=XXXXXX-XXXXXX-XXXXXX-XXXXXX\nonedrive_username=XXXXXX@XXXXXX\nonedrive_password=XXXXXX\n```\n\n`onedrive_username`和`onedrive_password`是你的账户名（邮箱）和密码，另外两个参数需要在[微软Azure门户](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)中注册应用后获取。\n\n## 2. 应用注册\n\n1. 登录[https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)，鼠标置于右上角账号处，浮窗将显示的`域`即为`onedrive_domain`的值。\n![onedrive_domain](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGCiErO85doq9Tcu/root/content)\n\n2. 点击左上角的`+新注册`，输入名称，\n  * 受支持的帐户类型：选择任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户(例如，Skype、Xbox)\n  * 重定向 URI (可选)：选择`Web`，并输入`http://localhost`\n\n3. 完成注册后进入概述页面，在概要中找到`应用程序(客户端)ID`，即为`onedrive_client_id`的值。\n![onedrive_client_id](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGHD4CNyJxm_QBb8/root/content)\n\n4. 此时还需要配置允许公共客户端流和API权限\n  * 在左侧选择`身份验证`，找到`允许的客户端流`，选择`是`，并**点击`保存`**。\n  ![允许的客户端流](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGJQMOlOCb2-L0Lh/root/content)\n  * 在左侧选择`API权限`，点击`+添加权限`，选择`Microsoft Graph`->`委托的权限`，并勾选下述权限：openid、Files中所有权限、User.Read，如下图所示。最后**点击下方的`添加权限`**。\n  ![添加权限](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGOZzz7sIrdXkD4w/root/content)\n  * 最后点击`授予管理员同意`，并**点击`是`**，最终状态变为`已授予`。\n  ![授予管理员同意](https://api.onedrive.com/v1.0/shares/s!Au-BDzXcM6_VmGSOAnjnHUlbirbU/root/content)\n\n## 3. 使用下述代码测试是否配置成功\n\n安装依赖：`pip install Office365-REST-Python-Client`\n\n```python\n# common.py\nimport msal\ndomain = 'XXXXXX'\nclient_id = 'XXXXXX'\nusername = 'XXXXXX'\npassword = 'XXXXXX'\n\ndef acquire_token_pwd():\n    authority_url = f'https://login.microsoftonline.com/{domain}'\n    app = msal.PublicClientApplication(\n        authority=authority_url,\n        client_id=client_id\n    )\n    result = app.acquire_token_by_username_password(\n        username=username,\n        password=password,\n        scopes=['https://graph.microsoft.com/.default']\n    )\n    return result\n```\n\n测试登录，如果成功打印出账户名，说明配置成功。\n\n```python\nfrom common import acquire_token_pwd\n\nfrom office365.graph_client import GraphClient\ntry:\n    client = GraphClient(acquire_token_pwd)\n    me = client.me.get().execute_query()\n    print(me.user_principal_name)\nexcept Exception as e:\n    print(e)\n```\n\n测试文件上传\n\n```python\nimport os\nfrom office365.graph_client import GraphClient\nfrom common import acquire_token_pwd\n\nremote_path = 'tmp'\nlocal_path = '.tmp/1689843925000.png'\n\ndef convert_link_to_download_link(link):\n    import re\n    p1 = re.search(r'https:\\/\\/(.+)\\.sharepoint\\.com', link).group(1)\n    p2 = re.search(r'personal\\/(.+)\\/', link).group(1)\n    p3 = re.search(rf'{p2}\\/(.+)', link).group(1)\n    return f'https://{p1}.sharepoint.com/personal/{p2}/_layouts/52/download.aspx?share={p3}'\n\nclient = GraphClient(acquire_token_pwd)\nfolder = client.me.drive.root.get_by_path(remote_path)\n# 1. upload\nfile = folder.upload_file(local_path).execute_query()\nprint(f'File {file.web_url} has been uploaded')\n# 2. create sharing link\nremote_file = folder.get_by_path(os.path.basename(local_path))\npermission = remote_file.create_link(\"view\", \"anonymous\").execute_query()\nprint(f\"sharing link: {convert_link_to_download_link(permission.link.webUrl)}\")\n```\n\n测试文件下载\n\n```python\nimport os\nfrom office365.graph_client import GraphClient\nfrom common import acquire_token_pwd\n\nremote_path = 'tmp/1689843925000.png'\nlocal_path = '.tmp'\nif not os.path.exists(local_path):\n    os.makedirs(local_path)\n\nclient = GraphClient(acquire_token_pwd)\nremote_file = client.me.drive.root.get_by_path(remote_path).get().execute_query()\nwith open(os.path.join(local_path, os.path.basename(remote_path)), 'wb') as local_file:\n    remote_file.download(local_file).execute_query()\n    print(f'{remote_file.name} has been downloaded into {local_file.name}')\n```\n\n测试删除文件\n\n```python\nfrom office365.graph_client import GraphClient\nfrom common import acquire_token_pwd\n\nremote_path = 'tmp/1689843925000.png'\n\nclient = GraphClient(acquire_token_pwd)\nfile = client.me.drive.root.get_by_path(remote_path)\nfile.delete_object().execute_query()\n```\n"
  },
  {
    "path": "docs/guide/storage-opendal.md",
    "content": "# 通过 OpenDAL 集成存储的配置方法\n\n## 需要配置的参数\n\n```dotenv\nfile_storage=opendal\nopendal_scheme=<service_name>\nopendal_<service_name>_<service_setting>=...\n```\n\n以 Gcs 为例，需要配置的参数如下：\n```dotenv\nfile_storage=opendal\nopendal_scheme=gcs\nopendal_gcs_root=<root>\nopendal_gcs_bucket=<bucket_name>\nopendal_gcs_credential=<base64_credential>\n```\n\n所有支持的服务可以在[此处](https://opendal.apache.org/docs/rust/opendal/services/index.html)查看。\n具体服务的配置参数与 OpenDAL 文档一致。\n\n## 补充说明\n\n通过 OpenDAL 集成的服务均通过服务器中转下载。因此，每次下载既消耗存储服务的流量，也消耗服务器的流量。\n\nOpenDAL 和该项目本身都支持本地存储、`s3`、`onedrive`。不同之处有以下几点:\n1. 项目的支持通过预签名实现，不消耗服务器流量。而 OpenDAL 通过服务器中转下载，消耗服务器流量。（本地存储除外）\n2. 项目的支持对于异常情况可能会有更多的调试信息，方便排查问题。\n3. OpenDAL 项目本身采用 Rust 编写，性能更好。"
  },
  {
    "path": "docs/guide/storage.md",
    "content": "# 存储配置\n\nFileCodeBox 支持多种存储后端，您可以根据需求选择合适的存储方式。本文档将详细介绍各种存储后端的配置方法。\n\n## 存储类型概览\n\n| 存储类型 | 配置值 | 说明 |\n|---------|--------|------|\n| 本地存储 | `local` | 默认存储方式，文件保存在服务器本地 |\n| S3 兼容存储 | `s3` | 支持 AWS S3、阿里云 OSS、MinIO 等 |\n| OneDrive | `onedrive` | 微软 OneDrive 云存储（仅支持工作/学校账户） |\n| WebDAV | `webdav` | 支持 WebDAV 协议的存储服务 |\n| OpenDAL | `opendal` | 通过 OpenDAL 集成更多存储服务 |\n\n## 本地存储\n\n本地存储是默认的存储方式，文件将保存在服务器的 `data/` 目录下。\n\n### 配置参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `file_storage` | string | `local` | 存储类型 |\n| `storage_path` | string | `\"\"` | 自定义存储路径（可选） |\n\n### 配置示例\n\n```bash\nfile_storage=local\nstorage_path=\n```\n\n### 说明\n\n- 文件默认存储在 `data/share/data/` 目录下\n- 按日期自动创建子目录：`年/月/日/文件ID/`\n- 建议在生产环境中将 `data/` 目录挂载到持久化存储\n\n## S3 兼容存储\n\n支持所有 S3 兼容的对象存储服务，包括 AWS S3、阿里云 OSS、MinIO、腾讯云 COS 等。\n\n### 配置参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `file_storage` | string | - | 设置为 `s3` |\n| `s3_access_key_id` | string | `\"\"` | Access Key ID |\n| `s3_secret_access_key` | string | `\"\"` | Secret Access Key |\n| `s3_bucket_name` | string | `\"\"` | 存储桶名称 |\n| `s3_endpoint_url` | string | `\"\"` | S3 端点 URL |\n| `s3_region_name` | string | `auto` | 区域名称 |\n| `s3_signature_version` | string | `s3v2` | 签名版本（`s3v2` 或 `s3v4`） |\n| `s3_hostname` | string | `\"\"` | S3 主机名（备用） |\n| `s3_proxy` | int | `0` | 是否通过服务器代理下载（1=是，0=否） |\n| `aws_session_token` | string | `\"\"` | AWS 会话令牌（可选） |\n\n### AWS S3 配置示例\n\n```bash\nfile_storage=s3\ns3_access_key_id=AKIAIOSFODNN7EXAMPLE\ns3_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\ns3_bucket_name=my-filecodebox-bucket\ns3_endpoint_url=https://s3.amazonaws.com\ns3_region_name=us-east-1\ns3_signature_version=s3v4\n```\n\n### 阿里云 OSS 配置示例\n\n```bash\nfile_storage=s3\ns3_access_key_id=您的AccessKeyId\ns3_secret_access_key=您的SecretAccessKey\ns3_bucket_name=bucket-name\ns3_endpoint_url=https://bucket-name.oss-cn-hangzhou.aliyuncs.com\ns3_region_name=oss-cn-hangzhou\ns3_signature_version=s3v4\n```\n\n::: tip 阿里云 OSS 端点格式\n端点 URL 格式为：`https://<bucket-name>.<region>.aliyuncs.com`\n\n常用区域：\n- 杭州：`oss-cn-hangzhou`\n- 上海：`oss-cn-shanghai`\n- 北京：`oss-cn-beijing`\n- 深圳：`oss-cn-shenzhen`\n:::\n\n### MinIO 配置示例\n\n```bash\nfile_storage=s3\ns3_access_key_id=minioadmin\ns3_secret_access_key=minioadmin\ns3_bucket_name=filecodebox\ns3_endpoint_url=http://localhost:9000\ns3_region_name=us-east-1\ns3_signature_version=s3v4\n```\n\n::: warning MinIO 注意事项\n- `s3_endpoint_url` 填写 MinIO 的 API 接口地址\n- `s3_region_name` 根据 MinIO 配置中的 `Server Location` 设置\n- 确保存储桶已创建且有正确的访问权限\n:::\n\n### 腾讯云 COS 配置示例\n\n```bash\nfile_storage=s3\ns3_access_key_id=您的SecretId\ns3_secret_access_key=您的SecretKey\ns3_bucket_name=bucket-name-1250000000\ns3_endpoint_url=https://cos.ap-guangzhou.myqcloud.com\ns3_region_name=ap-guangzhou\ns3_signature_version=s3v4\n```\n\n### 代理下载\n\n当 `s3_proxy=1` 时，文件下载将通过服务器中转，而不是直接从 S3 下载。这在以下情况下有用：\n\n- S3 存储桶不允许公开访问\n- 需要隐藏实际的存储地址\n- 网络环境限制直接访问 S3\n\n\n\n## OneDrive 存储\n\nOneDrive 存储支持将文件保存到微软 OneDrive 云存储。\n\n::: warning 重要限制\nOneDrive 存储**仅支持工作或学校账户**，并且需要有管理员权限以授权 API。个人账户无法使用此功能。\n:::\n\n### 配置参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `file_storage` | string | - | 设置为 `onedrive` |\n| `onedrive_domain` | string | `\"\"` | Azure AD 域名 |\n| `onedrive_client_id` | string | `\"\"` | 应用程序（客户端）ID |\n| `onedrive_username` | string | `\"\"` | 账户邮箱 |\n| `onedrive_password` | string | `\"\"` | 账户密码 |\n| `onedrive_root_path` | string | `filebox_storage` | OneDrive 中的存储根目录 |\n| `onedrive_proxy` | int | `0` | 是否通过服务器代理下载 |\n\n### 配置示例\n\n```bash\nfile_storage=onedrive\nonedrive_domain=contoso.onmicrosoft.com\nonedrive_client_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\nonedrive_username=user@contoso.onmicrosoft.com\nonedrive_password=your_password\nonedrive_root_path=filebox_storage\n```\n\n### Azure 应用注册步骤\n\n要使用 OneDrive 存储，您需要在 Azure 门户中注册应用程序：\n\n#### 1. 获取域名\n\n登录 [Azure 门户](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)，将鼠标置于右上角账号处，浮窗显示的**域**即为 `onedrive_domain` 的值。\n\n#### 2. 注册应用\n\n1. 点击左上角的 **+ 新注册**\n2. 输入应用名称（如：FileCodeBox）\n3. **受支持的帐户类型**：选择\"任何组织目录(任何 Azure AD 目录 - 多租户)中的帐户和个人 Microsoft 帐户\"\n4. **重定向 URI**：选择 `Web`，输入 `http://localhost`\n5. 点击**注册**\n\n#### 3. 获取客户端 ID\n\n注册完成后，在应用概述页面的**概要**中找到**应用程序(客户端)ID**，即为 `onedrive_client_id` 的值。\n\n#### 4. 配置身份验证\n\n1. 在左侧菜单选择**身份验证**\n2. 找到**允许公共客户端流**，选择**是**\n3. 点击**保存**\n\n#### 5. 配置 API 权限\n\n1. 在左侧菜单选择 **API 权限**\n2. 点击 **+ 添加权限**\n3. 选择 **Microsoft Graph** → **委托的权限**\n4. 勾选以下权限：\n   - `openid`\n   - `Files.Read`\n   - `Files.Read.All`\n   - `Files.ReadWrite`\n   - `Files.ReadWrite.All`\n   - `User.Read`\n5. 点击**添加权限**\n6. 点击**代表 xxx 授予管理员同意**\n7. 确认后，权限状态应显示为**已授予**\n\n### 安装依赖\n\n使用 OneDrive 存储需要安装额外的 Python 依赖：\n\n```bash\npip install msal Office365-REST-Python-Client\n```\n\n### 验证配置\n\n您可以使用以下代码测试配置是否正确：\n\n```python\nimport msal\nfrom office365.graph_client import GraphClient\n\ndomain = 'your_domain'\nclient_id = 'your_client_id'\nusername = 'your_username'\npassword = 'your_password'\n\ndef acquire_token_pwd():\n    authority_url = f'https://login.microsoftonline.com/{domain}'\n    app = msal.PublicClientApplication(\n        authority=authority_url,\n        client_id=client_id\n    )\n    result = app.acquire_token_by_username_password(\n        username=username,\n        password=password,\n        scopes=['https://graph.microsoft.com/.default']\n    )\n    return result\n\n# 测试连接\nclient = GraphClient(acquire_token_pwd)\nme = client.me.get().execute_query()\nprint(f\"登录成功：{me.user_principal_name}\")\n```\n\n## WebDAV 存储\n\nWebDAV 存储支持将文件保存到任何支持 WebDAV 协议的服务，如 Nextcloud、ownCloud、坚果云等。\n\n### 配置参数\n\n| 参数 | 类型 | 默认值 | 说明 |\n|------|------|--------|------|\n| `file_storage` | string | - | 设置为 `webdav` |\n| `webdav_url` | string | `\"\"` | WebDAV 服务器 URL |\n| `webdav_username` | string | `\"\"` | WebDAV 用户名 |\n| `webdav_password` | string | `\"\"` | WebDAV 密码 |\n| `webdav_root_path` | string | `filebox_storage` | WebDAV 中的存储根目录 |\n| `webdav_proxy` | int | `0` | 是否通过服务器代理下载 |\n\n### 配置示例\n\n```bash\nfile_storage=webdav\nwebdav_url=https://dav.example.com/remote.php/dav/files/username/\nwebdav_username=your_username\nwebdav_password=your_password\nwebdav_root_path=filebox_storage\n```\n\n### Nextcloud 配置示例\n\n```bash\nfile_storage=webdav\nwebdav_url=https://your-nextcloud.com/remote.php/dav/files/username/\nwebdav_username=your_username\nwebdav_password=your_app_password\nwebdav_root_path=FileCodeBox\n```\n\n::: tip Nextcloud 应用密码\n建议在 Nextcloud 中创建应用密码，而不是使用主密码：\n1. 登录 Nextcloud\n2. 进入**设置** → **安全**\n3. 在**设备与会话**中创建新的应用密码\n:::\n\n### 坚果云配置示例\n\n```bash\nfile_storage=webdav\nwebdav_url=https://dav.jianguoyun.com/dav/\nwebdav_username=your_email@example.com\nwebdav_password=your_app_password\nwebdav_root_path=FileCodeBox\n```\n\n::: tip 坚果云应用密码\n坚果云需要使用应用密码：\n1. 登录坚果云网页版\n2. 进入**账户信息** → **安全选项**\n3. 添加应用密码\n:::\n\n## OpenDAL 存储\n\nOpenDAL 是一个统一的数据访问层，支持多种存储服务。通过 OpenDAL，您可以使用 Google Cloud Storage、Azure Blob Storage 等更多存储服务。\n\n### 配置参数\n\n| 参数 | 类型 | 说明 |\n|------|------|------|\n| `file_storage` | string | 设置为 `opendal` |\n| `opendal_scheme` | string | 存储服务类型（如 `gcs`、`azblob`） |\n| `opendal_<scheme>_<setting>` | string | 服务特定的配置参数 |\n\n### 安装依赖\n\n```bash\npip install opendal\n```\n\n### Google Cloud Storage 配置示例\n\n```bash\nfile_storage=opendal\nopendal_scheme=gcs\nopendal_gcs_root=/filecodebox\nopendal_gcs_bucket=your-bucket-name\nopendal_gcs_credential=base64_encoded_credential\n```\n\n### Azure Blob Storage 配置示例\n\n```bash\nfile_storage=opendal\nopendal_scheme=azblob\nopendal_azblob_root=/filecodebox\nopendal_azblob_container=your-container\nopendal_azblob_account_name=your_account\nopendal_azblob_account_key=your_key\n```\n\n### 支持的服务\n\nOpenDAL 支持众多存储服务，完整列表请参考 [OpenDAL 官方文档](https://opendal.apache.org/docs/rust/opendal/services/index.html)。\n\n常用服务包括：\n- `gcs` - Google Cloud Storage\n- `azblob` - Azure Blob Storage\n- `obs` - 华为云 OBS\n- `oss` - 阿里云 OSS（通过 OpenDAL）\n- `cos` - 腾讯云 COS（通过 OpenDAL）\n- `hdfs` - Hadoop HDFS\n- `ftp` - FTP 服务器\n- `sftp` - SFTP 服务器\n\n::: warning OpenDAL 注意事项\n1. 通过 OpenDAL 集成的服务均通过服务器中转下载，会同时消耗存储服务和服务器的流量\n2. 相比原生 S3/OneDrive 支持，OpenDAL 方式可能缺少一些调试信息\n3. OpenDAL 采用 Rust 编写，性能较好\n:::\n\n## 存储选择建议\n\n| 场景 | 推荐存储 | 原因 |\n|------|----------|------|\n| 个人/小型部署 | 本地存储 | 简单易用，无需额外配置 |\n| 企业内网 | MinIO + S3 | 自建对象存储，数据可控 |\n| 公有云部署 | 对应云厂商 S3 | 同区域访问快，成本低 |\n| 已有 OneDrive | OneDrive | 利用现有资源 |\n| 已有 WebDAV | WebDAV | 兼容性好 |\n| 特殊存储需求 | OpenDAL | 支持更多存储服务 |\n\n## 常见问题\n\n### S3 上传失败\n\n1. 检查 Access Key 和 Secret Key 是否正确\n2. 确认存储桶名称和区域配置正确\n3. 检查存储桶的访问权限设置\n4. 确认签名版本（`s3v2` 或 `s3v4`）与服务商要求一致\n\n### OneDrive 认证失败\n\n1. 确认使用的是工作/学校账户，而非个人账户\n2. 检查 Azure 应用是否已授予管理员同意\n3. 确认 API 权限配置完整\n4. 验证用户名和密码是否正确\n\n### WebDAV 连接失败\n\n1. 检查 WebDAV URL 格式是否正确\n2. 确认用户名和密码（或应用密码）正确\n3. 检查服务器是否支持 WebDAV 协议\n4. 确认网络连接正常\n"
  },
  {
    "path": "docs/guide/upload.md",
    "content": "# 文件上传\n\nFileCodeBox 提供了多种灵活的文件上传方式，支持普通上传和分片上传，满足不同场景的需求。\n\n## 上传方式\n\nFileCodeBox 支持以下几种上传方式：\n\n### 拖拽上传\n\n将文件直接拖拽到上传区域即可开始上传。这是最便捷的上传方式。\n\n1. 打开 FileCodeBox 首页\n2. 将文件从文件管理器拖拽到上传区域\n3. 松开鼠标，文件开始上传\n4. 上传完成后获取提取码\n\n::: tip 提示\n拖拽上传支持同时拖拽多个文件（取决于主题支持）。\n:::\n\n### 点击上传\n\n点击上传区域，通过系统文件选择器选择文件。\n\n1. 点击上传区域的「选择文件」按钮\n2. 在弹出的文件选择器中选择要上传的文件\n3. 确认选择后文件开始上传\n4. 上传完成后获取提取码\n\n### 粘贴上传\n\n支持从剪贴板直接粘贴图片进行上传（部分主题支持）。\n\n1. 复制图片到剪贴板（截图或复制图片）\n2. 在上传区域使用 `Ctrl+V`（Windows/Linux）或 `Cmd+V`（macOS）粘贴\n3. 图片自动开始上传\n4. 上传完成后获取提取码\n\n::: warning 注意\n粘贴上传仅支持图片格式，不支持其他文件类型。具体支持情况取决于所使用的主题。\n:::\n\n## 文件大小限制\n\n### 默认限制\n\n| 配置项 | 默认值 | 说明 |\n|--------|--------|------|\n| `uploadSize` | 10MB | 单文件最大上传大小 |\n\n### 修改上传限制\n\n管理员可以通过管理面板或配置文件修改上传大小限制：\n\n```python\n# 设置最大上传大小为 100MB\nuploadSize = 104857600  # 100 * 1024 * 1024\n```\n\n::: info 说明\n`uploadSize` 的单位是字节。常用换算：\n- 10MB = 10485760\n- 50MB = 52428800\n- 100MB = 104857600\n- 500MB = 524288000\n- 1GB = 1073741824\n:::\n\n### 超出限制的处理\n\n当上传文件超过大小限制时，系统会返回 403 错误：\n\n```json\n{\n  \"detail\": \"大小超过限制,最大为10.00 MB\"\n}\n```\n\n## 普通上传 API\n\n### 文件上传接口\n\n**POST** `/share/file/`\n\nContent-Type: `multipart/form-data`\n\n**请求参数：**\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `file` | file | 是 | 要上传的文件 |\n| `expire_value` | int | 否 | 过期数值，默认 1 |\n| `expire_style` | string | 否 | 过期方式，默认 `day` |\n\n**过期方式选项：**\n\n| 值 | 说明 |\n|----|------|\n| `day` | 按天过期 |\n| `hour` | 按小时过期 |\n| `minute` | 按分钟过期 |\n| `forever` | 永不过期 |\n| `count` | 按下载次数过期 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"654321\",\n    \"name\": \"example.pdf\"\n  }\n}\n```\n\n**cURL 示例：**\n\n```bash\n# 上传文件（默认1天有效期）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\"\n\n# 上传文件并指定有效期（7天）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\" \\\n  -F \"expire_value=7\" \\\n  -F \"expire_style=day\"\n\n# 上传文件并指定有效期（可下载10次）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.pdf\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n\n# 分享文本\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=这是要分享的文本内容\"\n\n# 通过取件码下载文件\ncurl -L \"http://localhost:12345/share/select/?code=取件码\" -o downloaded_file\n```\n\n::: tip 需要认证时\n如果管理面板关闭了游客上传（`openUpload=0`），需要先登录获取 token：\n\n```bash\n# 1. 登录获取 token\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n\n# 返回: {\"code\":200,\"msg\":\"success\",\"detail\":{\"token\":\"xxx.xxx.xxx\",\"token_type\":\"Bearer\"}}\n\n# 2. 使用 token 上传文件\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"file=@/path/to/file.pdf\"\n\n# 3. 使用 token 分享文本\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -H \"Authorization: Bearer xxx.xxx.xxx\" \\\n  -F \"text=这是要分享的文本内容\"\n```\n:::\n\n## 分片上传 API\n\n对于大文件，FileCodeBox 支持分片上传功能。分片上传将大文件分割成多个小块分别上传，支持断点续传。\n\n::: warning 前提条件\n分片上传功能需要管理员启用：`enableChunk=1`\n:::\n\n### 分片上传流程\n\n```\n┌─────────────┐     ┌─────────────┐     ┌─────────────┐\n│  初始化上传  │ ──▶ │  上传分片   │ ──▶ │  完成上传   │\n│   /init/    │     │  /chunk/    │     │ /complete/  │\n└─────────────┘     └─────────────┘     └─────────────┘\n                          │\n                          ▼\n                    ┌───────────┐\n                    │ 循环上传  │\n                    │ 每个分片  │\n                    └───────────┘\n```\n\n### 1. 初始化上传\n\n**POST** `/chunk/upload/init/`\n\n**请求参数：**\n\n```json\n{\n  \"file_name\": \"large_file.zip\",\n  \"file_size\": 104857600,\n  \"chunk_size\": 5242880,\n  \"file_hash\": \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\"\n}\n```\n\n| 参数 | 类型 | 必填 | 默认值 | 说明 |\n|------|------|------|--------|------|\n| `file_name` | string | 是 | - | 文件名 |\n| `file_size` | int | 是 | - | 文件总大小（字节） |\n| `chunk_size` | int | 否 | 5MB | 分片大小（字节） |\n| `file_hash` | string | 是 | - | 文件的 SHA256 哈希值 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"existed\": false,\n    \"upload_id\": \"abc123def456789\",\n    \"chunk_size\": 5242880,\n    \"total_chunks\": 20,\n    \"uploaded_chunks\": []\n  }\n}\n```\n\n| 字段 | 说明 |\n|------|------|\n| `existed` | 文件是否已存在（秒传） |\n| `upload_id` | 上传会话 ID |\n| `chunk_size` | 分片大小 |\n| `total_chunks` | 总分片数 |\n| `uploaded_chunks` | 已上传的分片索引列表 |\n\n### 2. 上传分片\n\n**POST** `/chunk/upload/chunk/{upload_id}/{chunk_index}`\n\n**路径参数：**\n\n| 参数 | 说明 |\n|------|------|\n| `upload_id` | 初始化时返回的上传会话 ID |\n| `chunk_index` | 分片索引，从 0 开始 |\n\n**请求体：**\n\nContent-Type: `multipart/form-data`\n\n| 参数 | 类型 | 说明 |\n|------|------|------|\n| `chunk` | file | 分片数据 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"chunk_hash\": \"a1b2c3d4e5f6...\"\n  }\n}\n```\n\n**cURL 示例：**\n\n```bash\n# 上传第一个分片（索引为 0）\ncurl -X POST \"http://localhost:12345/chunk/upload/chunk/abc123def456789/0\" \\\n  -F \"chunk=@/path/to/chunk_0\"\n```\n\n### 3. 完成上传\n\n**POST** `/chunk/upload/complete/{upload_id}`\n\n**路径参数：**\n\n| 参数 | 说明 |\n|------|------|\n| `upload_id` | 上传会话 ID |\n\n**请求参数：**\n\n```json\n{\n  \"expire_value\": 7,\n  \"expire_style\": \"day\"\n}\n```\n\n| 参数 | 类型 | 必填 | 说明 |\n|------|------|------|------|\n| `expire_value` | int | 是 | 过期数值 |\n| `expire_style` | string | 是 | 过期方式 |\n\n**响应示例：**\n\n```json\n{\n  \"code\": 200,\n  \"detail\": {\n    \"code\": \"789012\",\n    \"name\": \"large_file.zip\"\n  }\n}\n```\n\n### 断点续传\n\n分片上传支持断点续传。当上传中断后：\n\n1. 使用相同的 `file_hash` 重新调用初始化接口\n2. 服务器返回 `uploaded_chunks` 列表，包含已上传的分片索引\n3. 客户端只需上传不在列表中的分片\n4. 所有分片上传完成后调用完成接口\n\n**示例流程：**\n\n```javascript\n// 1. 初始化上传\nconst initResponse = await fetch('/chunk/upload/init/', {\n  method: 'POST',\n  body: JSON.stringify({\n    file_name: 'large_file.zip',\n    file_size: fileSize,\n    chunk_size: 5 * 1024 * 1024,\n    file_hash: fileHash\n  })\n});\nconst { upload_id, uploaded_chunks, total_chunks } = await initResponse.json();\n\n// 2. 上传未完成的分片\nfor (let i = 0; i < total_chunks; i++) {\n  if (!uploaded_chunks.includes(i)) {\n    const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);\n    await fetch(`/chunk/upload/chunk/${upload_id}/${i}`, {\n      method: 'POST',\n      body: chunk\n    });\n  }\n}\n\n// 3. 完成上传\nawait fetch(`/chunk/upload/complete/${upload_id}`, {\n  method: 'POST',\n  body: JSON.stringify({\n    expire_value: 7,\n    expire_style: 'day'\n  })\n});\n```\n\n## 错误处理\n\n### 常见错误\n\n| HTTP 状态码 | 错误信息 | 原因 | 解决方案 |\n|-------------|----------|------|----------|\n| 403 | 大小超过限制 | 文件超过 `uploadSize` 限制 | 减小文件大小或联系管理员调整限制 |\n| 403 | 上传频率限制 | 超过 IP 上传频率限制 | 等待限制时间窗口后重试 |\n| 400 | 过期时间类型错误 | `expire_style` 值不在允许列表中 | 使用有效的过期方式 |\n| 404 | 上传会话不存在 | `upload_id` 无效或已过期 | 重新初始化上传 |\n| 400 | 无效的分片索引 | `chunk_index` 超出范围 | 检查分片索引是否正确 |\n| 400 | 分片不完整 | 完成上传时分片数量不足 | 确保所有分片都已上传 |\n\n### 频率限制\n\n系统对上传操作有频率限制，防止滥用：\n\n| 配置项 | 默认值 | 说明 |\n|--------|--------|------|\n| `uploadMinute` | 1 | 限制时间窗口（分钟） |\n| `uploadCount` | 10 | 时间窗口内最大上传次数 |\n\n当超过频率限制时，需要等待时间窗口过后才能继续上传。\n\n### 错误响应格式\n\n```json\n{\n  \"detail\": \"错误信息描述\"\n}\n```\n\n## 上传配置\n\n### 相关配置项\n\n| 配置项 | 类型 | 默认值 | 说明 |\n|--------|------|--------|------|\n| `openUpload` | int | 1 | 是否开放上传（1=开放，0=关闭） |\n| `uploadSize` | int | 10485760 | 最大上传大小（字节） |\n| `enableChunk` | int | 0 | 是否启用分片上传（1=启用，0=禁用） |\n| `uploadMinute` | int | 1 | 上传频率限制时间窗口（分钟） |\n| `uploadCount` | int | 10 | 时间窗口内最大上传次数 |\n| `expireStyle` | list | [\"day\",\"hour\",\"minute\",\"forever\",\"count\"] | 允许的过期方式 |\n\n### 配置示例\n\n```python\n# 允许上传 100MB 文件，启用分片上传\nuploadSize = 104857600\nenableChunk = 1\n\n# 放宽上传频率限制：每 5 分钟最多 50 次\nuploadMinute = 5\nuploadCount = 50\n\n# 只允许按天和按次数过期\nexpireStyle = [\"day\", \"count\"]\n```\n\n## 下一步\n\n- [文件分享](/guide/share) - 了解完整的分享流程\n- [配置说明](/guide/configuration) - 了解所有配置选项\n- [存储配置](/guide/storage) - 了解文件存储方式\n- [安全设置](/guide/security) - 了解安全相关配置\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\nlayout: home\n\nhero:\n  name: \"FileCodeBox\"\n  text: \"文件快递柜\"\n  tagline: 匿名口令分享文本，文件，像拿快递一样取文件\n  image:\n    src: /logo_small.png\n    alt: FileCodeBox\n  actions:\n    - theme: brand\n      text: 快速开始\n      link: /guide/getting-started\n    - theme: alt\n      text: 在线体验\n      link: https://share.lanol.cn\n    - theme: alt\n      text: 在 GitHub 上查看\n      link: https://github.com/vastsa/FileCodeBox\n\nfeatures:\n  - icon: 🚀\n    title: 快速部署\n    details: 支持 Docker 一键部署，简单快捷，无需复杂配置\n  - icon: 🔒\n    title: 安全可靠\n    details: 文件访问需要提取码，支持设置有效期和下载次数限制\n  - icon: 💻\n    title: 简洁界面\n    details: 清爽的用户界面，支持拖拽上传，使用体验极佳\n  - icon: 🛠️\n    title: 功能丰富\n    details: 支持文件预览、在线播放、图片处理等多种功能\n  - icon: 📦\n    title: 存储扩展\n    details: 支持本地存储、对象存储等多种存储方式\n  - icon: 🔌\n    title: API 支持\n    details: 提供完整的 REST API，方便与其他系统集成\n---\n\n"
  },
  {
    "path": "docs/package.json",
    "content": "{\n  \"devDependencies\": {\n    \"vitepress\": \"^1.6.3\"\n  },\n  \"scripts\": {\n    \"docs:dev\": \"vitepress dev\",\n    \"docs:build\": \"vitepress build\",\n    \"docs:preview\": \"vitepress preview\"\n  }\n}"
  },
  {
    "path": "docs/showcase.md",
    "content": "# 优秀案例\n\n这里收录了一些使用 FileCodeBox 搭建的优秀站点。如果你也部署了 FileCodeBox，欢迎提交 PR 将你的站点添加到这里！\n\n## 官方演示站\n\n<div class=\"showcase-grid\">\n\n<div class=\"showcase-item\">\n\n### 🌟 FileCodeBox Demo\n\n- **网址**：[share.lanol.cn](https://share.lanol.cn)\n- **简介**：官方演示站点，体验最新功能\n- **特点**：稳定运行，功能完整\n\n</div>\n\n</div>\n\n## 社区站点\n\n::: tip 提交你的站点\n如果你使用 FileCodeBox 搭建了自己的文件分享服务，欢迎通过以下方式提交：\n\n1. 在 [GitHub](https://github.com/vastsa/FileCodeBox) 提交 PR，编辑此页面\n2. 在 [Issues](https://github.com/vastsa/FileCodeBox/issues) 中提交你的站点信息\n3. 加入 QQ 群 739673698 联系管理员\n:::\n\n<!-- \n提交格式示例：\n\n<div class=\"showcase-item\">\n\n### 站点名称\n\n- **网址**：[example.com](https://example.com)\n- **简介**：简短描述你的站点\n- **特点**：列出站点特色\n\n</div>\n-->\n\n<div class=\"showcase-grid\">\n\n<div class=\"showcase-item\">\n\n### 取文件\n\n- **网址**：[www.quwenjian.cn/fby.html](https://www.quwenjian.cn/fby.html)\n- **简介**：取文件 - 存储无界，便携无限\n- **特点**：永久免费的文件中转站\n- **运营者**：取文件&取文件网盘\n\n</div>\n\n<div class=\"showcase-item\">\n\n### 潘多拉盒子\n\n- **网址**：[pan.duo.la](https://pan.duo.la)\n- **简介**：潘多拉盒子\n- **特点**：经典1.6版本\n- **运营者**：五行缺心眼\n\n</div>\n\n</div>\n\n## 提交要求\n\n为了保证收录站点的质量，请确保你的站点满足以下条件：\n\n1. **稳定运行**：站点需要稳定运行，能够正常访问\n2. **合法合规**：站点内容需要合法合规，不得包含违法违规内容\n3. **保留版权**：建议保留 FileCodeBox 的版权信息\n4. **HTTPS 支持**：建议启用 HTTPS 加密访问\n\n## 案例展示模板\n\n如果你想提交站点，请按照以下格式：\n\n```markdown\n### 站点名称\n\n- **网址**：[域名](https://域名)\n- **简介**：一句话描述站点用途\n- **特点**：站点的特色功能或亮点\n- **运营者**：可选，你的名字或组织\n```\n\n<style>\n.showcase-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n  gap: 20px;\n  margin: 20px 0;\n}\n\n.showcase-item {\n  border: 1px solid var(--vp-c-divider);\n  border-radius: 8px;\n  padding: 20px;\n  transition: all 0.3s ease;\n}\n\n.showcase-item:hover {\n  border-color: var(--vp-c-brand);\n  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);\n}\n\n.showcase-item h3 {\n  margin-top: 0;\n  color: var(--vp-c-brand);\n}\n\n.showcase-item ul {\n  list-style: none;\n  padding: 0;\n  margin: 0;\n}\n\n.showcase-item li {\n  margin: 8px 0;\n}\n</style>\n"
  },
  {
    "path": "main.py",
    "content": "# @Time    : 2023/8/9 23:23\n# @Author  : Lan\n# @File    : main.py\n# @Software: PyCharm\nimport asyncio\nimport time\nfrom contextlib import asynccontextmanager\n\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fastapi.responses import HTMLResponse\nfrom fastapi.staticfiles import StaticFiles\nfrom tortoise import Tortoise\nfrom tortoise.contrib.fastapi import register_tortoise\n\nfrom apps.admin.views import admin_api\nfrom apps.base.models import KeyValue\nfrom apps.base.utils import ip_limit\nfrom apps.base.views import share_api, chunk_api, presign_api\nfrom core.config import ensure_settings_row, refresh_settings\nfrom core.database import db_startup_lock, get_db_config, init_db\nfrom core.logger import logger\nfrom core.response import APIResponse\nfrom core.settings import settings, BASE_DIR, DEFAULT_CONFIG\nfrom core.tasks import delete_expire_files, clean_incomplete_uploads\nfrom core.utils import hash_password, is_password_hashed\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    logger.info(\"正在初始化应用...\")\n    # 初始化数据库\n    await init_db()\n\n    # 加载配置（多进程下串行化启动写操作）\n    async with db_startup_lock():\n        await load_config()\n    app.mount(\n        \"/assets\",\n        StaticFiles(directory=f\"./{settings.themesSelect}/assets\"),\n        name=\"assets\",\n    )\n\n    # 启动后台任务\n    task = asyncio.create_task(delete_expire_files())\n    chunk_cleanup_task = asyncio.create_task(clean_incomplete_uploads())\n    logger.info(\"应用初始化完成\")\n\n    try:\n        yield\n    finally:\n        # 清理操作\n        logger.info(\"正在关闭应用...\")\n        task.cancel()\n        chunk_cleanup_task.cancel()\n        await asyncio.gather(task, chunk_cleanup_task, return_exceptions=True)\n        await Tortoise.close_connections()\n        logger.info(\"应用已关闭\")\n\n\nasync def load_config():\n    await ensure_settings_row()\n    await KeyValue.update_or_create(\n        key=\"sys_start\", defaults={\"value\": int(time.time() * 1000)}\n    )\n    await refresh_settings()\n\n    await migrate_password_to_hash()\n\n    ip_limit[\"error\"].minutes = settings.errorMinute\n    ip_limit[\"error\"].count = settings.errorCount\n    ip_limit[\"upload\"].minutes = settings.uploadMinute\n    ip_limit[\"upload\"].count = settings.uploadCount\n\n\nasync def migrate_password_to_hash():\n    if not is_password_hashed(settings.admin_token):\n        hashed = hash_password(settings.admin_token)\n        settings.admin_token = hashed\n        config_record = await KeyValue.filter(key=\"settings\").first()\n        if config_record and config_record.value:\n            config_record.value[\"admin_token\"] = hashed\n            await config_record.save()\n            logger.info(\"已将管理员密码迁移为哈希存储\")\n\n\napp = FastAPI(lifespan=lifespan)\n\n@app.middleware(\"http\")\nasync def refresh_settings_middleware(request, call_next):\n    await refresh_settings()\n    return await call_next(request)\n\napp.add_middleware(\n    CORSMiddleware,\n    allow_origins=[\"*\"],\n    allow_credentials=True,\n    allow_methods=[\"*\"],\n    allow_headers=[\"*\"],\n)\n\n# 使用 register_tortoise 来添加异常处理器\nregister_tortoise(\n    app,\n    config=get_db_config(),\n    generate_schemas=False,\n    add_exception_handlers=True,\n)\n\napp.include_router(share_api)\napp.include_router(chunk_api)\napp.include_router(presign_api)\napp.include_router(admin_api)\n\n\n@app.exception_handler(404)\n@app.get(\"/\")\nasync def index(request=None, exc=None):\n    return HTMLResponse(\n        content=open(\n            BASE_DIR / f\"{settings.themesSelect}/index.html\", \"r\", encoding=\"utf-8\"\n        )\n        .read()\n        .replace(\"{{title}}\", str(settings.name))\n        .replace(\"{{description}}\", str(settings.description))\n        .replace(\"{{keywords}}\", str(settings.keywords))\n        .replace(\"{{opacity}}\", str(settings.opacity))\n        .replace('\"/assets/', '\"assets/')\n        .replace(\"{{background}}\", str(settings.background)),\n        media_type=\"text/html\",\n        headers={\"Cache-Control\": \"no-cache\"},\n    )\n\n\n@app.get(\"/robots.txt\")\nasync def robots():\n    return HTMLResponse(content=settings.robotsText, media_type=\"text/plain\")\n\n\n@app.post(\"/\")\nasync def get_config():\n    return APIResponse(\n        detail={\n            \"name\": settings.name,\n            \"description\": settings.description,\n            \"explain\": settings.page_explain,\n            \"uploadSize\": settings.uploadSize,\n            \"expireStyle\": settings.expireStyle,\n            \"enableChunk\": settings.enableChunk,\n            \"openUpload\": settings.openUpload,\n            \"notify_title\": settings.notify_title,\n            \"notify_content\": settings.notify_content,\n            \"show_admin_address\": settings.showAdminAddr,\n            \"max_save_seconds\": settings.max_save_seconds,\n        }\n    )\n\n\nif __name__ == \"__main__\":\n    import uvicorn\n\n    uvicorn.run(\n        app=\"main:app\",\n        host=settings.serverHost,\n        port=settings.serverPort,\n        reload=False,\n        workers=settings.serverWorkers,\n    )\n"
  },
  {
    "path": "readme.md",
    "content": "<div align=\"center\">\n\n# FileCodeBox\n\n### 文件快递柜 - 匿名口令分享文本和文件\n\n<img src=\"https://fastly.jsdelivr.net/gh/vastsa/FileCodeBox@V1.6/static/banners/img_1.png\" alt=\"FileCodeBox Logo\" width=\"400\">\n\n像拿快递一样取文件，无需注册，输入口令即可获取\n\n[![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/network)\n[![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/issues)\n[![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox?style=flat-square)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)\n[![Docker Pulls](https://img.shields.io/docker/pulls/lanol/filecodebox?style=flat-square&logo=docker)](https://hub.docker.com/r/lanol/filecodebox)\n\n[![Python](https://img.shields.io/badge/Python-3.8+-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.68+-009688?style=flat-square&logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com)\n[![Vue.js](https://img.shields.io/badge/Vue.js-3.x-4FC08D?style=flat-square&logo=vue.js&logoColor=white)](https://vuejs.org)\n\n[English](./readme_en.md) | [在线演示](https://share.lanol.cn) | [部署教程](https://github.com/vastsa/FileCodeBox/wiki/部署教程) | [常见问题](https://github.com/vastsa/FileCodeBox/wiki/常见问题) | [QQ群: 739673698](https://qm.qq.com/q/PemPzhdEIM)\n\n```bash\n# 🚀 一键部署\ndocker run -d -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest\n# 国内镜像（如果上面拉取缓慢）: docker.cnb.cool/aixk/filecodebox\n```\n\n</div>\n\n---\n\n## 目录\n\n- [项目简介](#-项目简介)\n- [功能特性](#-功能特性)\n- [界面预览](#-界面预览)\n- [快速开始](#-快速开始)\n- [使用指南](#-使用指南)\n- [开发指南](#-开发指南)\n- [常见问题](#-常见问题)\n- [贡献指南](#-贡献指南)\n- [项目统计](#-项目统计)\n- [免责声明](#-免责声明)\n\n---\n\n## 📝 项目简介\n\nFileCodeBox 是一个轻量级的文件分享工具，基于 **FastAPI + Vue3** 开发。用户可以通过简单的方式匿名分享文本和文件，接收者只需输入提取码即可获取内容——就像从快递柜取出快递一样简单。\n\n### 应用场景\n\n| 场景 | 描述 |\n|------|------|\n| 📁 **临时文件分享** | 快速分享文件，无需注册登录 |\n| 📝 **代码片段分享** | 分享代码、配置文件等文本内容 |\n| 🕶️ **匿名文件传输** | 保护隐私的点对点传输 |\n| 🔄 **跨设备传输** | 在不同设备间快速同步文件 |\n| 💾 **临时存储** | 支持自定义过期时间的云存储 |\n| 🌐 **私有服务** | 搭建企业或个人专属分享服务 |\n\n---\n\n## ✨ 功能特性\n\n<table>\n<tr>\n<td width=\"33%\" valign=\"top\">\n\n### 🚀 轻量高效\n- FastAPI + SQLite3 后端\n- Vue3 + Element Plus 前端\n- Docker 一键部署\n- 资源占用极低\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 🔒 安全可靠\n- IP 上传频率限制\n- 提取码错误次数限制\n- 文件自动过期清理\n- 支持管理员认证\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 📤 便捷上传\n- 拖拽上传\n- 复制粘贴上传\n- 命令行 curl 上传\n- 批量文件上传\n\n</td>\n</tr>\n<tr>\n<td width=\"33%\" valign=\"top\">\n\n### 🎫 灵活分享\n- 随机/自定义提取码\n- 可设置有效期（时间/次数）\n- 支持永久有效\n- 文本和文件统一管理\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 💾 多存储支持\n- 本地文件系统\n- S3 兼容存储\n- [OneDrive](./docs/guide/storage-onedrive.md)\n- [OpenDAL](./docs/guide/storage-opendal.md)\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 🌍 国际化\n- 简体中文\n- 繁体中文\n- English\n- 响应式设计 / 深色模式\n\n</td>\n</tr>\n</table>\n\n---\n\n## 🖼️ 界面预览\n\n> 前端源码仓库：[2024主题](https://github.com/vastsa/FileCodeBoxFronted) | [2023主题](https://github.com/vastsa/FileCodeBoxFronted2023)\n\n<details open>\n<summary><b>🎨 新版界面 (2024)</b></summary>\n<br>\n<div align=\"center\">\n<table>\n<tr>\n<td><img src=\"./.github/images/img_7.png\" alt=\"文件上传\"></td>\n<td><img src=\"./.github/images/img_8.png\" alt=\"文本分享\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_10.png\" alt=\"文件管理\"></td>\n<td><img src=\"./.github/images/img_9.png\" alt=\"系统设置\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_11.png\" alt=\"移动端\"></td>\n<td><img src=\"./.github/images/img_12.png\" alt=\"深色模式\"></td>\n</tr>\n</table>\n</div>\n</details>\n\n<details>\n<summary><b>📦 经典界面 (2023)</b></summary>\n<br>\n<div align=\"center\">\n<table>\n<tr>\n<td><img src=\"./.github/images/img.png\" alt=\"首页\"></td>\n<td><img src=\"./.github/images/img_1.png\" alt=\"上传\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_2.png\" alt=\"管理\"></td>\n<td><img src=\"./.github/images/img_3.png\" alt=\"设置\"></td>\n</tr>\n</table>\n</div>\n</details>\n\n---\n\n## 🚀 快速开始\n\n### Docker 部署（推荐）\n\n**方式一：Docker CLI**\n\n```bash\n# Docker Hub（推荐）\ndocker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest\n\n# 国内镜像（如果 Docker Hub 拉取缓慢）\ndocker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox docker.cnb.cool/aixk/filecodebox\n```\n\n**方式二：Docker Compose**\n\n```yaml\nservices:\n  filecodebox:\n    image: lanol/filecodebox:latest\n    container_name: filecodebox\n    restart: unless-stopped\n    ports:\n      - \"12345:12345\"\n    volumes:\n      - ./data:/app/data\n    environment:\n      - WORKERS=4\n      - LOG_LEVEL=info\n```\n\n```bash\ndocker compose up -d\n```\n\n**环境变量说明**\n\n| 变量 | 默认值 | 说明 |\n|------|--------|------|\n| `HOST` | `::` | 监听地址（支持 IPv4/IPv6 双栈） |\n| `PORT` | `12345` | 服务端口 |\n| `WORKERS` | `4` | 工作进程数（建议设为 CPU 核心数） |\n| `LOG_LEVEL` | `info` | 日志级别：`debug` / `info` / `warning` / `error` |\n\n### 反向代理配置\n\n使用 Nginx 时，请添加以下配置以正确获取客户端 IP：\n\n```nginx\nlocation / {\n    proxy_pass http://127.0.0.1:12345;\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    client_max_body_size 100m;  # 根据需要调整上传大小限制\n}\n```\n\n### 手动部署\n\n```bash\n# 1. 克隆项目\ngit clone https://github.com/vastsa/FileCodeBox.git\ncd FileCodeBox\n\n# 2. 安装依赖\npip install -r requirements.txt\n\n# 3. 启动服务\npython main.py\n```\n\n---\n\n## 📖 使用指南\n\n### 基础操作\n\n| 操作 | 步骤 |\n|------|------|\n| **分享文件** | 打开网页 → 选择/拖拽文件 → 设置有效期 → 获取提取码 |\n| **获取文件** | 打开网页 → 输入提取码 → 下载文件或查看文本 |\n| **管理后台** | 访问 `/#/admin` → 输入密码 `FileCodeBox2023` |\n\n### 命令行使用（curl）\n\n<details>\n<summary><b>点击展开 curl 使用示例</b></summary>\n\n**上传文件**\n\n```bash\n# 基础上传（默认 1 天有效期）\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\"\n\n# 指定 1 小时有效期\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=1\" \\\n  -F \"expire_style=hour\"\n\n# 指定下载 10 次后过期\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n```\n\n**分享文本**\n\n```bash\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=要分享的文本内容\"\n```\n\n**下载文件**\n\n```bash\ncurl -L \"http://localhost:12345/share/select/?code=提取码\" -o filename\n```\n\n**有效期参数**\n\n| `expire_style` | 说明 |\n|----------------|------|\n| `day` | 天数 |\n| `hour` | 小时 |\n| `minute` | 分钟 |\n| `count` | 下载次数 |\n| `forever` | 永久有效 |\n\n**返回示例**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abcd1234\",\n    \"name\": \"file.txt\"\n  }\n}\n```\n\n**需要认证时**（管理员关闭游客上传后）\n\n```bash\n# 1. 获取 token\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n\n# 2. 携带 token 上传\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer <token>\" \\\n  -F \"file=@/path/to/file.txt\"\n```\n\n</details>\n\n---\n\n## 🛠 开发指南\n\n### 项目结构\n\n```\nFileCodeBox/\n├── apps/              # 应用模块\n│   ├── admin/         # 管理后台\n│   └── base/          # 基础功能\n├── core/              # 核心模块\n├── data/              # 数据目录（运行时生成）\n├── docs/              # 文档\n└── main.py            # 入口文件\n```\n\n### 本地开发\n\n**后端**\n\n```bash\npip install -r requirements.txt\npython main.py\n```\n\n**前端**\n\n```bash\n# 前端仓库: https://github.com/vastsa/FileCodeBoxFronted\ncd fcb-fronted\nnpm install\nnpm run dev\n```\n\n### 技术栈\n\n| 类别 | 技术 |\n|------|------|\n| **后端框架** | FastAPI 0.128+ / Uvicorn |\n| **数据库** | SQLite + Tortoise ORM |\n| **数据验证** | Pydantic 2.x |\n| **异步支持** | aiofiles / aiohttp / aioboto3 |\n| **对象存储** | S3 协议 / OneDrive / OpenDAL |\n| **前端框架** | Vue 3 + Element Plus + Vite |\n| **运行环境** | Python 3.8+ / Node.js 18+ |\n| **容器化** | Docker / Docker Compose |\n\n---\n\n## ❓ 常见问题\n\n<details>\n<summary><b>如何修改上传大小限制？</b></summary>\n\n在管理面板中修改 `uploadSize` 配置项。如果使用 Nginx 反向代理，还需修改 `client_max_body_size`。\n</details>\n\n<details>\n<summary><b>如何配置存储引擎？</b></summary>\n\n在管理面板中选择存储引擎类型并配置相应参数。支持本地存储、S3、OneDrive、OpenDAL 等。\n</details>\n\n<details>\n<summary><b>如何备份数据？</b></summary>\n\n备份 `data` 目录即可，包含数据库和上传的文件。\n</details>\n\n<details>\n<summary><b>如何修改管理员密码？</b></summary>\n\n登录管理面板后，在系统设置中修改 `adminPassword` 配置项。\n</details>\n\n更多问题请访问 [Wiki](https://github.com/vastsa/FileCodeBox/wiki/常见问题) 或加入 [QQ群: 739673698](https://qm.qq.com/q/PemPzhdEIM)\n\n---\n\n## 🤝 贡献指南\n\n欢迎提交 Issue 和 Pull Request！\n\n```bash\n# 1. Fork 并克隆\ngit clone https://github.com/your-username/FileCodeBox.git\n\n# 2. 创建分支\ngit checkout -b feature/your-feature\n\n# 3. 提交更改\ngit commit -m \"feat: add your feature\"\n\n# 4. 推送并创建 PR\ngit push origin feature/your-feature\n```\n\n---\n\n## 📊 项目统计\n\n<div align=\"center\">\n\n<a href=\"https://hellogithub.com/repository/75ad7ffedd404a6485b4d621ec5b47e6\" target=\"_blank\">\n  <img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=75ad7ffedd404a6485b4d621ec5b47e6&claim_uid=beSz6INEkCM4mDH\" alt=\"HelloGitHub\" width=\"200\">\n</a>\n\n![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg)\n\n[![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date)\n\n</div>\n\n---\n\n## 🗓 更新计划\n\n- [ ] 2025 年新皮肤\n- [ ] 文件收集功能\n\n---\n\n## 📜 免责声明\n\n本项目开源仅供学习交流使用，不得用于任何违法用途，否则后果自负，与作者无关。使用本项目时请保留项目地址和版权信息。\n\n---\n\n<div align=\"center\">\n\n**如果觉得项目不错，欢迎 ⭐ Star 支持！**\n\nMade with ❤️ by [vastsa](https://github.com/vastsa)\n\n</div>\n"
  },
  {
    "path": "readme_en.md",
    "content": "<div align=\"center\">\n\n# FileCodeBox\n\n### Anonymous File & Text Sharing with Passcode\n\n<img src=\"https://fastly.jsdelivr.net/gh/vastsa/FileCodeBox@V1.6/static/banners/img_1.png\" alt=\"FileCodeBox Logo\" width=\"400\">\n\nShare files like picking up a package — no registration required, just enter the passcode\n\n[![GitHub stars](https://img.shields.io/github/stars/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/stargazers)\n[![GitHub forks](https://img.shields.io/github/forks/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/network)\n[![GitHub issues](https://img.shields.io/github/issues/vastsa/FileCodeBox?style=flat-square&logo=github)](https://github.com/vastsa/FileCodeBox/issues)\n[![GitHub license](https://img.shields.io/github/license/vastsa/FileCodeBox?style=flat-square)](https://github.com/vastsa/FileCodeBox/blob/master/LICENSE)\n[![Docker Pulls](https://img.shields.io/docker/pulls/lanol/filecodebox?style=flat-square&logo=docker)](https://hub.docker.com/r/lanol/filecodebox)\n\n[![Python](https://img.shields.io/badge/Python-3.8+-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org)\n[![FastAPI](https://img.shields.io/badge/FastAPI-0.128+-009688?style=flat-square&logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com)\n[![Vue.js](https://img.shields.io/badge/Vue.js-3.x-4FC08D?style=flat-square&logo=vue.js&logoColor=white)](https://vuejs.org)\n\n[简体中文](./README.md) | [Live Demo](https://share.lanol.cn) | [Documentation](https://github.com/vastsa/FileCodeBox/wiki/Deployment-Guide) | [FAQ](https://github.com/vastsa/FileCodeBox/wiki/FAQ)\n\n```bash\n# 🚀 Quick Deploy\ndocker run -d -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest\n# China Mirror (if slow): docker.cnb.cool/aixk/filecodebox\n```\n\n</div>\n\n---\n\n## Table of Contents\n\n- [Introduction](#-introduction)\n- [Features](#-features)\n- [Screenshots](#-screenshots)\n- [Quick Start](#-quick-start)\n- [Usage Guide](#-usage-guide)\n- [Development](#-development)\n- [FAQ](#-faq)\n- [Contributing](#-contributing)\n- [Statistics](#-statistics)\n- [Disclaimer](#-disclaimer)\n\n---\n\n## 📝 Introduction\n\nFileCodeBox is a lightweight file sharing tool built with **FastAPI + Vue3**. Users can anonymously share text and files, and recipients only need to enter a passcode to retrieve the content — just like picking up a package from a locker.\n\n### Use Cases\n\n| Scenario | Description |\n|----------|-------------|\n| 📁 **Temporary File Sharing** | Quick file sharing without registration |\n| 📝 **Code Snippet Sharing** | Share code, config files, and text content |\n| 🕶️ **Anonymous Transfer** | Privacy-protected peer-to-peer transfer |\n| 🔄 **Cross-Device Transfer** | Quickly sync files between devices |\n| 💾 **Temporary Storage** | Cloud storage with custom expiration |\n| 🌐 **Private Service** | Build your own enterprise or personal sharing service |\n\n---\n\n## ✨ Features\n\n<table>\n<tr>\n<td width=\"33%\" valign=\"top\">\n\n### 🚀 Lightweight & Fast\n- FastAPI + SQLite3 backend\n- Vue3 + Element Plus frontend\n- One-click Docker deployment\n- Minimal resource usage\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 🔒 Secure & Reliable\n- IP upload rate limiting\n- Passcode attempt limiting\n- Auto file expiration cleanup\n- Admin authentication support\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 📤 Easy Upload\n- Drag & drop upload\n- Copy & paste upload\n- Command line curl upload\n- Batch file upload\n\n</td>\n</tr>\n<tr>\n<td width=\"33%\" valign=\"top\">\n\n### 🎫 Flexible Sharing\n- Random / custom passcodes\n- Set expiration (time/count)\n- Permanent validity support\n- Unified text & file management\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 💾 Multiple Storage\n- Local file system\n- S3-compatible storage\n- [OneDrive](./docs/guide/storage-onedrive.md)\n- [OpenDAL](./docs/guide/storage-opendal.md)\n\n</td>\n<td width=\"33%\" valign=\"top\">\n\n### 🌍 Internationalization\n- Simplified Chinese\n- Traditional Chinese\n- English\n- Responsive design / Dark mode\n\n</td>\n</tr>\n</table>\n\n---\n\n## 🖼️ Screenshots\n\n> Frontend repositories: [2024 Theme](https://github.com/vastsa/FileCodeBoxFronted) | [2023 Theme](https://github.com/vastsa/FileCodeBoxFronted2023)\n\n<details open>\n<summary><b>🎨 New Interface (2024)</b></summary>\n<br>\n<div align=\"center\">\n<table>\n<tr>\n<td><img src=\"./.github/images/img_7.png\" alt=\"File Upload\"></td>\n<td><img src=\"./.github/images/img_8.png\" alt=\"Text Share\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_10.png\" alt=\"File Management\"></td>\n<td><img src=\"./.github/images/img_9.png\" alt=\"System Settings\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_11.png\" alt=\"Mobile View\"></td>\n<td><img src=\"./.github/images/img_12.png\" alt=\"Dark Mode\"></td>\n</tr>\n</table>\n</div>\n</details>\n\n<details>\n<summary><b>📦 Classic Interface (2023)</b></summary>\n<br>\n<div align=\"center\">\n<table>\n<tr>\n<td><img src=\"./.github/images/img.png\" alt=\"Home\"></td>\n<td><img src=\"./.github/images/img_1.png\" alt=\"Upload\"></td>\n</tr>\n<tr>\n<td><img src=\"./.github/images/img_2.png\" alt=\"Management\"></td>\n<td><img src=\"./.github/images/img_3.png\" alt=\"Settings\"></td>\n</tr>\n</table>\n</div>\n</details>\n\n---\n\n## 🚀 Quick Start\n\n### Docker Deployment (Recommended)\n\n**Option 1: Docker CLI**\n\n```bash\n# Docker Hub (Recommended)\ndocker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox lanol/filecodebox:latest\n\n# China Mirror (if Docker Hub is slow)\ndocker run -d --restart always -p 12345:12345 -v /opt/FileCodeBox:/app/data --name filecodebox docker.cnb.cool/aixk/filecodebox\n```\n\n**Option 2: Docker Compose**\n\n```yaml\nservices:\n  filecodebox:\n    image: lanol/filecodebox:latest\n    container_name: filecodebox\n    restart: unless-stopped\n    ports:\n      - \"12345:12345\"\n    volumes:\n      - ./data:/app/data\n    environment:\n      - WORKERS=4\n      - LOG_LEVEL=info\n```\n\n```bash\ndocker compose up -d\n```\n\n**Environment Variables**\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `HOST` | `::` | Listen address (supports IPv4/IPv6 dual-stack) |\n| `PORT` | `12345` | Service port |\n| `WORKERS` | `4` | Worker processes (recommended: CPU cores) |\n| `LOG_LEVEL` | `info` | Log level: `debug` / `info` / `warning` / `error` |\n\n### Reverse Proxy Configuration\n\nWhen using Nginx, add the following configuration to properly obtain client IP:\n\n```nginx\nlocation / {\n    proxy_pass http://127.0.0.1:12345;\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    proxy_set_header X-Forwarded-Proto $scheme;\n    client_max_body_size 100m;  # Adjust upload size limit as needed\n}\n```\n\n### Manual Deployment\n\n```bash\n# 1. Clone the repository\ngit clone https://github.com/vastsa/FileCodeBox.git\ncd FileCodeBox\n\n# 2. Install dependencies\npip install -r requirements.txt\n\n# 3. Start the service\npython main.py\n```\n\n---\n\n## 📖 Usage Guide\n\n### Basic Operations\n\n| Operation | Steps |\n|-----------|-------|\n| **Share File** | Open website → Select/drag files → Set expiration → Get passcode |\n| **Retrieve File** | Open website → Enter passcode → Download file or view text |\n| **Admin Panel** | Visit `/#/admin` → Enter password `FileCodeBox2023` |\n\n### Command Line Usage (curl)\n\n<details>\n<summary><b>Click to expand curl examples</b></summary>\n\n**Upload File**\n\n```bash\n# Basic upload (default 1 day expiration)\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\"\n\n# Set 1 hour expiration\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=1\" \\\n  -F \"expire_style=hour\"\n\n# Set expiration after 10 downloads\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -F \"file=@/path/to/file.txt\" \\\n  -F \"expire_value=10\" \\\n  -F \"expire_style=count\"\n```\n\n**Share Text**\n\n```bash\ncurl -X POST \"http://localhost:12345/share/text/\" \\\n  -F \"text=Text content to share\"\n```\n\n**Download File**\n\n```bash\ncurl -L \"http://localhost:12345/share/select/?code=PASSCODE\" -o filename\n```\n\n**Expiration Parameters**\n\n| `expire_style` | Description |\n|----------------|-------------|\n| `day` | Days |\n| `hour` | Hours |\n| `minute` | Minutes |\n| `count` | Download count |\n| `forever` | Never expire |\n\n**Response Example**\n\n```json\n{\n  \"code\": 200,\n  \"msg\": \"success\",\n  \"detail\": {\n    \"code\": \"abcd1234\",\n    \"name\": \"file.txt\"\n  }\n}\n```\n\n**When Authentication Required** (after admin disables guest upload)\n\n```bash\n# 1. Get token\ncurl -X POST \"http://localhost:12345/admin/login\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"password\": \"FileCodeBox2023\"}'\n\n# 2. Upload with token\ncurl -X POST \"http://localhost:12345/share/file/\" \\\n  -H \"Authorization: Bearer <token>\" \\\n  -F \"file=@/path/to/file.txt\"\n```\n\n</details>\n\n---\n\n## 🛠 Development\n\n### Project Structure\n\n```\nFileCodeBox/\n├── apps/              # Application modules\n│   ├── admin/         # Admin backend\n│   └── base/          # Base functionality\n├── core/              # Core modules\n├── data/              # Data directory (generated at runtime)\n├── docs/              # Documentation\n└── main.py            # Entry point\n```\n\n### Local Development\n\n**Backend**\n\n```bash\npip install -r requirements.txt\npython main.py\n```\n\n**Frontend**\n\n```bash\n# Frontend repo: https://github.com/vastsa/FileCodeBoxFronted\ncd fcb-fronted\nnpm install\nnpm run dev\n```\n\n### Tech Stack\n\n| Category | Technology |\n|----------|------------|\n| **Backend Framework** | FastAPI 0.128+ / Uvicorn |\n| **Database** | SQLite + Tortoise ORM |\n| **Data Validation** | Pydantic 2.x |\n| **Async Support** | aiofiles / aiohttp / aioboto3 |\n| **Object Storage** | S3 Protocol / OneDrive / OpenDAL |\n| **Frontend Framework** | Vue 3 + Element Plus + Vite |\n| **Runtime** | Python 3.8+ / Node.js 18+ |\n| **Containerization** | Docker / Docker Compose |\n\n---\n\n## ❓ FAQ\n\n<details>\n<summary><b>How to modify upload size limit?</b></summary>\n\nModify the `uploadSize` configuration in the admin panel. If using Nginx reverse proxy, also modify `client_max_body_size`.\n</details>\n\n<details>\n<summary><b>How to configure storage engine?</b></summary>\n\nSelect the storage engine type and configure parameters in the admin panel. Supports local storage, S3, OneDrive, OpenDAL, etc.\n</details>\n\n<details>\n<summary><b>How to backup data?</b></summary>\n\nBackup the `data` directory, which contains the database and uploaded files.\n</details>\n\n<details>\n<summary><b>How to change admin password?</b></summary>\n\nAfter logging into the admin panel, modify the `adminPassword` configuration in system settings.\n</details>\n\nFor more questions, visit [Wiki](https://github.com/vastsa/FileCodeBox/wiki/FAQ)\n\n---\n\n## 🤝 Contributing\n\nIssues and Pull Requests are welcome!\n\n```bash\n# 1. Fork and clone\ngit clone https://github.com/your-username/FileCodeBox.git\n\n# 2. Create branch\ngit checkout -b feature/your-feature\n\n# 3. Commit changes\ngit commit -m \"feat: add your feature\"\n\n# 4. Push and create PR\ngit push origin feature/your-feature\n```\n\n---\n\n## 📊 Statistics\n\n<div align=\"center\">\n\n<a href=\"https://hellogithub.com/repository/75ad7ffedd404a6485b4d621ec5b47e6\" target=\"_blank\">\n  <img src=\"https://api.hellogithub.com/v1/widgets/recommend.svg?rid=75ad7ffedd404a6485b4d621ec5b47e6&claim_uid=beSz6INEkCM4mDH\" alt=\"HelloGitHub\" width=\"200\">\n</a>\n\n![Repobeats](https://repobeats.axiom.co/api/embed/7a6c92f1d96ee57e6fb67f0df371528397b0c9ac.svg)\n\n[![Star History](https://api.star-history.com/svg?repos=vastsa/FileCodeBox&type=Date)](https://star-history.com/#vastsa/FileCodeBox&Date)\n\n</div>\n\n---\n\n## 🗓 Roadmap\n\n- [ ] 2025 New Theme\n- [ ] File Collection Feature\n\n---\n\n## 📜 Disclaimer\n\nThis project is open-source for learning and communication purposes only. It should not be used for any illegal purposes. The author is not responsible for any consequences. Please retain the project address and copyright information when using it.\n\n---\n\n<div align=\"center\">\n\n**If you find this project helpful, please give it a ⭐ Star!**\n\nMade with ❤️ by [vastsa](https://github.com/vastsa)\n\n</div>\n"
  },
  {
    "path": "requirements.txt",
    "content": "aioboto3==15.5.0\naiohttp==3.13.3\naiofiles==25.1.0\nfastapi==0.128.0\npydantic==2.12.5\nuvicorn==0.40.0\ntortoise-orm==0.25.3\npython-multipart==0.0.21\n"
  }
]